roojs-ui.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         if(typeof key == "undefined" || key === null){
12871             this.length++;
12872             this.items.push(o);
12873             this.keys.push(null);
12874         }else{
12875             var old = this.map[key];
12876             if(old){
12877                 return this.replace(key, o);
12878             }
12879             this.length++;
12880             this.items.push(o);
12881             this.map[key] = o;
12882             this.keys.push(key);
12883         }
12884         this.fireEvent("add", this.length-1, o, key);
12885         return o;
12886     },
12887        
12888 /**
12889   * MixedCollection has a generic way to fetch keys if you implement getKey.
12890 <pre><code>
12891 // normal way
12892 var mc = new Roo.util.MixedCollection();
12893 mc.add(someEl.dom.id, someEl);
12894 mc.add(otherEl.dom.id, otherEl);
12895 //and so on
12896
12897 // using getKey
12898 var mc = new Roo.util.MixedCollection();
12899 mc.getKey = function(el){
12900    return el.dom.id;
12901 };
12902 mc.add(someEl);
12903 mc.add(otherEl);
12904
12905 // or via the constructor
12906 var mc = new Roo.util.MixedCollection(false, function(el){
12907    return el.dom.id;
12908 });
12909 mc.add(someEl);
12910 mc.add(otherEl);
12911 </code></pre>
12912  * @param o {Object} The item for which to find the key.
12913  * @return {Object} The key for the passed item.
12914  */
12915     getKey : function(o){
12916          return o.id; 
12917     },
12918    
12919 /**
12920  * Replaces an item in the collection.
12921  * @param {String} key The key associated with the item to replace, or the item to replace.
12922  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12923  * @return {Object}  The new item.
12924  */
12925     replace : function(key, o){
12926         if(arguments.length == 1){
12927             o = arguments[0];
12928             key = this.getKey(o);
12929         }
12930         var old = this.item(key);
12931         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12932              return this.add(key, o);
12933         }
12934         var index = this.indexOfKey(key);
12935         this.items[index] = o;
12936         this.map[key] = o;
12937         this.fireEvent("replace", key, old, o);
12938         return o;
12939     },
12940    
12941 /**
12942  * Adds all elements of an Array or an Object to the collection.
12943  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12944  * an Array of values, each of which are added to the collection.
12945  */
12946     addAll : function(objs){
12947         if(arguments.length > 1 || objs instanceof Array){
12948             var args = arguments.length > 1 ? arguments : objs;
12949             for(var i = 0, len = args.length; i < len; i++){
12950                 this.add(args[i]);
12951             }
12952         }else{
12953             for(var key in objs){
12954                 if(this.allowFunctions || typeof objs[key] != "function"){
12955                     this.add(key, objs[key]);
12956                 }
12957             }
12958         }
12959     },
12960    
12961 /**
12962  * Executes the specified function once for every item in the collection, passing each
12963  * item as the first and only parameter. returning false from the function will stop the iteration.
12964  * @param {Function} fn The function to execute for each item.
12965  * @param {Object} scope (optional) The scope in which to execute the function.
12966  */
12967     each : function(fn, scope){
12968         var items = [].concat(this.items); // each safe for removal
12969         for(var i = 0, len = items.length; i < len; i++){
12970             if(fn.call(scope || items[i], items[i], i, len) === false){
12971                 break;
12972             }
12973         }
12974     },
12975    
12976 /**
12977  * Executes the specified function once for every key in the collection, passing each
12978  * key, and its associated item as the first two parameters.
12979  * @param {Function} fn The function to execute for each item.
12980  * @param {Object} scope (optional) The scope in which to execute the function.
12981  */
12982     eachKey : function(fn, scope){
12983         for(var i = 0, len = this.keys.length; i < len; i++){
12984             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12985         }
12986     },
12987    
12988 /**
12989  * Returns the first item in the collection which elicits a true return value from the
12990  * passed selection function.
12991  * @param {Function} fn The selection function to execute for each item.
12992  * @param {Object} scope (optional) The scope in which to execute the function.
12993  * @return {Object} The first item in the collection which returned true from the selection function.
12994  */
12995     find : function(fn, scope){
12996         for(var i = 0, len = this.items.length; i < len; i++){
12997             if(fn.call(scope || window, this.items[i], this.keys[i])){
12998                 return this.items[i];
12999             }
13000         }
13001         return null;
13002     },
13003    
13004 /**
13005  * Inserts an item at the specified index in the collection.
13006  * @param {Number} index The index to insert the item at.
13007  * @param {String} key The key to associate with the new item, or the item itself.
13008  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13009  * @return {Object} The item inserted.
13010  */
13011     insert : function(index, key, o){
13012         if(arguments.length == 2){
13013             o = arguments[1];
13014             key = this.getKey(o);
13015         }
13016         if(index >= this.length){
13017             return this.add(key, o);
13018         }
13019         this.length++;
13020         this.items.splice(index, 0, o);
13021         if(typeof key != "undefined" && key != null){
13022             this.map[key] = o;
13023         }
13024         this.keys.splice(index, 0, key);
13025         this.fireEvent("add", index, o, key);
13026         return o;
13027     },
13028    
13029 /**
13030  * Removed an item from the collection.
13031  * @param {Object} o The item to remove.
13032  * @return {Object} The item removed.
13033  */
13034     remove : function(o){
13035         return this.removeAt(this.indexOf(o));
13036     },
13037    
13038 /**
13039  * Remove an item from a specified index in the collection.
13040  * @param {Number} index The index within the collection of the item to remove.
13041  */
13042     removeAt : function(index){
13043         if(index < this.length && index >= 0){
13044             this.length--;
13045             var o = this.items[index];
13046             this.items.splice(index, 1);
13047             var key = this.keys[index];
13048             if(typeof key != "undefined"){
13049                 delete this.map[key];
13050             }
13051             this.keys.splice(index, 1);
13052             this.fireEvent("remove", o, key);
13053         }
13054     },
13055    
13056 /**
13057  * Removed an item associated with the passed key fom the collection.
13058  * @param {String} key The key of the item to remove.
13059  */
13060     removeKey : function(key){
13061         return this.removeAt(this.indexOfKey(key));
13062     },
13063    
13064 /**
13065  * Returns the number of items in the collection.
13066  * @return {Number} the number of items in the collection.
13067  */
13068     getCount : function(){
13069         return this.length; 
13070     },
13071    
13072 /**
13073  * Returns index within the collection of the passed Object.
13074  * @param {Object} o The item to find the index of.
13075  * @return {Number} index of the item.
13076  */
13077     indexOf : function(o){
13078         if(!this.items.indexOf){
13079             for(var i = 0, len = this.items.length; i < len; i++){
13080                 if(this.items[i] == o) return i;
13081             }
13082             return -1;
13083         }else{
13084             return this.items.indexOf(o);
13085         }
13086     },
13087    
13088 /**
13089  * Returns index within the collection of the passed key.
13090  * @param {String} key The key to find the index of.
13091  * @return {Number} index of the key.
13092  */
13093     indexOfKey : function(key){
13094         if(!this.keys.indexOf){
13095             for(var i = 0, len = this.keys.length; i < len; i++){
13096                 if(this.keys[i] == key) return i;
13097             }
13098             return -1;
13099         }else{
13100             return this.keys.indexOf(key);
13101         }
13102     },
13103    
13104 /**
13105  * Returns the item associated with the passed key OR index. Key has priority over index.
13106  * @param {String/Number} key The key or index of the item.
13107  * @return {Object} The item associated with the passed key.
13108  */
13109     item : function(key){
13110         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13111         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13112     },
13113     
13114 /**
13115  * Returns the item at the specified index.
13116  * @param {Number} index The index of the item.
13117  * @return {Object}
13118  */
13119     itemAt : function(index){
13120         return this.items[index];
13121     },
13122     
13123 /**
13124  * Returns the item associated with the passed key.
13125  * @param {String/Number} key The key of the item.
13126  * @return {Object} The item associated with the passed key.
13127  */
13128     key : function(key){
13129         return this.map[key];
13130     },
13131    
13132 /**
13133  * Returns true if the collection contains the passed Object as an item.
13134  * @param {Object} o  The Object to look for in the collection.
13135  * @return {Boolean} True if the collection contains the Object as an item.
13136  */
13137     contains : function(o){
13138         return this.indexOf(o) != -1;
13139     },
13140    
13141 /**
13142  * Returns true if the collection contains the passed Object as a key.
13143  * @param {String} key The key to look for in the collection.
13144  * @return {Boolean} True if the collection contains the Object as a key.
13145  */
13146     containsKey : function(key){
13147         return typeof this.map[key] != "undefined";
13148     },
13149    
13150 /**
13151  * Removes all items from the collection.
13152  */
13153     clear : function(){
13154         this.length = 0;
13155         this.items = [];
13156         this.keys = [];
13157         this.map = {};
13158         this.fireEvent("clear");
13159     },
13160    
13161 /**
13162  * Returns the first item in the collection.
13163  * @return {Object} the first item in the collection..
13164  */
13165     first : function(){
13166         return this.items[0]; 
13167     },
13168    
13169 /**
13170  * Returns the last item in the collection.
13171  * @return {Object} the last item in the collection..
13172  */
13173     last : function(){
13174         return this.items[this.length-1];   
13175     },
13176     
13177     _sort : function(property, dir, fn){
13178         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13179         fn = fn || function(a, b){
13180             return a-b;
13181         };
13182         var c = [], k = this.keys, items = this.items;
13183         for(var i = 0, len = items.length; i < len; i++){
13184             c[c.length] = {key: k[i], value: items[i], index: i};
13185         }
13186         c.sort(function(a, b){
13187             var v = fn(a[property], b[property]) * dsc;
13188             if(v == 0){
13189                 v = (a.index < b.index ? -1 : 1);
13190             }
13191             return v;
13192         });
13193         for(var i = 0, len = c.length; i < len; i++){
13194             items[i] = c[i].value;
13195             k[i] = c[i].key;
13196         }
13197         this.fireEvent("sort", this);
13198     },
13199     
13200     /**
13201      * Sorts this collection with the passed comparison function
13202      * @param {String} direction (optional) "ASC" or "DESC"
13203      * @param {Function} fn (optional) comparison function
13204      */
13205     sort : function(dir, fn){
13206         this._sort("value", dir, fn);
13207     },
13208     
13209     /**
13210      * Sorts this collection by keys
13211      * @param {String} direction (optional) "ASC" or "DESC"
13212      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13213      */
13214     keySort : function(dir, fn){
13215         this._sort("key", dir, fn || function(a, b){
13216             return String(a).toUpperCase()-String(b).toUpperCase();
13217         });
13218     },
13219     
13220     /**
13221      * Returns a range of items in this collection
13222      * @param {Number} startIndex (optional) defaults to 0
13223      * @param {Number} endIndex (optional) default to the last item
13224      * @return {Array} An array of items
13225      */
13226     getRange : function(start, end){
13227         var items = this.items;
13228         if(items.length < 1){
13229             return [];
13230         }
13231         start = start || 0;
13232         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13233         var r = [];
13234         if(start <= end){
13235             for(var i = start; i <= end; i++) {
13236                     r[r.length] = items[i];
13237             }
13238         }else{
13239             for(var i = start; i >= end; i--) {
13240                     r[r.length] = items[i];
13241             }
13242         }
13243         return r;
13244     },
13245         
13246     /**
13247      * Filter the <i>objects</i> in this collection by a specific property. 
13248      * Returns a new collection that has been filtered.
13249      * @param {String} property A property on your objects
13250      * @param {String/RegExp} value Either string that the property values 
13251      * should start with or a RegExp to test against the property
13252      * @return {MixedCollection} The new filtered collection
13253      */
13254     filter : function(property, value){
13255         if(!value.exec){ // not a regex
13256             value = String(value);
13257             if(value.length == 0){
13258                 return this.clone();
13259             }
13260             value = new RegExp("^" + Roo.escapeRe(value), "i");
13261         }
13262         return this.filterBy(function(o){
13263             return o && value.test(o[property]);
13264         });
13265         },
13266     
13267     /**
13268      * Filter by a function. * Returns a new collection that has been filtered.
13269      * The passed function will be called with each 
13270      * object in the collection. If the function returns true, the value is included 
13271      * otherwise it is filtered.
13272      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13273      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13274      * @return {MixedCollection} The new filtered collection
13275      */
13276     filterBy : function(fn, scope){
13277         var r = new Roo.util.MixedCollection();
13278         r.getKey = this.getKey;
13279         var k = this.keys, it = this.items;
13280         for(var i = 0, len = it.length; i < len; i++){
13281             if(fn.call(scope||this, it[i], k[i])){
13282                                 r.add(k[i], it[i]);
13283                         }
13284         }
13285         return r;
13286     },
13287     
13288     /**
13289      * Creates a duplicate of this collection
13290      * @return {MixedCollection}
13291      */
13292     clone : function(){
13293         var r = new Roo.util.MixedCollection();
13294         var k = this.keys, it = this.items;
13295         for(var i = 0, len = it.length; i < len; i++){
13296             r.add(k[i], it[i]);
13297         }
13298         r.getKey = this.getKey;
13299         return r;
13300     }
13301 });
13302 /**
13303  * Returns the item associated with the passed key or index.
13304  * @method
13305  * @param {String/Number} key The key or index of the item.
13306  * @return {Object} The item associated with the passed key.
13307  */
13308 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13309  * Based on:
13310  * Ext JS Library 1.1.1
13311  * Copyright(c) 2006-2007, Ext JS, LLC.
13312  *
13313  * Originally Released Under LGPL - original licence link has changed is not relivant.
13314  *
13315  * Fork - LGPL
13316  * <script type="text/javascript">
13317  */
13318 /**
13319  * @class Roo.util.JSON
13320  * Modified version of Douglas Crockford"s json.js that doesn"t
13321  * mess with the Object prototype 
13322  * http://www.json.org/js.html
13323  * @singleton
13324  */
13325 Roo.util.JSON = new (function(){
13326     var useHasOwn = {}.hasOwnProperty ? true : false;
13327     
13328     // crashes Safari in some instances
13329     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13330     
13331     var pad = function(n) {
13332         return n < 10 ? "0" + n : n;
13333     };
13334     
13335     var m = {
13336         "\b": '\\b',
13337         "\t": '\\t',
13338         "\n": '\\n',
13339         "\f": '\\f',
13340         "\r": '\\r',
13341         '"' : '\\"',
13342         "\\": '\\\\'
13343     };
13344
13345     var encodeString = function(s){
13346         if (/["\\\x00-\x1f]/.test(s)) {
13347             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13348                 var c = m[b];
13349                 if(c){
13350                     return c;
13351                 }
13352                 c = b.charCodeAt();
13353                 return "\\u00" +
13354                     Math.floor(c / 16).toString(16) +
13355                     (c % 16).toString(16);
13356             }) + '"';
13357         }
13358         return '"' + s + '"';
13359     };
13360     
13361     var encodeArray = function(o){
13362         var a = ["["], b, i, l = o.length, v;
13363             for (i = 0; i < l; i += 1) {
13364                 v = o[i];
13365                 switch (typeof v) {
13366                     case "undefined":
13367                     case "function":
13368                     case "unknown":
13369                         break;
13370                     default:
13371                         if (b) {
13372                             a.push(',');
13373                         }
13374                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13375                         b = true;
13376                 }
13377             }
13378             a.push("]");
13379             return a.join("");
13380     };
13381     
13382     var encodeDate = function(o){
13383         return '"' + o.getFullYear() + "-" +
13384                 pad(o.getMonth() + 1) + "-" +
13385                 pad(o.getDate()) + "T" +
13386                 pad(o.getHours()) + ":" +
13387                 pad(o.getMinutes()) + ":" +
13388                 pad(o.getSeconds()) + '"';
13389     };
13390     
13391     /**
13392      * Encodes an Object, Array or other value
13393      * @param {Mixed} o The variable to encode
13394      * @return {String} The JSON string
13395      */
13396     this.encode = function(o)
13397     {
13398         // should this be extended to fully wrap stringify..
13399         
13400         if(typeof o == "undefined" || o === null){
13401             return "null";
13402         }else if(o instanceof Array){
13403             return encodeArray(o);
13404         }else if(o instanceof Date){
13405             return encodeDate(o);
13406         }else if(typeof o == "string"){
13407             return encodeString(o);
13408         }else if(typeof o == "number"){
13409             return isFinite(o) ? String(o) : "null";
13410         }else if(typeof o == "boolean"){
13411             return String(o);
13412         }else {
13413             var a = ["{"], b, i, v;
13414             for (i in o) {
13415                 if(!useHasOwn || o.hasOwnProperty(i)) {
13416                     v = o[i];
13417                     switch (typeof v) {
13418                     case "undefined":
13419                     case "function":
13420                     case "unknown":
13421                         break;
13422                     default:
13423                         if(b){
13424                             a.push(',');
13425                         }
13426                         a.push(this.encode(i), ":",
13427                                 v === null ? "null" : this.encode(v));
13428                         b = true;
13429                     }
13430                 }
13431             }
13432             a.push("}");
13433             return a.join("");
13434         }
13435     };
13436     
13437     /**
13438      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13439      * @param {String} json The JSON string
13440      * @return {Object} The resulting object
13441      */
13442     this.decode = function(json){
13443         
13444         return  /** eval:var:json */ eval("(" + json + ')');
13445     };
13446 })();
13447 /** 
13448  * Shorthand for {@link Roo.util.JSON#encode}
13449  * @member Roo encode 
13450  * @method */
13451 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13452 /** 
13453  * Shorthand for {@link Roo.util.JSON#decode}
13454  * @member Roo decode 
13455  * @method */
13456 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13457 /*
13458  * Based on:
13459  * Ext JS Library 1.1.1
13460  * Copyright(c) 2006-2007, Ext JS, LLC.
13461  *
13462  * Originally Released Under LGPL - original licence link has changed is not relivant.
13463  *
13464  * Fork - LGPL
13465  * <script type="text/javascript">
13466  */
13467  
13468 /**
13469  * @class Roo.util.Format
13470  * Reusable data formatting functions
13471  * @singleton
13472  */
13473 Roo.util.Format = function(){
13474     var trimRe = /^\s+|\s+$/g;
13475     return {
13476         /**
13477          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13478          * @param {String} value The string to truncate
13479          * @param {Number} length The maximum length to allow before truncating
13480          * @return {String} The converted text
13481          */
13482         ellipsis : function(value, len){
13483             if(value && value.length > len){
13484                 return value.substr(0, len-3)+"...";
13485             }
13486             return value;
13487         },
13488
13489         /**
13490          * Checks a reference and converts it to empty string if it is undefined
13491          * @param {Mixed} value Reference to check
13492          * @return {Mixed} Empty string if converted, otherwise the original value
13493          */
13494         undef : function(value){
13495             return typeof value != "undefined" ? value : "";
13496         },
13497
13498         /**
13499          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13500          * @param {String} value The string to encode
13501          * @return {String} The encoded text
13502          */
13503         htmlEncode : function(value){
13504             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13505         },
13506
13507         /**
13508          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13509          * @param {String} value The string to decode
13510          * @return {String} The decoded text
13511          */
13512         htmlDecode : function(value){
13513             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13514         },
13515
13516         /**
13517          * Trims any whitespace from either side of a string
13518          * @param {String} value The text to trim
13519          * @return {String} The trimmed text
13520          */
13521         trim : function(value){
13522             return String(value).replace(trimRe, "");
13523         },
13524
13525         /**
13526          * Returns a substring from within an original string
13527          * @param {String} value The original text
13528          * @param {Number} start The start index of the substring
13529          * @param {Number} length The length of the substring
13530          * @return {String} The substring
13531          */
13532         substr : function(value, start, length){
13533             return String(value).substr(start, length);
13534         },
13535
13536         /**
13537          * Converts a string to all lower case letters
13538          * @param {String} value The text to convert
13539          * @return {String} The converted text
13540          */
13541         lowercase : function(value){
13542             return String(value).toLowerCase();
13543         },
13544
13545         /**
13546          * Converts a string to all upper case letters
13547          * @param {String} value The text to convert
13548          * @return {String} The converted text
13549          */
13550         uppercase : function(value){
13551             return String(value).toUpperCase();
13552         },
13553
13554         /**
13555          * Converts the first character only of a string to upper case
13556          * @param {String} value The text to convert
13557          * @return {String} The converted text
13558          */
13559         capitalize : function(value){
13560             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13561         },
13562
13563         // private
13564         call : function(value, fn){
13565             if(arguments.length > 2){
13566                 var args = Array.prototype.slice.call(arguments, 2);
13567                 args.unshift(value);
13568                  
13569                 return /** eval:var:value */  eval(fn).apply(window, args);
13570             }else{
13571                 /** eval:var:value */
13572                 return /** eval:var:value */ eval(fn).call(window, value);
13573             }
13574         },
13575
13576        
13577         /**
13578          * safer version of Math.toFixed..??/
13579          * @param {Number/String} value The numeric value to format
13580          * @param {Number/String} value Decimal places 
13581          * @return {String} The formatted currency string
13582          */
13583         toFixed : function(v, n)
13584         {
13585             // why not use to fixed - precision is buggered???
13586             if (!n) {
13587                 return Math.round(v-0);
13588             }
13589             var fact = Math.pow(10,n+1);
13590             v = (Math.round((v-0)*fact))/fact;
13591             var z = (''+fact).substring(2);
13592             if (v == Math.floor(v)) {
13593                 return Math.floor(v) + '.' + z;
13594             }
13595             
13596             // now just padd decimals..
13597             var ps = String(v).split('.');
13598             var fd = (ps[1] + z);
13599             var r = fd.substring(0,n); 
13600             var rm = fd.substring(n); 
13601             if (rm < 5) {
13602                 return ps[0] + '.' + r;
13603             }
13604             r*=1; // turn it into a number;
13605             r++;
13606             if (String(r).length != n) {
13607                 ps[0]*=1;
13608                 ps[0]++;
13609                 r = String(r).substring(1); // chop the end off.
13610             }
13611             
13612             return ps[0] + '.' + r;
13613              
13614         },
13615         
13616         /**
13617          * Format a number as US currency
13618          * @param {Number/String} value The numeric value to format
13619          * @return {String} The formatted currency string
13620          */
13621         usMoney : function(v){
13622             return '$' + Roo.util.Format.number(v);
13623         },
13624         
13625         /**
13626          * Format a number
13627          * eventually this should probably emulate php's number_format
13628          * @param {Number/String} value The numeric value to format
13629          * @param {Number} decimals number of decimal places
13630          * @return {String} The formatted currency string
13631          */
13632         number : function(v,decimals)
13633         {
13634             // multiply and round.
13635             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13636             var mul = Math.pow(10, decimals);
13637             var zero = String(mul).substring(1);
13638             v = (Math.round((v-0)*mul))/mul;
13639             
13640             // if it's '0' number.. then
13641             
13642             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13643             v = String(v);
13644             var ps = v.split('.');
13645             var whole = ps[0];
13646             
13647             
13648             var r = /(\d+)(\d{3})/;
13649             // add comma's
13650             while (r.test(whole)) {
13651                 whole = whole.replace(r, '$1' + ',' + '$2');
13652             }
13653             
13654             
13655             var sub = ps[1] ?
13656                     // has decimals..
13657                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13658                     // does not have decimals
13659                     (decimals ? ('.' + zero) : '');
13660             
13661             
13662             return whole + sub ;
13663         },
13664         
13665         /**
13666          * Parse a value into a formatted date using the specified format pattern.
13667          * @param {Mixed} value The value to format
13668          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13669          * @return {String} The formatted date string
13670          */
13671         date : function(v, format){
13672             if(!v){
13673                 return "";
13674             }
13675             if(!(v instanceof Date)){
13676                 v = new Date(Date.parse(v));
13677             }
13678             return v.dateFormat(format || Roo.util.Format.defaults.date);
13679         },
13680
13681         /**
13682          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13683          * @param {String} format Any valid date format string
13684          * @return {Function} The date formatting function
13685          */
13686         dateRenderer : function(format){
13687             return function(v){
13688                 return Roo.util.Format.date(v, format);  
13689             };
13690         },
13691
13692         // private
13693         stripTagsRE : /<\/?[^>]+>/gi,
13694         
13695         /**
13696          * Strips all HTML tags
13697          * @param {Mixed} value The text from which to strip tags
13698          * @return {String} The stripped text
13699          */
13700         stripTags : function(v){
13701             return !v ? v : String(v).replace(this.stripTagsRE, "");
13702         }
13703     };
13704 }();
13705 Roo.util.Format.defaults = {
13706     date : 'd/M/Y'
13707 };/*
13708  * Based on:
13709  * Ext JS Library 1.1.1
13710  * Copyright(c) 2006-2007, Ext JS, LLC.
13711  *
13712  * Originally Released Under LGPL - original licence link has changed is not relivant.
13713  *
13714  * Fork - LGPL
13715  * <script type="text/javascript">
13716  */
13717
13718
13719  
13720
13721 /**
13722  * @class Roo.MasterTemplate
13723  * @extends Roo.Template
13724  * Provides a template that can have child templates. The syntax is:
13725 <pre><code>
13726 var t = new Roo.MasterTemplate(
13727         '&lt;select name="{name}"&gt;',
13728                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13729         '&lt;/select&gt;'
13730 );
13731 t.add('options', {value: 'foo', text: 'bar'});
13732 // or you can add multiple child elements in one shot
13733 t.addAll('options', [
13734     {value: 'foo', text: 'bar'},
13735     {value: 'foo2', text: 'bar2'},
13736     {value: 'foo3', text: 'bar3'}
13737 ]);
13738 // then append, applying the master template values
13739 t.append('my-form', {name: 'my-select'});
13740 </code></pre>
13741 * A name attribute for the child template is not required if you have only one child
13742 * template or you want to refer to them by index.
13743  */
13744 Roo.MasterTemplate = function(){
13745     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13746     this.originalHtml = this.html;
13747     var st = {};
13748     var m, re = this.subTemplateRe;
13749     re.lastIndex = 0;
13750     var subIndex = 0;
13751     while(m = re.exec(this.html)){
13752         var name = m[1], content = m[2];
13753         st[subIndex] = {
13754             name: name,
13755             index: subIndex,
13756             buffer: [],
13757             tpl : new Roo.Template(content)
13758         };
13759         if(name){
13760             st[name] = st[subIndex];
13761         }
13762         st[subIndex].tpl.compile();
13763         st[subIndex].tpl.call = this.call.createDelegate(this);
13764         subIndex++;
13765     }
13766     this.subCount = subIndex;
13767     this.subs = st;
13768 };
13769 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13770     /**
13771     * The regular expression used to match sub templates
13772     * @type RegExp
13773     * @property
13774     */
13775     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13776
13777     /**
13778      * Applies the passed values to a child template.
13779      * @param {String/Number} name (optional) The name or index of the child template
13780      * @param {Array/Object} values The values to be applied to the template
13781      * @return {MasterTemplate} this
13782      */
13783      add : function(name, values){
13784         if(arguments.length == 1){
13785             values = arguments[0];
13786             name = 0;
13787         }
13788         var s = this.subs[name];
13789         s.buffer[s.buffer.length] = s.tpl.apply(values);
13790         return this;
13791     },
13792
13793     /**
13794      * Applies all the passed values to a child template.
13795      * @param {String/Number} name (optional) The name or index of the child template
13796      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13797      * @param {Boolean} reset (optional) True to reset the template first
13798      * @return {MasterTemplate} this
13799      */
13800     fill : function(name, values, reset){
13801         var a = arguments;
13802         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13803             values = a[0];
13804             name = 0;
13805             reset = a[1];
13806         }
13807         if(reset){
13808             this.reset();
13809         }
13810         for(var i = 0, len = values.length; i < len; i++){
13811             this.add(name, values[i]);
13812         }
13813         return this;
13814     },
13815
13816     /**
13817      * Resets the template for reuse
13818      * @return {MasterTemplate} this
13819      */
13820      reset : function(){
13821         var s = this.subs;
13822         for(var i = 0; i < this.subCount; i++){
13823             s[i].buffer = [];
13824         }
13825         return this;
13826     },
13827
13828     applyTemplate : function(values){
13829         var s = this.subs;
13830         var replaceIndex = -1;
13831         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13832             return s[++replaceIndex].buffer.join("");
13833         });
13834         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13835     },
13836
13837     apply : function(){
13838         return this.applyTemplate.apply(this, arguments);
13839     },
13840
13841     compile : function(){return this;}
13842 });
13843
13844 /**
13845  * Alias for fill().
13846  * @method
13847  */
13848 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13849  /**
13850  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13851  * var tpl = Roo.MasterTemplate.from('element-id');
13852  * @param {String/HTMLElement} el
13853  * @param {Object} config
13854  * @static
13855  */
13856 Roo.MasterTemplate.from = function(el, config){
13857     el = Roo.getDom(el);
13858     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13859 };/*
13860  * Based on:
13861  * Ext JS Library 1.1.1
13862  * Copyright(c) 2006-2007, Ext JS, LLC.
13863  *
13864  * Originally Released Under LGPL - original licence link has changed is not relivant.
13865  *
13866  * Fork - LGPL
13867  * <script type="text/javascript">
13868  */
13869
13870  
13871 /**
13872  * @class Roo.util.CSS
13873  * Utility class for manipulating CSS rules
13874  * @singleton
13875  */
13876 Roo.util.CSS = function(){
13877         var rules = null;
13878         var doc = document;
13879
13880     var camelRe = /(-[a-z])/gi;
13881     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13882
13883    return {
13884    /**
13885     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13886     * tag and appended to the HEAD of the document.
13887     * @param {String|Object} cssText The text containing the css rules
13888     * @param {String} id An id to add to the stylesheet for later removal
13889     * @return {StyleSheet}
13890     */
13891     createStyleSheet : function(cssText, id){
13892         var ss;
13893         var head = doc.getElementsByTagName("head")[0];
13894         var nrules = doc.createElement("style");
13895         nrules.setAttribute("type", "text/css");
13896         if(id){
13897             nrules.setAttribute("id", id);
13898         }
13899         if (typeof(cssText) != 'string') {
13900             // support object maps..
13901             // not sure if this a good idea.. 
13902             // perhaps it should be merged with the general css handling
13903             // and handle js style props.
13904             var cssTextNew = [];
13905             for(var n in cssText) {
13906                 var citems = [];
13907                 for(var k in cssText[n]) {
13908                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13909                 }
13910                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13911                 
13912             }
13913             cssText = cssTextNew.join("\n");
13914             
13915         }
13916        
13917        
13918        if(Roo.isIE){
13919            head.appendChild(nrules);
13920            ss = nrules.styleSheet;
13921            ss.cssText = cssText;
13922        }else{
13923            try{
13924                 nrules.appendChild(doc.createTextNode(cssText));
13925            }catch(e){
13926                nrules.cssText = cssText; 
13927            }
13928            head.appendChild(nrules);
13929            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13930        }
13931        this.cacheStyleSheet(ss);
13932        return ss;
13933    },
13934
13935    /**
13936     * Removes a style or link tag by id
13937     * @param {String} id The id of the tag
13938     */
13939    removeStyleSheet : function(id){
13940        var existing = doc.getElementById(id);
13941        if(existing){
13942            existing.parentNode.removeChild(existing);
13943        }
13944    },
13945
13946    /**
13947     * Dynamically swaps an existing stylesheet reference for a new one
13948     * @param {String} id The id of an existing link tag to remove
13949     * @param {String} url The href of the new stylesheet to include
13950     */
13951    swapStyleSheet : function(id, url){
13952        this.removeStyleSheet(id);
13953        var ss = doc.createElement("link");
13954        ss.setAttribute("rel", "stylesheet");
13955        ss.setAttribute("type", "text/css");
13956        ss.setAttribute("id", id);
13957        ss.setAttribute("href", url);
13958        doc.getElementsByTagName("head")[0].appendChild(ss);
13959    },
13960    
13961    /**
13962     * Refresh the rule cache if you have dynamically added stylesheets
13963     * @return {Object} An object (hash) of rules indexed by selector
13964     */
13965    refreshCache : function(){
13966        return this.getRules(true);
13967    },
13968
13969    // private
13970    cacheStyleSheet : function(stylesheet){
13971        if(!rules){
13972            rules = {};
13973        }
13974        try{// try catch for cross domain access issue
13975            var ssRules = stylesheet.cssRules || stylesheet.rules;
13976            for(var j = ssRules.length-1; j >= 0; --j){
13977                rules[ssRules[j].selectorText] = ssRules[j];
13978            }
13979        }catch(e){}
13980    },
13981    
13982    /**
13983     * Gets all css rules for the document
13984     * @param {Boolean} refreshCache true to refresh the internal cache
13985     * @return {Object} An object (hash) of rules indexed by selector
13986     */
13987    getRules : function(refreshCache){
13988                 if(rules == null || refreshCache){
13989                         rules = {};
13990                         var ds = doc.styleSheets;
13991                         for(var i =0, len = ds.length; i < len; i++){
13992                             try{
13993                         this.cacheStyleSheet(ds[i]);
13994                     }catch(e){} 
13995                 }
13996                 }
13997                 return rules;
13998         },
13999         
14000         /**
14001     * Gets an an individual CSS rule by selector(s)
14002     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14003     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14004     * @return {CSSRule} The CSS rule or null if one is not found
14005     */
14006    getRule : function(selector, refreshCache){
14007                 var rs = this.getRules(refreshCache);
14008                 if(!(selector instanceof Array)){
14009                     return rs[selector];
14010                 }
14011                 for(var i = 0; i < selector.length; i++){
14012                         if(rs[selector[i]]){
14013                                 return rs[selector[i]];
14014                         }
14015                 }
14016                 return null;
14017         },
14018         
14019         
14020         /**
14021     * Updates a rule property
14022     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14023     * @param {String} property The css property
14024     * @param {String} value The new value for the property
14025     * @return {Boolean} true If a rule was found and updated
14026     */
14027    updateRule : function(selector, property, value){
14028                 if(!(selector instanceof Array)){
14029                         var rule = this.getRule(selector);
14030                         if(rule){
14031                                 rule.style[property.replace(camelRe, camelFn)] = value;
14032                                 return true;
14033                         }
14034                 }else{
14035                         for(var i = 0; i < selector.length; i++){
14036                                 if(this.updateRule(selector[i], property, value)){
14037                                         return true;
14038                                 }
14039                         }
14040                 }
14041                 return false;
14042         }
14043    };   
14044 }();/*
14045  * Based on:
14046  * Ext JS Library 1.1.1
14047  * Copyright(c) 2006-2007, Ext JS, LLC.
14048  *
14049  * Originally Released Under LGPL - original licence link has changed is not relivant.
14050  *
14051  * Fork - LGPL
14052  * <script type="text/javascript">
14053  */
14054
14055  
14056
14057 /**
14058  * @class Roo.util.ClickRepeater
14059  * @extends Roo.util.Observable
14060  * 
14061  * A wrapper class which can be applied to any element. Fires a "click" event while the
14062  * mouse is pressed. The interval between firings may be specified in the config but
14063  * defaults to 10 milliseconds.
14064  * 
14065  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14066  * 
14067  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14068  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14069  * Similar to an autorepeat key delay.
14070  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14071  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14072  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14073  *           "interval" and "delay" are ignored. "immediate" is honored.
14074  * @cfg {Boolean} preventDefault True to prevent the default click event
14075  * @cfg {Boolean} stopDefault True to stop the default click event
14076  * 
14077  * @history
14078  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14079  *     2007-02-02 jvs Renamed to ClickRepeater
14080  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14081  *
14082  *  @constructor
14083  * @param {String/HTMLElement/Element} el The element to listen on
14084  * @param {Object} config
14085  **/
14086 Roo.util.ClickRepeater = function(el, config)
14087 {
14088     this.el = Roo.get(el);
14089     this.el.unselectable();
14090
14091     Roo.apply(this, config);
14092
14093     this.addEvents({
14094     /**
14095      * @event mousedown
14096      * Fires when the mouse button is depressed.
14097      * @param {Roo.util.ClickRepeater} this
14098      */
14099         "mousedown" : true,
14100     /**
14101      * @event click
14102      * Fires on a specified interval during the time the element is pressed.
14103      * @param {Roo.util.ClickRepeater} this
14104      */
14105         "click" : true,
14106     /**
14107      * @event mouseup
14108      * Fires when the mouse key is released.
14109      * @param {Roo.util.ClickRepeater} this
14110      */
14111         "mouseup" : true
14112     });
14113
14114     this.el.on("mousedown", this.handleMouseDown, this);
14115     if(this.preventDefault || this.stopDefault){
14116         this.el.on("click", function(e){
14117             if(this.preventDefault){
14118                 e.preventDefault();
14119             }
14120             if(this.stopDefault){
14121                 e.stopEvent();
14122             }
14123         }, this);
14124     }
14125
14126     // allow inline handler
14127     if(this.handler){
14128         this.on("click", this.handler,  this.scope || this);
14129     }
14130
14131     Roo.util.ClickRepeater.superclass.constructor.call(this);
14132 };
14133
14134 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14135     interval : 20,
14136     delay: 250,
14137     preventDefault : true,
14138     stopDefault : false,
14139     timer : 0,
14140
14141     // private
14142     handleMouseDown : function(){
14143         clearTimeout(this.timer);
14144         this.el.blur();
14145         if(this.pressClass){
14146             this.el.addClass(this.pressClass);
14147         }
14148         this.mousedownTime = new Date();
14149
14150         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14151         this.el.on("mouseout", this.handleMouseOut, this);
14152
14153         this.fireEvent("mousedown", this);
14154         this.fireEvent("click", this);
14155         
14156         this.timer = this.click.defer(this.delay || this.interval, this);
14157     },
14158
14159     // private
14160     click : function(){
14161         this.fireEvent("click", this);
14162         this.timer = this.click.defer(this.getInterval(), this);
14163     },
14164
14165     // private
14166     getInterval: function(){
14167         if(!this.accelerate){
14168             return this.interval;
14169         }
14170         var pressTime = this.mousedownTime.getElapsed();
14171         if(pressTime < 500){
14172             return 400;
14173         }else if(pressTime < 1700){
14174             return 320;
14175         }else if(pressTime < 2600){
14176             return 250;
14177         }else if(pressTime < 3500){
14178             return 180;
14179         }else if(pressTime < 4400){
14180             return 140;
14181         }else if(pressTime < 5300){
14182             return 80;
14183         }else if(pressTime < 6200){
14184             return 50;
14185         }else{
14186             return 10;
14187         }
14188     },
14189
14190     // private
14191     handleMouseOut : function(){
14192         clearTimeout(this.timer);
14193         if(this.pressClass){
14194             this.el.removeClass(this.pressClass);
14195         }
14196         this.el.on("mouseover", this.handleMouseReturn, this);
14197     },
14198
14199     // private
14200     handleMouseReturn : function(){
14201         this.el.un("mouseover", this.handleMouseReturn);
14202         if(this.pressClass){
14203             this.el.addClass(this.pressClass);
14204         }
14205         this.click();
14206     },
14207
14208     // private
14209     handleMouseUp : function(){
14210         clearTimeout(this.timer);
14211         this.el.un("mouseover", this.handleMouseReturn);
14212         this.el.un("mouseout", this.handleMouseOut);
14213         Roo.get(document).un("mouseup", this.handleMouseUp);
14214         this.el.removeClass(this.pressClass);
14215         this.fireEvent("mouseup", this);
14216     }
14217 });/*
14218  * Based on:
14219  * Ext JS Library 1.1.1
14220  * Copyright(c) 2006-2007, Ext JS, LLC.
14221  *
14222  * Originally Released Under LGPL - original licence link has changed is not relivant.
14223  *
14224  * Fork - LGPL
14225  * <script type="text/javascript">
14226  */
14227
14228  
14229 /**
14230  * @class Roo.KeyNav
14231  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14232  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14233  * way to implement custom navigation schemes for any UI component.</p>
14234  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14235  * pageUp, pageDown, del, home, end.  Usage:</p>
14236  <pre><code>
14237 var nav = new Roo.KeyNav("my-element", {
14238     "left" : function(e){
14239         this.moveLeft(e.ctrlKey);
14240     },
14241     "right" : function(e){
14242         this.moveRight(e.ctrlKey);
14243     },
14244     "enter" : function(e){
14245         this.save();
14246     },
14247     scope : this
14248 });
14249 </code></pre>
14250  * @constructor
14251  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14252  * @param {Object} config The config
14253  */
14254 Roo.KeyNav = function(el, config){
14255     this.el = Roo.get(el);
14256     Roo.apply(this, config);
14257     if(!this.disabled){
14258         this.disabled = true;
14259         this.enable();
14260     }
14261 };
14262
14263 Roo.KeyNav.prototype = {
14264     /**
14265      * @cfg {Boolean} disabled
14266      * True to disable this KeyNav instance (defaults to false)
14267      */
14268     disabled : false,
14269     /**
14270      * @cfg {String} defaultEventAction
14271      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14272      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14273      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14274      */
14275     defaultEventAction: "stopEvent",
14276     /**
14277      * @cfg {Boolean} forceKeyDown
14278      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14279      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14280      * handle keydown instead of keypress.
14281      */
14282     forceKeyDown : false,
14283
14284     // private
14285     prepareEvent : function(e){
14286         var k = e.getKey();
14287         var h = this.keyToHandler[k];
14288         //if(h && this[h]){
14289         //    e.stopPropagation();
14290         //}
14291         if(Roo.isSafari && h && k >= 37 && k <= 40){
14292             e.stopEvent();
14293         }
14294     },
14295
14296     // private
14297     relay : function(e){
14298         var k = e.getKey();
14299         var h = this.keyToHandler[k];
14300         if(h && this[h]){
14301             if(this.doRelay(e, this[h], h) !== true){
14302                 e[this.defaultEventAction]();
14303             }
14304         }
14305     },
14306
14307     // private
14308     doRelay : function(e, h, hname){
14309         return h.call(this.scope || this, e);
14310     },
14311
14312     // possible handlers
14313     enter : false,
14314     left : false,
14315     right : false,
14316     up : false,
14317     down : false,
14318     tab : false,
14319     esc : false,
14320     pageUp : false,
14321     pageDown : false,
14322     del : false,
14323     home : false,
14324     end : false,
14325
14326     // quick lookup hash
14327     keyToHandler : {
14328         37 : "left",
14329         39 : "right",
14330         38 : "up",
14331         40 : "down",
14332         33 : "pageUp",
14333         34 : "pageDown",
14334         46 : "del",
14335         36 : "home",
14336         35 : "end",
14337         13 : "enter",
14338         27 : "esc",
14339         9  : "tab"
14340     },
14341
14342         /**
14343          * Enable this KeyNav
14344          */
14345         enable: function(){
14346                 if(this.disabled){
14347             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14348             // the EventObject will normalize Safari automatically
14349             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14350                 this.el.on("keydown", this.relay,  this);
14351             }else{
14352                 this.el.on("keydown", this.prepareEvent,  this);
14353                 this.el.on("keypress", this.relay,  this);
14354             }
14355                     this.disabled = false;
14356                 }
14357         },
14358
14359         /**
14360          * Disable this KeyNav
14361          */
14362         disable: function(){
14363                 if(!this.disabled){
14364                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14365                 this.el.un("keydown", this.relay);
14366             }else{
14367                 this.el.un("keydown", this.prepareEvent);
14368                 this.el.un("keypress", this.relay);
14369             }
14370                     this.disabled = true;
14371                 }
14372         }
14373 };/*
14374  * Based on:
14375  * Ext JS Library 1.1.1
14376  * Copyright(c) 2006-2007, Ext JS, LLC.
14377  *
14378  * Originally Released Under LGPL - original licence link has changed is not relivant.
14379  *
14380  * Fork - LGPL
14381  * <script type="text/javascript">
14382  */
14383
14384  
14385 /**
14386  * @class Roo.KeyMap
14387  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14388  * The constructor accepts the same config object as defined by {@link #addBinding}.
14389  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14390  * combination it will call the function with this signature (if the match is a multi-key
14391  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14392  * A KeyMap can also handle a string representation of keys.<br />
14393  * Usage:
14394  <pre><code>
14395 // map one key by key code
14396 var map = new Roo.KeyMap("my-element", {
14397     key: 13, // or Roo.EventObject.ENTER
14398     fn: myHandler,
14399     scope: myObject
14400 });
14401
14402 // map multiple keys to one action by string
14403 var map = new Roo.KeyMap("my-element", {
14404     key: "a\r\n\t",
14405     fn: myHandler,
14406     scope: myObject
14407 });
14408
14409 // map multiple keys to multiple actions by strings and array of codes
14410 var map = new Roo.KeyMap("my-element", [
14411     {
14412         key: [10,13],
14413         fn: function(){ alert("Return was pressed"); }
14414     }, {
14415         key: "abc",
14416         fn: function(){ alert('a, b or c was pressed'); }
14417     }, {
14418         key: "\t",
14419         ctrl:true,
14420         shift:true,
14421         fn: function(){ alert('Control + shift + tab was pressed.'); }
14422     }
14423 ]);
14424 </code></pre>
14425  * <b>Note: A KeyMap starts enabled</b>
14426  * @constructor
14427  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14428  * @param {Object} config The config (see {@link #addBinding})
14429  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14430  */
14431 Roo.KeyMap = function(el, config, eventName){
14432     this.el  = Roo.get(el);
14433     this.eventName = eventName || "keydown";
14434     this.bindings = [];
14435     if(config){
14436         this.addBinding(config);
14437     }
14438     this.enable();
14439 };
14440
14441 Roo.KeyMap.prototype = {
14442     /**
14443      * True to stop the event from bubbling and prevent the default browser action if the
14444      * key was handled by the KeyMap (defaults to false)
14445      * @type Boolean
14446      */
14447     stopEvent : false,
14448
14449     /**
14450      * Add a new binding to this KeyMap. The following config object properties are supported:
14451      * <pre>
14452 Property    Type             Description
14453 ----------  ---------------  ----------------------------------------------------------------------
14454 key         String/Array     A single keycode or an array of keycodes to handle
14455 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14456 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14457 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14458 fn          Function         The function to call when KeyMap finds the expected key combination
14459 scope       Object           The scope of the callback function
14460 </pre>
14461      *
14462      * Usage:
14463      * <pre><code>
14464 // Create a KeyMap
14465 var map = new Roo.KeyMap(document, {
14466     key: Roo.EventObject.ENTER,
14467     fn: handleKey,
14468     scope: this
14469 });
14470
14471 //Add a new binding to the existing KeyMap later
14472 map.addBinding({
14473     key: 'abc',
14474     shift: true,
14475     fn: handleKey,
14476     scope: this
14477 });
14478 </code></pre>
14479      * @param {Object/Array} config A single KeyMap config or an array of configs
14480      */
14481         addBinding : function(config){
14482         if(config instanceof Array){
14483             for(var i = 0, len = config.length; i < len; i++){
14484                 this.addBinding(config[i]);
14485             }
14486             return;
14487         }
14488         var keyCode = config.key,
14489             shift = config.shift, 
14490             ctrl = config.ctrl, 
14491             alt = config.alt,
14492             fn = config.fn,
14493             scope = config.scope;
14494         if(typeof keyCode == "string"){
14495             var ks = [];
14496             var keyString = keyCode.toUpperCase();
14497             for(var j = 0, len = keyString.length; j < len; j++){
14498                 ks.push(keyString.charCodeAt(j));
14499             }
14500             keyCode = ks;
14501         }
14502         var keyArray = keyCode instanceof Array;
14503         var handler = function(e){
14504             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14505                 var k = e.getKey();
14506                 if(keyArray){
14507                     for(var i = 0, len = keyCode.length; i < len; i++){
14508                         if(keyCode[i] == k){
14509                           if(this.stopEvent){
14510                               e.stopEvent();
14511                           }
14512                           fn.call(scope || window, k, e);
14513                           return;
14514                         }
14515                     }
14516                 }else{
14517                     if(k == keyCode){
14518                         if(this.stopEvent){
14519                            e.stopEvent();
14520                         }
14521                         fn.call(scope || window, k, e);
14522                     }
14523                 }
14524             }
14525         };
14526         this.bindings.push(handler);  
14527         },
14528
14529     /**
14530      * Shorthand for adding a single key listener
14531      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14532      * following options:
14533      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14534      * @param {Function} fn The function to call
14535      * @param {Object} scope (optional) The scope of the function
14536      */
14537     on : function(key, fn, scope){
14538         var keyCode, shift, ctrl, alt;
14539         if(typeof key == "object" && !(key instanceof Array)){
14540             keyCode = key.key;
14541             shift = key.shift;
14542             ctrl = key.ctrl;
14543             alt = key.alt;
14544         }else{
14545             keyCode = key;
14546         }
14547         this.addBinding({
14548             key: keyCode,
14549             shift: shift,
14550             ctrl: ctrl,
14551             alt: alt,
14552             fn: fn,
14553             scope: scope
14554         })
14555     },
14556
14557     // private
14558     handleKeyDown : function(e){
14559             if(this.enabled){ //just in case
14560             var b = this.bindings;
14561             for(var i = 0, len = b.length; i < len; i++){
14562                 b[i].call(this, e);
14563             }
14564             }
14565         },
14566         
14567         /**
14568          * Returns true if this KeyMap is enabled
14569          * @return {Boolean} 
14570          */
14571         isEnabled : function(){
14572             return this.enabled;  
14573         },
14574         
14575         /**
14576          * Enables this KeyMap
14577          */
14578         enable: function(){
14579                 if(!this.enabled){
14580                     this.el.on(this.eventName, this.handleKeyDown, this);
14581                     this.enabled = true;
14582                 }
14583         },
14584
14585         /**
14586          * Disable this KeyMap
14587          */
14588         disable: function(){
14589                 if(this.enabled){
14590                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14591                     this.enabled = false;
14592                 }
14593         }
14594 };/*
14595  * Based on:
14596  * Ext JS Library 1.1.1
14597  * Copyright(c) 2006-2007, Ext JS, LLC.
14598  *
14599  * Originally Released Under LGPL - original licence link has changed is not relivant.
14600  *
14601  * Fork - LGPL
14602  * <script type="text/javascript">
14603  */
14604
14605  
14606 /**
14607  * @class Roo.util.TextMetrics
14608  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14609  * wide, in pixels, a given block of text will be.
14610  * @singleton
14611  */
14612 Roo.util.TextMetrics = function(){
14613     var shared;
14614     return {
14615         /**
14616          * Measures the size of the specified text
14617          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14618          * that can affect the size of the rendered text
14619          * @param {String} text The text to measure
14620          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14621          * in order to accurately measure the text height
14622          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14623          */
14624         measure : function(el, text, fixedWidth){
14625             if(!shared){
14626                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14627             }
14628             shared.bind(el);
14629             shared.setFixedWidth(fixedWidth || 'auto');
14630             return shared.getSize(text);
14631         },
14632
14633         /**
14634          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14635          * the overhead of multiple calls to initialize the style properties on each measurement.
14636          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14637          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14638          * in order to accurately measure the text height
14639          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14640          */
14641         createInstance : function(el, fixedWidth){
14642             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14643         }
14644     };
14645 }();
14646
14647  
14648
14649 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14650     var ml = new Roo.Element(document.createElement('div'));
14651     document.body.appendChild(ml.dom);
14652     ml.position('absolute');
14653     ml.setLeftTop(-1000, -1000);
14654     ml.hide();
14655
14656     if(fixedWidth){
14657         ml.setWidth(fixedWidth);
14658     }
14659      
14660     var instance = {
14661         /**
14662          * Returns the size of the specified text based on the internal element's style and width properties
14663          * @memberOf Roo.util.TextMetrics.Instance#
14664          * @param {String} text The text to measure
14665          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14666          */
14667         getSize : function(text){
14668             ml.update(text);
14669             var s = ml.getSize();
14670             ml.update('');
14671             return s;
14672         },
14673
14674         /**
14675          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14676          * that can affect the size of the rendered text
14677          * @memberOf Roo.util.TextMetrics.Instance#
14678          * @param {String/HTMLElement} el The element, dom node or id
14679          */
14680         bind : function(el){
14681             ml.setStyle(
14682                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14683             );
14684         },
14685
14686         /**
14687          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14688          * to set a fixed width in order to accurately measure the text height.
14689          * @memberOf Roo.util.TextMetrics.Instance#
14690          * @param {Number} width The width to set on the element
14691          */
14692         setFixedWidth : function(width){
14693             ml.setWidth(width);
14694         },
14695
14696         /**
14697          * Returns the measured width of the specified text
14698          * @memberOf Roo.util.TextMetrics.Instance#
14699          * @param {String} text The text to measure
14700          * @return {Number} width The width in pixels
14701          */
14702         getWidth : function(text){
14703             ml.dom.style.width = 'auto';
14704             return this.getSize(text).width;
14705         },
14706
14707         /**
14708          * Returns the measured height of the specified text.  For multiline text, be sure to call
14709          * {@link #setFixedWidth} if necessary.
14710          * @memberOf Roo.util.TextMetrics.Instance#
14711          * @param {String} text The text to measure
14712          * @return {Number} height The height in pixels
14713          */
14714         getHeight : function(text){
14715             return this.getSize(text).height;
14716         }
14717     };
14718
14719     instance.bind(bindTo);
14720
14721     return instance;
14722 };
14723
14724 // backwards compat
14725 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14726  * Based on:
14727  * Ext JS Library 1.1.1
14728  * Copyright(c) 2006-2007, Ext JS, LLC.
14729  *
14730  * Originally Released Under LGPL - original licence link has changed is not relivant.
14731  *
14732  * Fork - LGPL
14733  * <script type="text/javascript">
14734  */
14735
14736 /**
14737  * @class Roo.state.Provider
14738  * Abstract base class for state provider implementations. This class provides methods
14739  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14740  * Provider interface.
14741  */
14742 Roo.state.Provider = function(){
14743     /**
14744      * @event statechange
14745      * Fires when a state change occurs.
14746      * @param {Provider} this This state provider
14747      * @param {String} key The state key which was changed
14748      * @param {String} value The encoded value for the state
14749      */
14750     this.addEvents({
14751         "statechange": true
14752     });
14753     this.state = {};
14754     Roo.state.Provider.superclass.constructor.call(this);
14755 };
14756 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14757     /**
14758      * Returns the current value for a key
14759      * @param {String} name The key name
14760      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14761      * @return {Mixed} The state data
14762      */
14763     get : function(name, defaultValue){
14764         return typeof this.state[name] == "undefined" ?
14765             defaultValue : this.state[name];
14766     },
14767     
14768     /**
14769      * Clears a value from the state
14770      * @param {String} name The key name
14771      */
14772     clear : function(name){
14773         delete this.state[name];
14774         this.fireEvent("statechange", this, name, null);
14775     },
14776     
14777     /**
14778      * Sets the value for a key
14779      * @param {String} name The key name
14780      * @param {Mixed} value The value to set
14781      */
14782     set : function(name, value){
14783         this.state[name] = value;
14784         this.fireEvent("statechange", this, name, value);
14785     },
14786     
14787     /**
14788      * Decodes a string previously encoded with {@link #encodeValue}.
14789      * @param {String} value The value to decode
14790      * @return {Mixed} The decoded value
14791      */
14792     decodeValue : function(cookie){
14793         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14794         var matches = re.exec(unescape(cookie));
14795         if(!matches || !matches[1]) return; // non state cookie
14796         var type = matches[1];
14797         var v = matches[2];
14798         switch(type){
14799             case "n":
14800                 return parseFloat(v);
14801             case "d":
14802                 return new Date(Date.parse(v));
14803             case "b":
14804                 return (v == "1");
14805             case "a":
14806                 var all = [];
14807                 var values = v.split("^");
14808                 for(var i = 0, len = values.length; i < len; i++){
14809                     all.push(this.decodeValue(values[i]));
14810                 }
14811                 return all;
14812            case "o":
14813                 var all = {};
14814                 var values = v.split("^");
14815                 for(var i = 0, len = values.length; i < len; i++){
14816                     var kv = values[i].split("=");
14817                     all[kv[0]] = this.decodeValue(kv[1]);
14818                 }
14819                 return all;
14820            default:
14821                 return v;
14822         }
14823     },
14824     
14825     /**
14826      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14827      * @param {Mixed} value The value to encode
14828      * @return {String} The encoded value
14829      */
14830     encodeValue : function(v){
14831         var enc;
14832         if(typeof v == "number"){
14833             enc = "n:" + v;
14834         }else if(typeof v == "boolean"){
14835             enc = "b:" + (v ? "1" : "0");
14836         }else if(v instanceof Date){
14837             enc = "d:" + v.toGMTString();
14838         }else if(v instanceof Array){
14839             var flat = "";
14840             for(var i = 0, len = v.length; i < len; i++){
14841                 flat += this.encodeValue(v[i]);
14842                 if(i != len-1) flat += "^";
14843             }
14844             enc = "a:" + flat;
14845         }else if(typeof v == "object"){
14846             var flat = "";
14847             for(var key in v){
14848                 if(typeof v[key] != "function"){
14849                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14850                 }
14851             }
14852             enc = "o:" + flat.substring(0, flat.length-1);
14853         }else{
14854             enc = "s:" + v;
14855         }
14856         return escape(enc);        
14857     }
14858 });
14859
14860 /*
14861  * Based on:
14862  * Ext JS Library 1.1.1
14863  * Copyright(c) 2006-2007, Ext JS, LLC.
14864  *
14865  * Originally Released Under LGPL - original licence link has changed is not relivant.
14866  *
14867  * Fork - LGPL
14868  * <script type="text/javascript">
14869  */
14870 /**
14871  * @class Roo.state.Manager
14872  * This is the global state manager. By default all components that are "state aware" check this class
14873  * for state information if you don't pass them a custom state provider. In order for this class
14874  * to be useful, it must be initialized with a provider when your application initializes.
14875  <pre><code>
14876 // in your initialization function
14877 init : function(){
14878    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14879    ...
14880    // supposed you have a {@link Roo.BorderLayout}
14881    var layout = new Roo.BorderLayout(...);
14882    layout.restoreState();
14883    // or a {Roo.BasicDialog}
14884    var dialog = new Roo.BasicDialog(...);
14885    dialog.restoreState();
14886  </code></pre>
14887  * @singleton
14888  */
14889 Roo.state.Manager = function(){
14890     var provider = new Roo.state.Provider();
14891     
14892     return {
14893         /**
14894          * Configures the default state provider for your application
14895          * @param {Provider} stateProvider The state provider to set
14896          */
14897         setProvider : function(stateProvider){
14898             provider = stateProvider;
14899         },
14900         
14901         /**
14902          * Returns the current value for a key
14903          * @param {String} name The key name
14904          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14905          * @return {Mixed} The state data
14906          */
14907         get : function(key, defaultValue){
14908             return provider.get(key, defaultValue);
14909         },
14910         
14911         /**
14912          * Sets the value for a key
14913          * @param {String} name The key name
14914          * @param {Mixed} value The state data
14915          */
14916          set : function(key, value){
14917             provider.set(key, value);
14918         },
14919         
14920         /**
14921          * Clears a value from the state
14922          * @param {String} name The key name
14923          */
14924         clear : function(key){
14925             provider.clear(key);
14926         },
14927         
14928         /**
14929          * Gets the currently configured state provider
14930          * @return {Provider} The state provider
14931          */
14932         getProvider : function(){
14933             return provider;
14934         }
14935     };
14936 }();
14937 /*
14938  * Based on:
14939  * Ext JS Library 1.1.1
14940  * Copyright(c) 2006-2007, Ext JS, LLC.
14941  *
14942  * Originally Released Under LGPL - original licence link has changed is not relivant.
14943  *
14944  * Fork - LGPL
14945  * <script type="text/javascript">
14946  */
14947 /**
14948  * @class Roo.state.CookieProvider
14949  * @extends Roo.state.Provider
14950  * The default Provider implementation which saves state via cookies.
14951  * <br />Usage:
14952  <pre><code>
14953    var cp = new Roo.state.CookieProvider({
14954        path: "/cgi-bin/",
14955        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14956        domain: "roojs.com"
14957    })
14958    Roo.state.Manager.setProvider(cp);
14959  </code></pre>
14960  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14961  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14962  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14963  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14964  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14965  * domain the page is running on including the 'www' like 'www.roojs.com')
14966  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14967  * @constructor
14968  * Create a new CookieProvider
14969  * @param {Object} config The configuration object
14970  */
14971 Roo.state.CookieProvider = function(config){
14972     Roo.state.CookieProvider.superclass.constructor.call(this);
14973     this.path = "/";
14974     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14975     this.domain = null;
14976     this.secure = false;
14977     Roo.apply(this, config);
14978     this.state = this.readCookies();
14979 };
14980
14981 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14982     // private
14983     set : function(name, value){
14984         if(typeof value == "undefined" || value === null){
14985             this.clear(name);
14986             return;
14987         }
14988         this.setCookie(name, value);
14989         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14990     },
14991
14992     // private
14993     clear : function(name){
14994         this.clearCookie(name);
14995         Roo.state.CookieProvider.superclass.clear.call(this, name);
14996     },
14997
14998     // private
14999     readCookies : function(){
15000         var cookies = {};
15001         var c = document.cookie + ";";
15002         var re = /\s?(.*?)=(.*?);/g;
15003         var matches;
15004         while((matches = re.exec(c)) != null){
15005             var name = matches[1];
15006             var value = matches[2];
15007             if(name && name.substring(0,3) == "ys-"){
15008                 cookies[name.substr(3)] = this.decodeValue(value);
15009             }
15010         }
15011         return cookies;
15012     },
15013
15014     // private
15015     setCookie : function(name, value){
15016         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15017            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15018            ((this.path == null) ? "" : ("; path=" + this.path)) +
15019            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15020            ((this.secure == true) ? "; secure" : "");
15021     },
15022
15023     // private
15024     clearCookie : function(name){
15025         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15026            ((this.path == null) ? "" : ("; path=" + this.path)) +
15027            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15028            ((this.secure == true) ? "; secure" : "");
15029     }
15030 });/*
15031  * Based on:
15032  * Ext JS Library 1.1.1
15033  * Copyright(c) 2006-2007, Ext JS, LLC.
15034  *
15035  * Originally Released Under LGPL - original licence link has changed is not relivant.
15036  *
15037  * Fork - LGPL
15038  * <script type="text/javascript">
15039  */
15040  
15041
15042 /**
15043  * @class Roo.ComponentMgr
15044  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15045  * @singleton
15046  */
15047 Roo.ComponentMgr = function(){
15048     var all = new Roo.util.MixedCollection();
15049
15050     return {
15051         /**
15052          * Registers a component.
15053          * @param {Roo.Component} c The component
15054          */
15055         register : function(c){
15056             all.add(c);
15057         },
15058
15059         /**
15060          * Unregisters a component.
15061          * @param {Roo.Component} c The component
15062          */
15063         unregister : function(c){
15064             all.remove(c);
15065         },
15066
15067         /**
15068          * Returns a component by id
15069          * @param {String} id The component id
15070          */
15071         get : function(id){
15072             return all.get(id);
15073         },
15074
15075         /**
15076          * Registers a function that will be called when a specified component is added to ComponentMgr
15077          * @param {String} id The component id
15078          * @param {Funtction} fn The callback function
15079          * @param {Object} scope The scope of the callback
15080          */
15081         onAvailable : function(id, fn, scope){
15082             all.on("add", function(index, o){
15083                 if(o.id == id){
15084                     fn.call(scope || o, o);
15085                     all.un("add", fn, scope);
15086                 }
15087             });
15088         }
15089     };
15090 }();/*
15091  * Based on:
15092  * Ext JS Library 1.1.1
15093  * Copyright(c) 2006-2007, Ext JS, LLC.
15094  *
15095  * Originally Released Under LGPL - original licence link has changed is not relivant.
15096  *
15097  * Fork - LGPL
15098  * <script type="text/javascript">
15099  */
15100  
15101 /**
15102  * @class Roo.Component
15103  * @extends Roo.util.Observable
15104  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15105  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15106  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15107  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15108  * All visual components (widgets) that require rendering into a layout should subclass Component.
15109  * @constructor
15110  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15111  * 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
15112  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15113  */
15114 Roo.Component = function(config){
15115     config = config || {};
15116     if(config.tagName || config.dom || typeof config == "string"){ // element object
15117         config = {el: config, id: config.id || config};
15118     }
15119     this.initialConfig = config;
15120
15121     Roo.apply(this, config);
15122     this.addEvents({
15123         /**
15124          * @event disable
15125          * Fires after the component is disabled.
15126              * @param {Roo.Component} this
15127              */
15128         disable : true,
15129         /**
15130          * @event enable
15131          * Fires after the component is enabled.
15132              * @param {Roo.Component} this
15133              */
15134         enable : true,
15135         /**
15136          * @event beforeshow
15137          * Fires before the component is shown.  Return false to stop the show.
15138              * @param {Roo.Component} this
15139              */
15140         beforeshow : true,
15141         /**
15142          * @event show
15143          * Fires after the component is shown.
15144              * @param {Roo.Component} this
15145              */
15146         show : true,
15147         /**
15148          * @event beforehide
15149          * Fires before the component is hidden. Return false to stop the hide.
15150              * @param {Roo.Component} this
15151              */
15152         beforehide : true,
15153         /**
15154          * @event hide
15155          * Fires after the component is hidden.
15156              * @param {Roo.Component} this
15157              */
15158         hide : true,
15159         /**
15160          * @event beforerender
15161          * Fires before the component is rendered. Return false to stop the render.
15162              * @param {Roo.Component} this
15163              */
15164         beforerender : true,
15165         /**
15166          * @event render
15167          * Fires after the component is rendered.
15168              * @param {Roo.Component} this
15169              */
15170         render : true,
15171         /**
15172          * @event beforedestroy
15173          * Fires before the component is destroyed. Return false to stop the destroy.
15174              * @param {Roo.Component} this
15175              */
15176         beforedestroy : true,
15177         /**
15178          * @event destroy
15179          * Fires after the component is destroyed.
15180              * @param {Roo.Component} this
15181              */
15182         destroy : true
15183     });
15184     if(!this.id){
15185         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15186     }
15187     Roo.ComponentMgr.register(this);
15188     Roo.Component.superclass.constructor.call(this);
15189     this.initComponent();
15190     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15191         this.render(this.renderTo);
15192         delete this.renderTo;
15193     }
15194 };
15195
15196 /** @private */
15197 Roo.Component.AUTO_ID = 1000;
15198
15199 Roo.extend(Roo.Component, Roo.util.Observable, {
15200     /**
15201      * @scope Roo.Component.prototype
15202      * @type {Boolean}
15203      * true if this component is hidden. Read-only.
15204      */
15205     hidden : false,
15206     /**
15207      * @type {Boolean}
15208      * true if this component is disabled. Read-only.
15209      */
15210     disabled : false,
15211     /**
15212      * @type {Boolean}
15213      * true if this component has been rendered. Read-only.
15214      */
15215     rendered : false,
15216     
15217     /** @cfg {String} disableClass
15218      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15219      */
15220     disabledClass : "x-item-disabled",
15221         /** @cfg {Boolean} allowDomMove
15222          * Whether the component can move the Dom node when rendering (defaults to true).
15223          */
15224     allowDomMove : true,
15225     /** @cfg {String} hideMode (display|visibility)
15226      * How this component should hidden. Supported values are
15227      * "visibility" (css visibility), "offsets" (negative offset position) and
15228      * "display" (css display) - defaults to "display".
15229      */
15230     hideMode: 'display',
15231
15232     /** @private */
15233     ctype : "Roo.Component",
15234
15235     /**
15236      * @cfg {String} actionMode 
15237      * which property holds the element that used for  hide() / show() / disable() / enable()
15238      * default is 'el' 
15239      */
15240     actionMode : "el",
15241
15242     /** @private */
15243     getActionEl : function(){
15244         return this[this.actionMode];
15245     },
15246
15247     initComponent : Roo.emptyFn,
15248     /**
15249      * If this is a lazy rendering component, render it to its container element.
15250      * @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.
15251      */
15252     render : function(container, position){
15253         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15254             if(!container && this.el){
15255                 this.el = Roo.get(this.el);
15256                 container = this.el.dom.parentNode;
15257                 this.allowDomMove = false;
15258             }
15259             this.container = Roo.get(container);
15260             this.rendered = true;
15261             if(position !== undefined){
15262                 if(typeof position == 'number'){
15263                     position = this.container.dom.childNodes[position];
15264                 }else{
15265                     position = Roo.getDom(position);
15266                 }
15267             }
15268             this.onRender(this.container, position || null);
15269             if(this.cls){
15270                 this.el.addClass(this.cls);
15271                 delete this.cls;
15272             }
15273             if(this.style){
15274                 this.el.applyStyles(this.style);
15275                 delete this.style;
15276             }
15277             this.fireEvent("render", this);
15278             this.afterRender(this.container);
15279             if(this.hidden){
15280                 this.hide();
15281             }
15282             if(this.disabled){
15283                 this.disable();
15284             }
15285         }
15286         return this;
15287     },
15288
15289     /** @private */
15290     // default function is not really useful
15291     onRender : function(ct, position){
15292         if(this.el){
15293             this.el = Roo.get(this.el);
15294             if(this.allowDomMove !== false){
15295                 ct.dom.insertBefore(this.el.dom, position);
15296             }
15297         }
15298     },
15299
15300     /** @private */
15301     getAutoCreate : function(){
15302         var cfg = typeof this.autoCreate == "object" ?
15303                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15304         if(this.id && !cfg.id){
15305             cfg.id = this.id;
15306         }
15307         return cfg;
15308     },
15309
15310     /** @private */
15311     afterRender : Roo.emptyFn,
15312
15313     /**
15314      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15315      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15316      */
15317     destroy : function(){
15318         if(this.fireEvent("beforedestroy", this) !== false){
15319             this.purgeListeners();
15320             this.beforeDestroy();
15321             if(this.rendered){
15322                 this.el.removeAllListeners();
15323                 this.el.remove();
15324                 if(this.actionMode == "container"){
15325                     this.container.remove();
15326                 }
15327             }
15328             this.onDestroy();
15329             Roo.ComponentMgr.unregister(this);
15330             this.fireEvent("destroy", this);
15331         }
15332     },
15333
15334         /** @private */
15335     beforeDestroy : function(){
15336
15337     },
15338
15339         /** @private */
15340         onDestroy : function(){
15341
15342     },
15343
15344     /**
15345      * Returns the underlying {@link Roo.Element}.
15346      * @return {Roo.Element} The element
15347      */
15348     getEl : function(){
15349         return this.el;
15350     },
15351
15352     /**
15353      * Returns the id of this component.
15354      * @return {String}
15355      */
15356     getId : function(){
15357         return this.id;
15358     },
15359
15360     /**
15361      * Try to focus this component.
15362      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15363      * @return {Roo.Component} this
15364      */
15365     focus : function(selectText){
15366         if(this.rendered){
15367             this.el.focus();
15368             if(selectText === true){
15369                 this.el.dom.select();
15370             }
15371         }
15372         return this;
15373     },
15374
15375     /** @private */
15376     blur : function(){
15377         if(this.rendered){
15378             this.el.blur();
15379         }
15380         return this;
15381     },
15382
15383     /**
15384      * Disable this component.
15385      * @return {Roo.Component} this
15386      */
15387     disable : function(){
15388         if(this.rendered){
15389             this.onDisable();
15390         }
15391         this.disabled = true;
15392         this.fireEvent("disable", this);
15393         return this;
15394     },
15395
15396         // private
15397     onDisable : function(){
15398         this.getActionEl().addClass(this.disabledClass);
15399         this.el.dom.disabled = true;
15400     },
15401
15402     /**
15403      * Enable this component.
15404      * @return {Roo.Component} this
15405      */
15406     enable : function(){
15407         if(this.rendered){
15408             this.onEnable();
15409         }
15410         this.disabled = false;
15411         this.fireEvent("enable", this);
15412         return this;
15413     },
15414
15415         // private
15416     onEnable : function(){
15417         this.getActionEl().removeClass(this.disabledClass);
15418         this.el.dom.disabled = false;
15419     },
15420
15421     /**
15422      * Convenience function for setting disabled/enabled by boolean.
15423      * @param {Boolean} disabled
15424      */
15425     setDisabled : function(disabled){
15426         this[disabled ? "disable" : "enable"]();
15427     },
15428
15429     /**
15430      * Show this component.
15431      * @return {Roo.Component} this
15432      */
15433     show: function(){
15434         if(this.fireEvent("beforeshow", this) !== false){
15435             this.hidden = false;
15436             if(this.rendered){
15437                 this.onShow();
15438             }
15439             this.fireEvent("show", this);
15440         }
15441         return this;
15442     },
15443
15444     // private
15445     onShow : function(){
15446         var ae = this.getActionEl();
15447         if(this.hideMode == 'visibility'){
15448             ae.dom.style.visibility = "visible";
15449         }else if(this.hideMode == 'offsets'){
15450             ae.removeClass('x-hidden');
15451         }else{
15452             ae.dom.style.display = "";
15453         }
15454     },
15455
15456     /**
15457      * Hide this component.
15458      * @return {Roo.Component} this
15459      */
15460     hide: function(){
15461         if(this.fireEvent("beforehide", this) !== false){
15462             this.hidden = true;
15463             if(this.rendered){
15464                 this.onHide();
15465             }
15466             this.fireEvent("hide", this);
15467         }
15468         return this;
15469     },
15470
15471     // private
15472     onHide : function(){
15473         var ae = this.getActionEl();
15474         if(this.hideMode == 'visibility'){
15475             ae.dom.style.visibility = "hidden";
15476         }else if(this.hideMode == 'offsets'){
15477             ae.addClass('x-hidden');
15478         }else{
15479             ae.dom.style.display = "none";
15480         }
15481     },
15482
15483     /**
15484      * Convenience function to hide or show this component by boolean.
15485      * @param {Boolean} visible True to show, false to hide
15486      * @return {Roo.Component} this
15487      */
15488     setVisible: function(visible){
15489         if(visible) {
15490             this.show();
15491         }else{
15492             this.hide();
15493         }
15494         return this;
15495     },
15496
15497     /**
15498      * Returns true if this component is visible.
15499      */
15500     isVisible : function(){
15501         return this.getActionEl().isVisible();
15502     },
15503
15504     cloneConfig : function(overrides){
15505         overrides = overrides || {};
15506         var id = overrides.id || Roo.id();
15507         var cfg = Roo.applyIf(overrides, this.initialConfig);
15508         cfg.id = id; // prevent dup id
15509         return new this.constructor(cfg);
15510     }
15511 });/*
15512  * Based on:
15513  * Ext JS Library 1.1.1
15514  * Copyright(c) 2006-2007, Ext JS, LLC.
15515  *
15516  * Originally Released Under LGPL - original licence link has changed is not relivant.
15517  *
15518  * Fork - LGPL
15519  * <script type="text/javascript">
15520  */
15521
15522 /**
15523  * @class Roo.BoxComponent
15524  * @extends Roo.Component
15525  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15526  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15527  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15528  * layout containers.
15529  * @constructor
15530  * @param {Roo.Element/String/Object} config The configuration options.
15531  */
15532 Roo.BoxComponent = function(config){
15533     Roo.Component.call(this, config);
15534     this.addEvents({
15535         /**
15536          * @event resize
15537          * Fires after the component is resized.
15538              * @param {Roo.Component} this
15539              * @param {Number} adjWidth The box-adjusted width that was set
15540              * @param {Number} adjHeight The box-adjusted height that was set
15541              * @param {Number} rawWidth The width that was originally specified
15542              * @param {Number} rawHeight The height that was originally specified
15543              */
15544         resize : true,
15545         /**
15546          * @event move
15547          * Fires after the component is moved.
15548              * @param {Roo.Component} this
15549              * @param {Number} x The new x position
15550              * @param {Number} y The new y position
15551              */
15552         move : true
15553     });
15554 };
15555
15556 Roo.extend(Roo.BoxComponent, Roo.Component, {
15557     // private, set in afterRender to signify that the component has been rendered
15558     boxReady : false,
15559     // private, used to defer height settings to subclasses
15560     deferHeight: false,
15561     /** @cfg {Number} width
15562      * width (optional) size of component
15563      */
15564      /** @cfg {Number} height
15565      * height (optional) size of component
15566      */
15567      
15568     /**
15569      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15570      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15571      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15572      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15573      * @return {Roo.BoxComponent} this
15574      */
15575     setSize : function(w, h){
15576         // support for standard size objects
15577         if(typeof w == 'object'){
15578             h = w.height;
15579             w = w.width;
15580         }
15581         // not rendered
15582         if(!this.boxReady){
15583             this.width = w;
15584             this.height = h;
15585             return this;
15586         }
15587
15588         // prevent recalcs when not needed
15589         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15590             return this;
15591         }
15592         this.lastSize = {width: w, height: h};
15593
15594         var adj = this.adjustSize(w, h);
15595         var aw = adj.width, ah = adj.height;
15596         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15597             var rz = this.getResizeEl();
15598             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15599                 rz.setSize(aw, ah);
15600             }else if(!this.deferHeight && ah !== undefined){
15601                 rz.setHeight(ah);
15602             }else if(aw !== undefined){
15603                 rz.setWidth(aw);
15604             }
15605             this.onResize(aw, ah, w, h);
15606             this.fireEvent('resize', this, aw, ah, w, h);
15607         }
15608         return this;
15609     },
15610
15611     /**
15612      * Gets the current size of the component's underlying element.
15613      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15614      */
15615     getSize : function(){
15616         return this.el.getSize();
15617     },
15618
15619     /**
15620      * Gets the current XY position of the component's underlying element.
15621      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15622      * @return {Array} The XY position of the element (e.g., [100, 200])
15623      */
15624     getPosition : function(local){
15625         if(local === true){
15626             return [this.el.getLeft(true), this.el.getTop(true)];
15627         }
15628         return this.xy || this.el.getXY();
15629     },
15630
15631     /**
15632      * Gets the current box measurements of the component's underlying element.
15633      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15634      * @returns {Object} box An object in the format {x, y, width, height}
15635      */
15636     getBox : function(local){
15637         var s = this.el.getSize();
15638         if(local){
15639             s.x = this.el.getLeft(true);
15640             s.y = this.el.getTop(true);
15641         }else{
15642             var xy = this.xy || this.el.getXY();
15643             s.x = xy[0];
15644             s.y = xy[1];
15645         }
15646         return s;
15647     },
15648
15649     /**
15650      * Sets the current box measurements of the component's underlying element.
15651      * @param {Object} box An object in the format {x, y, width, height}
15652      * @returns {Roo.BoxComponent} this
15653      */
15654     updateBox : function(box){
15655         this.setSize(box.width, box.height);
15656         this.setPagePosition(box.x, box.y);
15657         return this;
15658     },
15659
15660     // protected
15661     getResizeEl : function(){
15662         return this.resizeEl || this.el;
15663     },
15664
15665     // protected
15666     getPositionEl : function(){
15667         return this.positionEl || this.el;
15668     },
15669
15670     /**
15671      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15672      * This method fires the move event.
15673      * @param {Number} left The new left
15674      * @param {Number} top The new top
15675      * @returns {Roo.BoxComponent} this
15676      */
15677     setPosition : function(x, y){
15678         this.x = x;
15679         this.y = y;
15680         if(!this.boxReady){
15681             return this;
15682         }
15683         var adj = this.adjustPosition(x, y);
15684         var ax = adj.x, ay = adj.y;
15685
15686         var el = this.getPositionEl();
15687         if(ax !== undefined || ay !== undefined){
15688             if(ax !== undefined && ay !== undefined){
15689                 el.setLeftTop(ax, ay);
15690             }else if(ax !== undefined){
15691                 el.setLeft(ax);
15692             }else if(ay !== undefined){
15693                 el.setTop(ay);
15694             }
15695             this.onPosition(ax, ay);
15696             this.fireEvent('move', this, ax, ay);
15697         }
15698         return this;
15699     },
15700
15701     /**
15702      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15703      * This method fires the move event.
15704      * @param {Number} x The new x position
15705      * @param {Number} y The new y position
15706      * @returns {Roo.BoxComponent} this
15707      */
15708     setPagePosition : function(x, y){
15709         this.pageX = x;
15710         this.pageY = y;
15711         if(!this.boxReady){
15712             return;
15713         }
15714         if(x === undefined || y === undefined){ // cannot translate undefined points
15715             return;
15716         }
15717         var p = this.el.translatePoints(x, y);
15718         this.setPosition(p.left, p.top);
15719         return this;
15720     },
15721
15722     // private
15723     onRender : function(ct, position){
15724         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15725         if(this.resizeEl){
15726             this.resizeEl = Roo.get(this.resizeEl);
15727         }
15728         if(this.positionEl){
15729             this.positionEl = Roo.get(this.positionEl);
15730         }
15731     },
15732
15733     // private
15734     afterRender : function(){
15735         Roo.BoxComponent.superclass.afterRender.call(this);
15736         this.boxReady = true;
15737         this.setSize(this.width, this.height);
15738         if(this.x || this.y){
15739             this.setPosition(this.x, this.y);
15740         }
15741         if(this.pageX || this.pageY){
15742             this.setPagePosition(this.pageX, this.pageY);
15743         }
15744     },
15745
15746     /**
15747      * Force the component's size to recalculate based on the underlying element's current height and width.
15748      * @returns {Roo.BoxComponent} this
15749      */
15750     syncSize : function(){
15751         delete this.lastSize;
15752         this.setSize(this.el.getWidth(), this.el.getHeight());
15753         return this;
15754     },
15755
15756     /**
15757      * Called after the component is resized, this method is empty by default but can be implemented by any
15758      * subclass that needs to perform custom logic after a resize occurs.
15759      * @param {Number} adjWidth The box-adjusted width that was set
15760      * @param {Number} adjHeight The box-adjusted height that was set
15761      * @param {Number} rawWidth The width that was originally specified
15762      * @param {Number} rawHeight The height that was originally specified
15763      */
15764     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15765
15766     },
15767
15768     /**
15769      * Called after the component is moved, this method is empty by default but can be implemented by any
15770      * subclass that needs to perform custom logic after a move occurs.
15771      * @param {Number} x The new x position
15772      * @param {Number} y The new y position
15773      */
15774     onPosition : function(x, y){
15775
15776     },
15777
15778     // private
15779     adjustSize : function(w, h){
15780         if(this.autoWidth){
15781             w = 'auto';
15782         }
15783         if(this.autoHeight){
15784             h = 'auto';
15785         }
15786         return {width : w, height: h};
15787     },
15788
15789     // private
15790     adjustPosition : function(x, y){
15791         return {x : x, y: y};
15792     }
15793 });/*
15794  * Original code for Roojs - LGPL
15795  * <script type="text/javascript">
15796  */
15797  
15798 /**
15799  * @class Roo.XComponent
15800  * A delayed Element creator...
15801  * Or a way to group chunks of interface together.
15802  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15803  *  used in conjunction with XComponent.build() it will create an instance of each element,
15804  *  then call addxtype() to build the User interface.
15805  * 
15806  * Mypart.xyx = new Roo.XComponent({
15807
15808     parent : 'Mypart.xyz', // empty == document.element.!!
15809     order : '001',
15810     name : 'xxxx'
15811     region : 'xxxx'
15812     disabled : function() {} 
15813      
15814     tree : function() { // return an tree of xtype declared components
15815         var MODULE = this;
15816         return 
15817         {
15818             xtype : 'NestedLayoutPanel',
15819             // technicall
15820         }
15821      ]
15822  *})
15823  *
15824  *
15825  * It can be used to build a big heiracy, with parent etc.
15826  * or you can just use this to render a single compoent to a dom element
15827  * MYPART.render(Roo.Element | String(id) | dom_element )
15828  *
15829  *
15830  * Usage patterns.
15831  *
15832  * Classic Roo
15833  *
15834  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15835  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15836  *
15837  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15838  *
15839  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15840  * - if mulitple topModules exist, the last one is defined as the top module.
15841  *
15842  * Embeded Roo
15843  * 
15844  * When the top level or multiple modules are to embedded into a existing HTML page,
15845  * the parent element can container '#id' of the element where the module will be drawn.
15846  *
15847  * Bootstrap Roo
15848  *
15849  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15850  * it relies more on a include mechanism, where sub modules are included into an outer page.
15851  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15852  * 
15853  * Bootstrap Roo Included elements
15854  *
15855  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15856  * hence confusing the component builder as it thinks there are multiple top level elements. 
15857  *
15858  * 
15859  * 
15860  * @extends Roo.util.Observable
15861  * @constructor
15862  * @param cfg {Object} configuration of component
15863  * 
15864  */
15865 Roo.XComponent = function(cfg) {
15866     Roo.apply(this, cfg);
15867     this.addEvents({ 
15868         /**
15869              * @event built
15870              * Fires when this the componnt is built
15871              * @param {Roo.XComponent} c the component
15872              */
15873         'built' : true
15874         
15875     });
15876     this.region = this.region || 'center'; // default..
15877     Roo.XComponent.register(this);
15878     this.modules = false;
15879     this.el = false; // where the layout goes..
15880     
15881     
15882 }
15883 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15884     /**
15885      * @property el
15886      * The created element (with Roo.factory())
15887      * @type {Roo.Layout}
15888      */
15889     el  : false,
15890     
15891     /**
15892      * @property el
15893      * for BC  - use el in new code
15894      * @type {Roo.Layout}
15895      */
15896     panel : false,
15897     
15898     /**
15899      * @property layout
15900      * for BC  - use el in new code
15901      * @type {Roo.Layout}
15902      */
15903     layout : false,
15904     
15905      /**
15906      * @cfg {Function|boolean} disabled
15907      * If this module is disabled by some rule, return true from the funtion
15908      */
15909     disabled : false,
15910     
15911     /**
15912      * @cfg {String} parent 
15913      * Name of parent element which it get xtype added to..
15914      */
15915     parent: false,
15916     
15917     /**
15918      * @cfg {String} order
15919      * Used to set the order in which elements are created (usefull for multiple tabs)
15920      */
15921     
15922     order : false,
15923     /**
15924      * @cfg {String} name
15925      * String to display while loading.
15926      */
15927     name : false,
15928     /**
15929      * @cfg {String} region
15930      * Region to render component to (defaults to center)
15931      */
15932     region : 'center',
15933     
15934     /**
15935      * @cfg {Array} items
15936      * A single item array - the first element is the root of the tree..
15937      * It's done this way to stay compatible with the Xtype system...
15938      */
15939     items : false,
15940     
15941     /**
15942      * @property _tree
15943      * The method that retuns the tree of parts that make up this compoennt 
15944      * @type {function}
15945      */
15946     _tree  : false,
15947     
15948      /**
15949      * render
15950      * render element to dom or tree
15951      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15952      */
15953     
15954     render : function(el)
15955     {
15956         
15957         el = el || false;
15958         var hp = this.parent ? 1 : 0;
15959         Roo.debug &&  Roo.log(this);
15960         
15961         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15962             // if parent is a '#.....' string, then let's use that..
15963             var ename = this.parent.substr(1);
15964             this.parent = false;
15965             Roo.debug && Roo.log(ename);
15966             switch (ename) {
15967                 case 'bootstrap-body' :
15968                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15969                         this.parent = { el :  new  Roo.bootstrap.Body() };
15970                         Roo.debug && Roo.log("setting el to doc body");
15971                          
15972                     } else {
15973                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15974                     }
15975                     break;
15976                 case 'bootstrap':
15977                     this.parent = { el : true};
15978                     // fall through
15979                 default:
15980                     el = Roo.get(ename);
15981                     break;
15982             }
15983                 
15984             
15985             if (!el && !this.parent) {
15986                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15987                 return;
15988             }
15989         }
15990         Roo.debug && Roo.log("EL:");
15991         Roo.debug && Roo.log(el);
15992         Roo.debug && Roo.log("this.parent.el:");
15993         Roo.debug && Roo.log(this.parent.el);
15994         
15995         var tree = this._tree ? this._tree() : this.tree();
15996
15997         // altertive root elements ??? - we need a better way to indicate these.
15998         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15999                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16000         
16001         if (!this.parent && is_alt) {
16002             //el = Roo.get(document.body);
16003             this.parent = { el : true };
16004         }
16005             
16006             
16007         
16008         if (!this.parent) {
16009             
16010             Roo.debug && Roo.log("no parent - creating one");
16011             
16012             el = el ? Roo.get(el) : false;      
16013             
16014             // it's a top level one..
16015             this.parent =  {
16016                 el : new Roo.BorderLayout(el || document.body, {
16017                 
16018                      center: {
16019                          titlebar: false,
16020                          autoScroll:false,
16021                          closeOnTab: true,
16022                          tabPosition: 'top',
16023                           //resizeTabs: true,
16024                          alwaysShowTabs: el && hp? false :  true,
16025                          hideTabs: el || !hp ? true :  false,
16026                          minTabWidth: 140
16027                      }
16028                  })
16029             }
16030         }
16031         
16032         if (!this.parent.el) {
16033                 // probably an old style ctor, which has been disabled.
16034                 return;
16035
16036         }
16037                 // The 'tree' method is  '_tree now' 
16038             
16039         tree.region = tree.region || this.region;
16040         
16041         if (this.parent.el === true) {
16042             // bootstrap... - body..
16043             this.parent.el = Roo.factory(tree);
16044         }
16045         
16046         this.el = this.parent.el.addxtype(tree);
16047         this.fireEvent('built', this);
16048         
16049         this.panel = this.el;
16050         this.layout = this.panel.layout;
16051         this.parentLayout = this.parent.layout  || false;  
16052          
16053     }
16054     
16055 });
16056
16057 Roo.apply(Roo.XComponent, {
16058     /**
16059      * @property  hideProgress
16060      * true to disable the building progress bar.. usefull on single page renders.
16061      * @type Boolean
16062      */
16063     hideProgress : false,
16064     /**
16065      * @property  buildCompleted
16066      * True when the builder has completed building the interface.
16067      * @type Boolean
16068      */
16069     buildCompleted : false,
16070      
16071     /**
16072      * @property  topModule
16073      * the upper most module - uses document.element as it's constructor.
16074      * @type Object
16075      */
16076      
16077     topModule  : false,
16078       
16079     /**
16080      * @property  modules
16081      * array of modules to be created by registration system.
16082      * @type {Array} of Roo.XComponent
16083      */
16084     
16085     modules : [],
16086     /**
16087      * @property  elmodules
16088      * array of modules to be created by which use #ID 
16089      * @type {Array} of Roo.XComponent
16090      */
16091      
16092     elmodules : [],
16093
16094      /**
16095      * @property  build_from_html
16096      * Build elements from html - used by bootstrap HTML stuff 
16097      *    - this is cleared after build is completed
16098      * @type {boolean} true  (default false)
16099      */
16100      
16101     build_from_html : false,
16102
16103     /**
16104      * Register components to be built later.
16105      *
16106      * This solves the following issues
16107      * - Building is not done on page load, but after an authentication process has occured.
16108      * - Interface elements are registered on page load
16109      * - Parent Interface elements may not be loaded before child, so this handles that..
16110      * 
16111      *
16112      * example:
16113      * 
16114      * MyApp.register({
16115           order : '000001',
16116           module : 'Pman.Tab.projectMgr',
16117           region : 'center',
16118           parent : 'Pman.layout',
16119           disabled : false,  // or use a function..
16120         })
16121      
16122      * * @param {Object} details about module
16123      */
16124     register : function(obj) {
16125                 
16126         Roo.XComponent.event.fireEvent('register', obj);
16127         switch(typeof(obj.disabled) ) {
16128                 
16129             case 'undefined':
16130                 break;
16131             
16132             case 'function':
16133                 if ( obj.disabled() ) {
16134                         return;
16135                 }
16136                 break;
16137             
16138             default:
16139                 if (obj.disabled) {
16140                         return;
16141                 }
16142                 break;
16143         }
16144                 
16145         this.modules.push(obj);
16146          
16147     },
16148     /**
16149      * convert a string to an object..
16150      * eg. 'AAA.BBB' -> finds AAA.BBB
16151
16152      */
16153     
16154     toObject : function(str)
16155     {
16156         if (!str || typeof(str) == 'object') {
16157             return str;
16158         }
16159         if (str.substring(0,1) == '#') {
16160             return str;
16161         }
16162
16163         var ar = str.split('.');
16164         var rt, o;
16165         rt = ar.shift();
16166             /** eval:var:o */
16167         try {
16168             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16169         } catch (e) {
16170             throw "Module not found : " + str;
16171         }
16172         
16173         if (o === false) {
16174             throw "Module not found : " + str;
16175         }
16176         Roo.each(ar, function(e) {
16177             if (typeof(o[e]) == 'undefined') {
16178                 throw "Module not found : " + str;
16179             }
16180             o = o[e];
16181         });
16182         
16183         return o;
16184         
16185     },
16186     
16187     
16188     /**
16189      * move modules into their correct place in the tree..
16190      * 
16191      */
16192     preBuild : function ()
16193     {
16194         var _t = this;
16195         Roo.each(this.modules , function (obj)
16196         {
16197             Roo.XComponent.event.fireEvent('beforebuild', obj);
16198             
16199             var opar = obj.parent;
16200             try { 
16201                 obj.parent = this.toObject(opar);
16202             } catch(e) {
16203                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16204                 return;
16205             }
16206             
16207             if (!obj.parent) {
16208                 Roo.debug && Roo.log("GOT top level module");
16209                 Roo.debug && Roo.log(obj);
16210                 obj.modules = new Roo.util.MixedCollection(false, 
16211                     function(o) { return o.order + '' }
16212                 );
16213                 this.topModule = obj;
16214                 return;
16215             }
16216                         // parent is a string (usually a dom element name..)
16217             if (typeof(obj.parent) == 'string') {
16218                 this.elmodules.push(obj);
16219                 return;
16220             }
16221             if (obj.parent.constructor != Roo.XComponent) {
16222                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16223             }
16224             if (!obj.parent.modules) {
16225                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16226                     function(o) { return o.order + '' }
16227                 );
16228             }
16229             if (obj.parent.disabled) {
16230                 obj.disabled = true;
16231             }
16232             obj.parent.modules.add(obj);
16233         }, this);
16234     },
16235     
16236      /**
16237      * make a list of modules to build.
16238      * @return {Array} list of modules. 
16239      */ 
16240     
16241     buildOrder : function()
16242     {
16243         var _this = this;
16244         var cmp = function(a,b) {   
16245             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16246         };
16247         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16248             throw "No top level modules to build";
16249         }
16250         
16251         // make a flat list in order of modules to build.
16252         var mods = this.topModule ? [ this.topModule ] : [];
16253                 
16254         
16255         // elmodules (is a list of DOM based modules )
16256         Roo.each(this.elmodules, function(e) {
16257             mods.push(e);
16258             if (!this.topModule &&
16259                 typeof(e.parent) == 'string' &&
16260                 e.parent.substring(0,1) == '#' &&
16261                 Roo.get(e.parent.substr(1))
16262                ) {
16263                 
16264                 _this.topModule = e;
16265             }
16266             
16267         });
16268
16269         
16270         // add modules to their parents..
16271         var addMod = function(m) {
16272             Roo.debug && Roo.log("build Order: add: " + m.name);
16273                 
16274             mods.push(m);
16275             if (m.modules && !m.disabled) {
16276                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16277                 m.modules.keySort('ASC',  cmp );
16278                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16279     
16280                 m.modules.each(addMod);
16281             } else {
16282                 Roo.debug && Roo.log("build Order: no child modules");
16283             }
16284             // not sure if this is used any more..
16285             if (m.finalize) {
16286                 m.finalize.name = m.name + " (clean up) ";
16287                 mods.push(m.finalize);
16288             }
16289             
16290         }
16291         if (this.topModule && this.topModule.modules) { 
16292             this.topModule.modules.keySort('ASC',  cmp );
16293             this.topModule.modules.each(addMod);
16294         } 
16295         return mods;
16296     },
16297     
16298      /**
16299      * Build the registered modules.
16300      * @param {Object} parent element.
16301      * @param {Function} optional method to call after module has been added.
16302      * 
16303      */ 
16304    
16305     build : function(opts) 
16306     {
16307         
16308         if (typeof(opts) != 'undefined') {
16309             Roo.apply(this,opts);
16310         }
16311         
16312         this.preBuild();
16313         var mods = this.buildOrder();
16314       
16315         //this.allmods = mods;
16316         //Roo.debug && Roo.log(mods);
16317         //return;
16318         if (!mods.length) { // should not happen
16319             throw "NO modules!!!";
16320         }
16321         
16322         
16323         var msg = "Building Interface...";
16324         // flash it up as modal - so we store the mask!?
16325         if (!this.hideProgress && Roo.MessageBox) {
16326             Roo.MessageBox.show({ title: 'loading' });
16327             Roo.MessageBox.show({
16328                title: "Please wait...",
16329                msg: msg,
16330                width:450,
16331                progress:true,
16332                closable:false,
16333                modal: false
16334               
16335             });
16336         }
16337         var total = mods.length;
16338         
16339         var _this = this;
16340         var progressRun = function() {
16341             if (!mods.length) {
16342                 Roo.debug && Roo.log('hide?');
16343                 if (!this.hideProgress && Roo.MessageBox) {
16344                     Roo.MessageBox.hide();
16345                 }
16346                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16347                 
16348                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16349                 
16350                 // THE END...
16351                 return false;   
16352             }
16353             
16354             var m = mods.shift();
16355             
16356             
16357             Roo.debug && Roo.log(m);
16358             // not sure if this is supported any more.. - modules that are are just function
16359             if (typeof(m) == 'function') { 
16360                 m.call(this);
16361                 return progressRun.defer(10, _this);
16362             } 
16363             
16364             
16365             msg = "Building Interface " + (total  - mods.length) + 
16366                     " of " + total + 
16367                     (m.name ? (' - ' + m.name) : '');
16368                         Roo.debug && Roo.log(msg);
16369             if (!this.hideProgress &&  Roo.MessageBox) { 
16370                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16371             }
16372             
16373          
16374             // is the module disabled?
16375             var disabled = (typeof(m.disabled) == 'function') ?
16376                 m.disabled.call(m.module.disabled) : m.disabled;    
16377             
16378             
16379             if (disabled) {
16380                 return progressRun(); // we do not update the display!
16381             }
16382             
16383             // now build 
16384             
16385                         
16386                         
16387             m.render();
16388             // it's 10 on top level, and 1 on others??? why...
16389             return progressRun.defer(10, _this);
16390              
16391         }
16392         progressRun.defer(1, _this);
16393      
16394         
16395         
16396     },
16397         
16398         
16399         /**
16400          * Event Object.
16401          *
16402          *
16403          */
16404         event: false, 
16405     /**
16406          * wrapper for event.on - aliased later..  
16407          * Typically use to register a event handler for register:
16408          *
16409          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16410          *
16411          */
16412     on : false
16413    
16414     
16415     
16416 });
16417
16418 Roo.XComponent.event = new Roo.util.Observable({
16419                 events : { 
16420                         /**
16421                          * @event register
16422                          * Fires when an Component is registered,
16423                          * set the disable property on the Component to stop registration.
16424                          * @param {Roo.XComponent} c the component being registerd.
16425                          * 
16426                          */
16427                         'register' : true,
16428             /**
16429                          * @event beforebuild
16430                          * Fires before each Component is built
16431                          * can be used to apply permissions.
16432                          * @param {Roo.XComponent} c the component being registerd.
16433                          * 
16434                          */
16435                         'beforebuild' : true,
16436                         /**
16437                          * @event buildcomplete
16438                          * Fires on the top level element when all elements have been built
16439                          * @param {Roo.XComponent} the top level component.
16440                          */
16441                         'buildcomplete' : true
16442                         
16443                 }
16444 });
16445
16446 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16447  /*
16448  * Based on:
16449  * Ext JS Library 1.1.1
16450  * Copyright(c) 2006-2007, Ext JS, LLC.
16451  *
16452  * Originally Released Under LGPL - original licence link has changed is not relivant.
16453  *
16454  * Fork - LGPL
16455  * <script type="text/javascript">
16456  */
16457
16458
16459
16460 /*
16461  * These classes are derivatives of the similarly named classes in the YUI Library.
16462  * The original license:
16463  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16464  * Code licensed under the BSD License:
16465  * http://developer.yahoo.net/yui/license.txt
16466  */
16467
16468 (function() {
16469
16470 var Event=Roo.EventManager;
16471 var Dom=Roo.lib.Dom;
16472
16473 /**
16474  * @class Roo.dd.DragDrop
16475  * @extends Roo.util.Observable
16476  * Defines the interface and base operation of items that that can be
16477  * dragged or can be drop targets.  It was designed to be extended, overriding
16478  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16479  * Up to three html elements can be associated with a DragDrop instance:
16480  * <ul>
16481  * <li>linked element: the element that is passed into the constructor.
16482  * This is the element which defines the boundaries for interaction with
16483  * other DragDrop objects.</li>
16484  * <li>handle element(s): The drag operation only occurs if the element that
16485  * was clicked matches a handle element.  By default this is the linked
16486  * element, but there are times that you will want only a portion of the
16487  * linked element to initiate the drag operation, and the setHandleElId()
16488  * method provides a way to define this.</li>
16489  * <li>drag element: this represents the element that would be moved along
16490  * with the cursor during a drag operation.  By default, this is the linked
16491  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16492  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16493  * </li>
16494  * </ul>
16495  * This class should not be instantiated until the onload event to ensure that
16496  * the associated elements are available.
16497  * The following would define a DragDrop obj that would interact with any
16498  * other DragDrop obj in the "group1" group:
16499  * <pre>
16500  *  dd = new Roo.dd.DragDrop("div1", "group1");
16501  * </pre>
16502  * Since none of the event handlers have been implemented, nothing would
16503  * actually happen if you were to run the code above.  Normally you would
16504  * override this class or one of the default implementations, but you can
16505  * also override the methods you want on an instance of the class...
16506  * <pre>
16507  *  dd.onDragDrop = function(e, id) {
16508  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16509  *  }
16510  * </pre>
16511  * @constructor
16512  * @param {String} id of the element that is linked to this instance
16513  * @param {String} sGroup the group of related DragDrop objects
16514  * @param {object} config an object containing configurable attributes
16515  *                Valid properties for DragDrop:
16516  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16517  */
16518 Roo.dd.DragDrop = function(id, sGroup, config) {
16519     if (id) {
16520         this.init(id, sGroup, config);
16521     }
16522     
16523 };
16524
16525 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16526
16527     /**
16528      * The id of the element associated with this object.  This is what we
16529      * refer to as the "linked element" because the size and position of
16530      * this element is used to determine when the drag and drop objects have
16531      * interacted.
16532      * @property id
16533      * @type String
16534      */
16535     id: null,
16536
16537     /**
16538      * Configuration attributes passed into the constructor
16539      * @property config
16540      * @type object
16541      */
16542     config: null,
16543
16544     /**
16545      * The id of the element that will be dragged.  By default this is same
16546      * as the linked element , but could be changed to another element. Ex:
16547      * Roo.dd.DDProxy
16548      * @property dragElId
16549      * @type String
16550      * @private
16551      */
16552     dragElId: null,
16553
16554     /**
16555      * the id of the element that initiates the drag operation.  By default
16556      * this is the linked element, but could be changed to be a child of this
16557      * element.  This lets us do things like only starting the drag when the
16558      * header element within the linked html element is clicked.
16559      * @property handleElId
16560      * @type String
16561      * @private
16562      */
16563     handleElId: null,
16564
16565     /**
16566      * An associative array of HTML tags that will be ignored if clicked.
16567      * @property invalidHandleTypes
16568      * @type {string: string}
16569      */
16570     invalidHandleTypes: null,
16571
16572     /**
16573      * An associative array of ids for elements that will be ignored if clicked
16574      * @property invalidHandleIds
16575      * @type {string: string}
16576      */
16577     invalidHandleIds: null,
16578
16579     /**
16580      * An indexted array of css class names for elements that will be ignored
16581      * if clicked.
16582      * @property invalidHandleClasses
16583      * @type string[]
16584      */
16585     invalidHandleClasses: null,
16586
16587     /**
16588      * The linked element's absolute X position at the time the drag was
16589      * started
16590      * @property startPageX
16591      * @type int
16592      * @private
16593      */
16594     startPageX: 0,
16595
16596     /**
16597      * The linked element's absolute X position at the time the drag was
16598      * started
16599      * @property startPageY
16600      * @type int
16601      * @private
16602      */
16603     startPageY: 0,
16604
16605     /**
16606      * The group defines a logical collection of DragDrop objects that are
16607      * related.  Instances only get events when interacting with other
16608      * DragDrop object in the same group.  This lets us define multiple
16609      * groups using a single DragDrop subclass if we want.
16610      * @property groups
16611      * @type {string: string}
16612      */
16613     groups: null,
16614
16615     /**
16616      * Individual drag/drop instances can be locked.  This will prevent
16617      * onmousedown start drag.
16618      * @property locked
16619      * @type boolean
16620      * @private
16621      */
16622     locked: false,
16623
16624     /**
16625      * Lock this instance
16626      * @method lock
16627      */
16628     lock: function() { this.locked = true; },
16629
16630     /**
16631      * Unlock this instace
16632      * @method unlock
16633      */
16634     unlock: function() { this.locked = false; },
16635
16636     /**
16637      * By default, all insances can be a drop target.  This can be disabled by
16638      * setting isTarget to false.
16639      * @method isTarget
16640      * @type boolean
16641      */
16642     isTarget: true,
16643
16644     /**
16645      * The padding configured for this drag and drop object for calculating
16646      * the drop zone intersection with this object.
16647      * @method padding
16648      * @type int[]
16649      */
16650     padding: null,
16651
16652     /**
16653      * Cached reference to the linked element
16654      * @property _domRef
16655      * @private
16656      */
16657     _domRef: null,
16658
16659     /**
16660      * Internal typeof flag
16661      * @property __ygDragDrop
16662      * @private
16663      */
16664     __ygDragDrop: true,
16665
16666     /**
16667      * Set to true when horizontal contraints are applied
16668      * @property constrainX
16669      * @type boolean
16670      * @private
16671      */
16672     constrainX: false,
16673
16674     /**
16675      * Set to true when vertical contraints are applied
16676      * @property constrainY
16677      * @type boolean
16678      * @private
16679      */
16680     constrainY: false,
16681
16682     /**
16683      * The left constraint
16684      * @property minX
16685      * @type int
16686      * @private
16687      */
16688     minX: 0,
16689
16690     /**
16691      * The right constraint
16692      * @property maxX
16693      * @type int
16694      * @private
16695      */
16696     maxX: 0,
16697
16698     /**
16699      * The up constraint
16700      * @property minY
16701      * @type int
16702      * @type int
16703      * @private
16704      */
16705     minY: 0,
16706
16707     /**
16708      * The down constraint
16709      * @property maxY
16710      * @type int
16711      * @private
16712      */
16713     maxY: 0,
16714
16715     /**
16716      * Maintain offsets when we resetconstraints.  Set to true when you want
16717      * the position of the element relative to its parent to stay the same
16718      * when the page changes
16719      *
16720      * @property maintainOffset
16721      * @type boolean
16722      */
16723     maintainOffset: false,
16724
16725     /**
16726      * Array of pixel locations the element will snap to if we specified a
16727      * horizontal graduation/interval.  This array is generated automatically
16728      * when you define a tick interval.
16729      * @property xTicks
16730      * @type int[]
16731      */
16732     xTicks: null,
16733
16734     /**
16735      * Array of pixel locations the element will snap to if we specified a
16736      * vertical graduation/interval.  This array is generated automatically
16737      * when you define a tick interval.
16738      * @property yTicks
16739      * @type int[]
16740      */
16741     yTicks: null,
16742
16743     /**
16744      * By default the drag and drop instance will only respond to the primary
16745      * button click (left button for a right-handed mouse).  Set to true to
16746      * allow drag and drop to start with any mouse click that is propogated
16747      * by the browser
16748      * @property primaryButtonOnly
16749      * @type boolean
16750      */
16751     primaryButtonOnly: true,
16752
16753     /**
16754      * The availabe property is false until the linked dom element is accessible.
16755      * @property available
16756      * @type boolean
16757      */
16758     available: false,
16759
16760     /**
16761      * By default, drags can only be initiated if the mousedown occurs in the
16762      * region the linked element is.  This is done in part to work around a
16763      * bug in some browsers that mis-report the mousedown if the previous
16764      * mouseup happened outside of the window.  This property is set to true
16765      * if outer handles are defined.
16766      *
16767      * @property hasOuterHandles
16768      * @type boolean
16769      * @default false
16770      */
16771     hasOuterHandles: false,
16772
16773     /**
16774      * Code that executes immediately before the startDrag event
16775      * @method b4StartDrag
16776      * @private
16777      */
16778     b4StartDrag: function(x, y) { },
16779
16780     /**
16781      * Abstract method called after a drag/drop object is clicked
16782      * and the drag or mousedown time thresholds have beeen met.
16783      * @method startDrag
16784      * @param {int} X click location
16785      * @param {int} Y click location
16786      */
16787     startDrag: function(x, y) { /* override this */ },
16788
16789     /**
16790      * Code that executes immediately before the onDrag event
16791      * @method b4Drag
16792      * @private
16793      */
16794     b4Drag: function(e) { },
16795
16796     /**
16797      * Abstract method called during the onMouseMove event while dragging an
16798      * object.
16799      * @method onDrag
16800      * @param {Event} e the mousemove event
16801      */
16802     onDrag: function(e) { /* override this */ },
16803
16804     /**
16805      * Abstract method called when this element fist begins hovering over
16806      * another DragDrop obj
16807      * @method onDragEnter
16808      * @param {Event} e the mousemove event
16809      * @param {String|DragDrop[]} id In POINT mode, the element
16810      * id this is hovering over.  In INTERSECT mode, an array of one or more
16811      * dragdrop items being hovered over.
16812      */
16813     onDragEnter: function(e, id) { /* override this */ },
16814
16815     /**
16816      * Code that executes immediately before the onDragOver event
16817      * @method b4DragOver
16818      * @private
16819      */
16820     b4DragOver: function(e) { },
16821
16822     /**
16823      * Abstract method called when this element is hovering over another
16824      * DragDrop obj
16825      * @method onDragOver
16826      * @param {Event} e the mousemove event
16827      * @param {String|DragDrop[]} id In POINT mode, the element
16828      * id this is hovering over.  In INTERSECT mode, an array of dd items
16829      * being hovered over.
16830      */
16831     onDragOver: function(e, id) { /* override this */ },
16832
16833     /**
16834      * Code that executes immediately before the onDragOut event
16835      * @method b4DragOut
16836      * @private
16837      */
16838     b4DragOut: function(e) { },
16839
16840     /**
16841      * Abstract method called when we are no longer hovering over an element
16842      * @method onDragOut
16843      * @param {Event} e the mousemove event
16844      * @param {String|DragDrop[]} id In POINT mode, the element
16845      * id this was hovering over.  In INTERSECT mode, an array of dd items
16846      * that the mouse is no longer over.
16847      */
16848     onDragOut: function(e, id) { /* override this */ },
16849
16850     /**
16851      * Code that executes immediately before the onDragDrop event
16852      * @method b4DragDrop
16853      * @private
16854      */
16855     b4DragDrop: function(e) { },
16856
16857     /**
16858      * Abstract method called when this item is dropped on another DragDrop
16859      * obj
16860      * @method onDragDrop
16861      * @param {Event} e the mouseup event
16862      * @param {String|DragDrop[]} id In POINT mode, the element
16863      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16864      * was dropped on.
16865      */
16866     onDragDrop: function(e, id) { /* override this */ },
16867
16868     /**
16869      * Abstract method called when this item is dropped on an area with no
16870      * drop target
16871      * @method onInvalidDrop
16872      * @param {Event} e the mouseup event
16873      */
16874     onInvalidDrop: function(e) { /* override this */ },
16875
16876     /**
16877      * Code that executes immediately before the endDrag event
16878      * @method b4EndDrag
16879      * @private
16880      */
16881     b4EndDrag: function(e) { },
16882
16883     /**
16884      * Fired when we are done dragging the object
16885      * @method endDrag
16886      * @param {Event} e the mouseup event
16887      */
16888     endDrag: function(e) { /* override this */ },
16889
16890     /**
16891      * Code executed immediately before the onMouseDown event
16892      * @method b4MouseDown
16893      * @param {Event} e the mousedown event
16894      * @private
16895      */
16896     b4MouseDown: function(e) {  },
16897
16898     /**
16899      * Event handler that fires when a drag/drop obj gets a mousedown
16900      * @method onMouseDown
16901      * @param {Event} e the mousedown event
16902      */
16903     onMouseDown: function(e) { /* override this */ },
16904
16905     /**
16906      * Event handler that fires when a drag/drop obj gets a mouseup
16907      * @method onMouseUp
16908      * @param {Event} e the mouseup event
16909      */
16910     onMouseUp: function(e) { /* override this */ },
16911
16912     /**
16913      * Override the onAvailable method to do what is needed after the initial
16914      * position was determined.
16915      * @method onAvailable
16916      */
16917     onAvailable: function () {
16918     },
16919
16920     /*
16921      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16922      * @type Object
16923      */
16924     defaultPadding : {left:0, right:0, top:0, bottom:0},
16925
16926     /*
16927      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16928  *
16929  * Usage:
16930  <pre><code>
16931  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16932                 { dragElId: "existingProxyDiv" });
16933  dd.startDrag = function(){
16934      this.constrainTo("parent-id");
16935  };
16936  </code></pre>
16937  * Or you can initalize it using the {@link Roo.Element} object:
16938  <pre><code>
16939  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16940      startDrag : function(){
16941          this.constrainTo("parent-id");
16942      }
16943  });
16944  </code></pre>
16945      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16946      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16947      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16948      * an object containing the sides to pad. For example: {right:10, bottom:10}
16949      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16950      */
16951     constrainTo : function(constrainTo, pad, inContent){
16952         if(typeof pad == "number"){
16953             pad = {left: pad, right:pad, top:pad, bottom:pad};
16954         }
16955         pad = pad || this.defaultPadding;
16956         var b = Roo.get(this.getEl()).getBox();
16957         var ce = Roo.get(constrainTo);
16958         var s = ce.getScroll();
16959         var c, cd = ce.dom;
16960         if(cd == document.body){
16961             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16962         }else{
16963             xy = ce.getXY();
16964             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16965         }
16966
16967
16968         var topSpace = b.y - c.y;
16969         var leftSpace = b.x - c.x;
16970
16971         this.resetConstraints();
16972         this.setXConstraint(leftSpace - (pad.left||0), // left
16973                 c.width - leftSpace - b.width - (pad.right||0) //right
16974         );
16975         this.setYConstraint(topSpace - (pad.top||0), //top
16976                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16977         );
16978     },
16979
16980     /**
16981      * Returns a reference to the linked element
16982      * @method getEl
16983      * @return {HTMLElement} the html element
16984      */
16985     getEl: function() {
16986         if (!this._domRef) {
16987             this._domRef = Roo.getDom(this.id);
16988         }
16989
16990         return this._domRef;
16991     },
16992
16993     /**
16994      * Returns a reference to the actual element to drag.  By default this is
16995      * the same as the html element, but it can be assigned to another
16996      * element. An example of this can be found in Roo.dd.DDProxy
16997      * @method getDragEl
16998      * @return {HTMLElement} the html element
16999      */
17000     getDragEl: function() {
17001         return Roo.getDom(this.dragElId);
17002     },
17003
17004     /**
17005      * Sets up the DragDrop object.  Must be called in the constructor of any
17006      * Roo.dd.DragDrop subclass
17007      * @method init
17008      * @param id the id of the linked element
17009      * @param {String} sGroup the group of related items
17010      * @param {object} config configuration attributes
17011      */
17012     init: function(id, sGroup, config) {
17013         this.initTarget(id, sGroup, config);
17014         if (!Roo.isTouch) {
17015             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17016         }
17017         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17018         // Event.on(this.id, "selectstart", Event.preventDefault);
17019     },
17020
17021     /**
17022      * Initializes Targeting functionality only... the object does not
17023      * get a mousedown handler.
17024      * @method initTarget
17025      * @param id the id of the linked element
17026      * @param {String} sGroup the group of related items
17027      * @param {object} config configuration attributes
17028      */
17029     initTarget: function(id, sGroup, config) {
17030
17031         // configuration attributes
17032         this.config = config || {};
17033
17034         // create a local reference to the drag and drop manager
17035         this.DDM = Roo.dd.DDM;
17036         // initialize the groups array
17037         this.groups = {};
17038
17039         // assume that we have an element reference instead of an id if the
17040         // parameter is not a string
17041         if (typeof id !== "string") {
17042             id = Roo.id(id);
17043         }
17044
17045         // set the id
17046         this.id = id;
17047
17048         // add to an interaction group
17049         this.addToGroup((sGroup) ? sGroup : "default");
17050
17051         // We don't want to register this as the handle with the manager
17052         // so we just set the id rather than calling the setter.
17053         this.handleElId = id;
17054
17055         // the linked element is the element that gets dragged by default
17056         this.setDragElId(id);
17057
17058         // by default, clicked anchors will not start drag operations.
17059         this.invalidHandleTypes = { A: "A" };
17060         this.invalidHandleIds = {};
17061         this.invalidHandleClasses = [];
17062
17063         this.applyConfig();
17064
17065         this.handleOnAvailable();
17066     },
17067
17068     /**
17069      * Applies the configuration parameters that were passed into the constructor.
17070      * This is supposed to happen at each level through the inheritance chain.  So
17071      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17072      * DragDrop in order to get all of the parameters that are available in
17073      * each object.
17074      * @method applyConfig
17075      */
17076     applyConfig: function() {
17077
17078         // configurable properties:
17079         //    padding, isTarget, maintainOffset, primaryButtonOnly
17080         this.padding           = this.config.padding || [0, 0, 0, 0];
17081         this.isTarget          = (this.config.isTarget !== false);
17082         this.maintainOffset    = (this.config.maintainOffset);
17083         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17084
17085     },
17086
17087     /**
17088      * Executed when the linked element is available
17089      * @method handleOnAvailable
17090      * @private
17091      */
17092     handleOnAvailable: function() {
17093         this.available = true;
17094         this.resetConstraints();
17095         this.onAvailable();
17096     },
17097
17098      /**
17099      * Configures the padding for the target zone in px.  Effectively expands
17100      * (or reduces) the virtual object size for targeting calculations.
17101      * Supports css-style shorthand; if only one parameter is passed, all sides
17102      * will have that padding, and if only two are passed, the top and bottom
17103      * will have the first param, the left and right the second.
17104      * @method setPadding
17105      * @param {int} iTop    Top pad
17106      * @param {int} iRight  Right pad
17107      * @param {int} iBot    Bot pad
17108      * @param {int} iLeft   Left pad
17109      */
17110     setPadding: function(iTop, iRight, iBot, iLeft) {
17111         // this.padding = [iLeft, iRight, iTop, iBot];
17112         if (!iRight && 0 !== iRight) {
17113             this.padding = [iTop, iTop, iTop, iTop];
17114         } else if (!iBot && 0 !== iBot) {
17115             this.padding = [iTop, iRight, iTop, iRight];
17116         } else {
17117             this.padding = [iTop, iRight, iBot, iLeft];
17118         }
17119     },
17120
17121     /**
17122      * Stores the initial placement of the linked element.
17123      * @method setInitialPosition
17124      * @param {int} diffX   the X offset, default 0
17125      * @param {int} diffY   the Y offset, default 0
17126      */
17127     setInitPosition: function(diffX, diffY) {
17128         var el = this.getEl();
17129
17130         if (!this.DDM.verifyEl(el)) {
17131             return;
17132         }
17133
17134         var dx = diffX || 0;
17135         var dy = diffY || 0;
17136
17137         var p = Dom.getXY( el );
17138
17139         this.initPageX = p[0] - dx;
17140         this.initPageY = p[1] - dy;
17141
17142         this.lastPageX = p[0];
17143         this.lastPageY = p[1];
17144
17145
17146         this.setStartPosition(p);
17147     },
17148
17149     /**
17150      * Sets the start position of the element.  This is set when the obj
17151      * is initialized, the reset when a drag is started.
17152      * @method setStartPosition
17153      * @param pos current position (from previous lookup)
17154      * @private
17155      */
17156     setStartPosition: function(pos) {
17157         var p = pos || Dom.getXY( this.getEl() );
17158         this.deltaSetXY = null;
17159
17160         this.startPageX = p[0];
17161         this.startPageY = p[1];
17162     },
17163
17164     /**
17165      * Add this instance to a group of related drag/drop objects.  All
17166      * instances belong to at least one group, and can belong to as many
17167      * groups as needed.
17168      * @method addToGroup
17169      * @param sGroup {string} the name of the group
17170      */
17171     addToGroup: function(sGroup) {
17172         this.groups[sGroup] = true;
17173         this.DDM.regDragDrop(this, sGroup);
17174     },
17175
17176     /**
17177      * Remove's this instance from the supplied interaction group
17178      * @method removeFromGroup
17179      * @param {string}  sGroup  The group to drop
17180      */
17181     removeFromGroup: function(sGroup) {
17182         if (this.groups[sGroup]) {
17183             delete this.groups[sGroup];
17184         }
17185
17186         this.DDM.removeDDFromGroup(this, sGroup);
17187     },
17188
17189     /**
17190      * Allows you to specify that an element other than the linked element
17191      * will be moved with the cursor during a drag
17192      * @method setDragElId
17193      * @param id {string} the id of the element that will be used to initiate the drag
17194      */
17195     setDragElId: function(id) {
17196         this.dragElId = id;
17197     },
17198
17199     /**
17200      * Allows you to specify a child of the linked element that should be
17201      * used to initiate the drag operation.  An example of this would be if
17202      * you have a content div with text and links.  Clicking anywhere in the
17203      * content area would normally start the drag operation.  Use this method
17204      * to specify that an element inside of the content div is the element
17205      * that starts the drag operation.
17206      * @method setHandleElId
17207      * @param id {string} the id of the element that will be used to
17208      * initiate the drag.
17209      */
17210     setHandleElId: function(id) {
17211         if (typeof id !== "string") {
17212             id = Roo.id(id);
17213         }
17214         this.handleElId = id;
17215         this.DDM.regHandle(this.id, id);
17216     },
17217
17218     /**
17219      * Allows you to set an element outside of the linked element as a drag
17220      * handle
17221      * @method setOuterHandleElId
17222      * @param id the id of the element that will be used to initiate the drag
17223      */
17224     setOuterHandleElId: function(id) {
17225         if (typeof id !== "string") {
17226             id = Roo.id(id);
17227         }
17228         Event.on(id, "mousedown",
17229                 this.handleMouseDown, this);
17230         this.setHandleElId(id);
17231
17232         this.hasOuterHandles = true;
17233     },
17234
17235     /**
17236      * Remove all drag and drop hooks for this element
17237      * @method unreg
17238      */
17239     unreg: function() {
17240         Event.un(this.id, "mousedown",
17241                 this.handleMouseDown);
17242         Event.un(this.id, "touchstart",
17243                 this.handleMouseDown);
17244         this._domRef = null;
17245         this.DDM._remove(this);
17246     },
17247
17248     destroy : function(){
17249         this.unreg();
17250     },
17251
17252     /**
17253      * Returns true if this instance is locked, or the drag drop mgr is locked
17254      * (meaning that all drag/drop is disabled on the page.)
17255      * @method isLocked
17256      * @return {boolean} true if this obj or all drag/drop is locked, else
17257      * false
17258      */
17259     isLocked: function() {
17260         return (this.DDM.isLocked() || this.locked);
17261     },
17262
17263     /**
17264      * Fired when this object is clicked
17265      * @method handleMouseDown
17266      * @param {Event} e
17267      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17268      * @private
17269      */
17270     handleMouseDown: function(e, oDD){
17271      
17272         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17273             //Roo.log('not touch/ button !=0');
17274             return;
17275         }
17276         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17277             return; // double touch..
17278         }
17279         
17280
17281         if (this.isLocked()) {
17282             //Roo.log('locked');
17283             return;
17284         }
17285
17286         this.DDM.refreshCache(this.groups);
17287 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17288         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17289         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17290             //Roo.log('no outer handes or not over target');
17291                 // do nothing.
17292         } else {
17293 //            Roo.log('check validator');
17294             if (this.clickValidator(e)) {
17295 //                Roo.log('validate success');
17296                 // set the initial element position
17297                 this.setStartPosition();
17298
17299
17300                 this.b4MouseDown(e);
17301                 this.onMouseDown(e);
17302
17303                 this.DDM.handleMouseDown(e, this);
17304
17305                 this.DDM.stopEvent(e);
17306             } else {
17307
17308
17309             }
17310         }
17311     },
17312
17313     clickValidator: function(e) {
17314         var target = e.getTarget();
17315         return ( this.isValidHandleChild(target) &&
17316                     (this.id == this.handleElId ||
17317                         this.DDM.handleWasClicked(target, this.id)) );
17318     },
17319
17320     /**
17321      * Allows you to specify a tag name that should not start a drag operation
17322      * when clicked.  This is designed to facilitate embedding links within a
17323      * drag handle that do something other than start the drag.
17324      * @method addInvalidHandleType
17325      * @param {string} tagName the type of element to exclude
17326      */
17327     addInvalidHandleType: function(tagName) {
17328         var type = tagName.toUpperCase();
17329         this.invalidHandleTypes[type] = type;
17330     },
17331
17332     /**
17333      * Lets you to specify an element id for a child of a drag handle
17334      * that should not initiate a drag
17335      * @method addInvalidHandleId
17336      * @param {string} id the element id of the element you wish to ignore
17337      */
17338     addInvalidHandleId: function(id) {
17339         if (typeof id !== "string") {
17340             id = Roo.id(id);
17341         }
17342         this.invalidHandleIds[id] = id;
17343     },
17344
17345     /**
17346      * Lets you specify a css class of elements that will not initiate a drag
17347      * @method addInvalidHandleClass
17348      * @param {string} cssClass the class of the elements you wish to ignore
17349      */
17350     addInvalidHandleClass: function(cssClass) {
17351         this.invalidHandleClasses.push(cssClass);
17352     },
17353
17354     /**
17355      * Unsets an excluded tag name set by addInvalidHandleType
17356      * @method removeInvalidHandleType
17357      * @param {string} tagName the type of element to unexclude
17358      */
17359     removeInvalidHandleType: function(tagName) {
17360         var type = tagName.toUpperCase();
17361         // this.invalidHandleTypes[type] = null;
17362         delete this.invalidHandleTypes[type];
17363     },
17364
17365     /**
17366      * Unsets an invalid handle id
17367      * @method removeInvalidHandleId
17368      * @param {string} id the id of the element to re-enable
17369      */
17370     removeInvalidHandleId: function(id) {
17371         if (typeof id !== "string") {
17372             id = Roo.id(id);
17373         }
17374         delete this.invalidHandleIds[id];
17375     },
17376
17377     /**
17378      * Unsets an invalid css class
17379      * @method removeInvalidHandleClass
17380      * @param {string} cssClass the class of the element(s) you wish to
17381      * re-enable
17382      */
17383     removeInvalidHandleClass: function(cssClass) {
17384         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17385             if (this.invalidHandleClasses[i] == cssClass) {
17386                 delete this.invalidHandleClasses[i];
17387             }
17388         }
17389     },
17390
17391     /**
17392      * Checks the tag exclusion list to see if this click should be ignored
17393      * @method isValidHandleChild
17394      * @param {HTMLElement} node the HTMLElement to evaluate
17395      * @return {boolean} true if this is a valid tag type, false if not
17396      */
17397     isValidHandleChild: function(node) {
17398
17399         var valid = true;
17400         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17401         var nodeName;
17402         try {
17403             nodeName = node.nodeName.toUpperCase();
17404         } catch(e) {
17405             nodeName = node.nodeName;
17406         }
17407         valid = valid && !this.invalidHandleTypes[nodeName];
17408         valid = valid && !this.invalidHandleIds[node.id];
17409
17410         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17411             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17412         }
17413
17414
17415         return valid;
17416
17417     },
17418
17419     /**
17420      * Create the array of horizontal tick marks if an interval was specified
17421      * in setXConstraint().
17422      * @method setXTicks
17423      * @private
17424      */
17425     setXTicks: function(iStartX, iTickSize) {
17426         this.xTicks = [];
17427         this.xTickSize = iTickSize;
17428
17429         var tickMap = {};
17430
17431         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17432             if (!tickMap[i]) {
17433                 this.xTicks[this.xTicks.length] = i;
17434                 tickMap[i] = true;
17435             }
17436         }
17437
17438         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17439             if (!tickMap[i]) {
17440                 this.xTicks[this.xTicks.length] = i;
17441                 tickMap[i] = true;
17442             }
17443         }
17444
17445         this.xTicks.sort(this.DDM.numericSort) ;
17446     },
17447
17448     /**
17449      * Create the array of vertical tick marks if an interval was specified in
17450      * setYConstraint().
17451      * @method setYTicks
17452      * @private
17453      */
17454     setYTicks: function(iStartY, iTickSize) {
17455         this.yTicks = [];
17456         this.yTickSize = iTickSize;
17457
17458         var tickMap = {};
17459
17460         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17461             if (!tickMap[i]) {
17462                 this.yTicks[this.yTicks.length] = i;
17463                 tickMap[i] = true;
17464             }
17465         }
17466
17467         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17468             if (!tickMap[i]) {
17469                 this.yTicks[this.yTicks.length] = i;
17470                 tickMap[i] = true;
17471             }
17472         }
17473
17474         this.yTicks.sort(this.DDM.numericSort) ;
17475     },
17476
17477     /**
17478      * By default, the element can be dragged any place on the screen.  Use
17479      * this method to limit the horizontal travel of the element.  Pass in
17480      * 0,0 for the parameters if you want to lock the drag to the y axis.
17481      * @method setXConstraint
17482      * @param {int} iLeft the number of pixels the element can move to the left
17483      * @param {int} iRight the number of pixels the element can move to the
17484      * right
17485      * @param {int} iTickSize optional parameter for specifying that the
17486      * element
17487      * should move iTickSize pixels at a time.
17488      */
17489     setXConstraint: function(iLeft, iRight, iTickSize) {
17490         this.leftConstraint = iLeft;
17491         this.rightConstraint = iRight;
17492
17493         this.minX = this.initPageX - iLeft;
17494         this.maxX = this.initPageX + iRight;
17495         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17496
17497         this.constrainX = true;
17498     },
17499
17500     /**
17501      * Clears any constraints applied to this instance.  Also clears ticks
17502      * since they can't exist independent of a constraint at this time.
17503      * @method clearConstraints
17504      */
17505     clearConstraints: function() {
17506         this.constrainX = false;
17507         this.constrainY = false;
17508         this.clearTicks();
17509     },
17510
17511     /**
17512      * Clears any tick interval defined for this instance
17513      * @method clearTicks
17514      */
17515     clearTicks: function() {
17516         this.xTicks = null;
17517         this.yTicks = null;
17518         this.xTickSize = 0;
17519         this.yTickSize = 0;
17520     },
17521
17522     /**
17523      * By default, the element can be dragged any place on the screen.  Set
17524      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17525      * parameters if you want to lock the drag to the x axis.
17526      * @method setYConstraint
17527      * @param {int} iUp the number of pixels the element can move up
17528      * @param {int} iDown the number of pixels the element can move down
17529      * @param {int} iTickSize optional parameter for specifying that the
17530      * element should move iTickSize pixels at a time.
17531      */
17532     setYConstraint: function(iUp, iDown, iTickSize) {
17533         this.topConstraint = iUp;
17534         this.bottomConstraint = iDown;
17535
17536         this.minY = this.initPageY - iUp;
17537         this.maxY = this.initPageY + iDown;
17538         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17539
17540         this.constrainY = true;
17541
17542     },
17543
17544     /**
17545      * resetConstraints must be called if you manually reposition a dd element.
17546      * @method resetConstraints
17547      * @param {boolean} maintainOffset
17548      */
17549     resetConstraints: function() {
17550
17551
17552         // Maintain offsets if necessary
17553         if (this.initPageX || this.initPageX === 0) {
17554             // figure out how much this thing has moved
17555             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17556             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17557
17558             this.setInitPosition(dx, dy);
17559
17560         // This is the first time we have detected the element's position
17561         } else {
17562             this.setInitPosition();
17563         }
17564
17565         if (this.constrainX) {
17566             this.setXConstraint( this.leftConstraint,
17567                                  this.rightConstraint,
17568                                  this.xTickSize        );
17569         }
17570
17571         if (this.constrainY) {
17572             this.setYConstraint( this.topConstraint,
17573                                  this.bottomConstraint,
17574                                  this.yTickSize         );
17575         }
17576     },
17577
17578     /**
17579      * Normally the drag element is moved pixel by pixel, but we can specify
17580      * that it move a number of pixels at a time.  This method resolves the
17581      * location when we have it set up like this.
17582      * @method getTick
17583      * @param {int} val where we want to place the object
17584      * @param {int[]} tickArray sorted array of valid points
17585      * @return {int} the closest tick
17586      * @private
17587      */
17588     getTick: function(val, tickArray) {
17589
17590         if (!tickArray) {
17591             // If tick interval is not defined, it is effectively 1 pixel,
17592             // so we return the value passed to us.
17593             return val;
17594         } else if (tickArray[0] >= val) {
17595             // The value is lower than the first tick, so we return the first
17596             // tick.
17597             return tickArray[0];
17598         } else {
17599             for (var i=0, len=tickArray.length; i<len; ++i) {
17600                 var next = i + 1;
17601                 if (tickArray[next] && tickArray[next] >= val) {
17602                     var diff1 = val - tickArray[i];
17603                     var diff2 = tickArray[next] - val;
17604                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17605                 }
17606             }
17607
17608             // The value is larger than the last tick, so we return the last
17609             // tick.
17610             return tickArray[tickArray.length - 1];
17611         }
17612     },
17613
17614     /**
17615      * toString method
17616      * @method toString
17617      * @return {string} string representation of the dd obj
17618      */
17619     toString: function() {
17620         return ("DragDrop " + this.id);
17621     }
17622
17623 });
17624
17625 })();
17626 /*
17627  * Based on:
17628  * Ext JS Library 1.1.1
17629  * Copyright(c) 2006-2007, Ext JS, LLC.
17630  *
17631  * Originally Released Under LGPL - original licence link has changed is not relivant.
17632  *
17633  * Fork - LGPL
17634  * <script type="text/javascript">
17635  */
17636
17637
17638 /**
17639  * The drag and drop utility provides a framework for building drag and drop
17640  * applications.  In addition to enabling drag and drop for specific elements,
17641  * the drag and drop elements are tracked by the manager class, and the
17642  * interactions between the various elements are tracked during the drag and
17643  * the implementing code is notified about these important moments.
17644  */
17645
17646 // Only load the library once.  Rewriting the manager class would orphan
17647 // existing drag and drop instances.
17648 if (!Roo.dd.DragDropMgr) {
17649
17650 /**
17651  * @class Roo.dd.DragDropMgr
17652  * DragDropMgr is a singleton that tracks the element interaction for
17653  * all DragDrop items in the window.  Generally, you will not call
17654  * this class directly, but it does have helper methods that could
17655  * be useful in your DragDrop implementations.
17656  * @singleton
17657  */
17658 Roo.dd.DragDropMgr = function() {
17659
17660     var Event = Roo.EventManager;
17661
17662     return {
17663
17664         /**
17665          * Two dimensional Array of registered DragDrop objects.  The first
17666          * dimension is the DragDrop item group, the second the DragDrop
17667          * object.
17668          * @property ids
17669          * @type {string: string}
17670          * @private
17671          * @static
17672          */
17673         ids: {},
17674
17675         /**
17676          * Array of element ids defined as drag handles.  Used to determine
17677          * if the element that generated the mousedown event is actually the
17678          * handle and not the html element itself.
17679          * @property handleIds
17680          * @type {string: string}
17681          * @private
17682          * @static
17683          */
17684         handleIds: {},
17685
17686         /**
17687          * the DragDrop object that is currently being dragged
17688          * @property dragCurrent
17689          * @type DragDrop
17690          * @private
17691          * @static
17692          **/
17693         dragCurrent: null,
17694
17695         /**
17696          * the DragDrop object(s) that are being hovered over
17697          * @property dragOvers
17698          * @type Array
17699          * @private
17700          * @static
17701          */
17702         dragOvers: {},
17703
17704         /**
17705          * the X distance between the cursor and the object being dragged
17706          * @property deltaX
17707          * @type int
17708          * @private
17709          * @static
17710          */
17711         deltaX: 0,
17712
17713         /**
17714          * the Y distance between the cursor and the object being dragged
17715          * @property deltaY
17716          * @type int
17717          * @private
17718          * @static
17719          */
17720         deltaY: 0,
17721
17722         /**
17723          * Flag to determine if we should prevent the default behavior of the
17724          * events we define. By default this is true, but this can be set to
17725          * false if you need the default behavior (not recommended)
17726          * @property preventDefault
17727          * @type boolean
17728          * @static
17729          */
17730         preventDefault: true,
17731
17732         /**
17733          * Flag to determine if we should stop the propagation of the events
17734          * we generate. This is true by default but you may want to set it to
17735          * false if the html element contains other features that require the
17736          * mouse click.
17737          * @property stopPropagation
17738          * @type boolean
17739          * @static
17740          */
17741         stopPropagation: true,
17742
17743         /**
17744          * Internal flag that is set to true when drag and drop has been
17745          * intialized
17746          * @property initialized
17747          * @private
17748          * @static
17749          */
17750         initalized: false,
17751
17752         /**
17753          * All drag and drop can be disabled.
17754          * @property locked
17755          * @private
17756          * @static
17757          */
17758         locked: false,
17759
17760         /**
17761          * Called the first time an element is registered.
17762          * @method init
17763          * @private
17764          * @static
17765          */
17766         init: function() {
17767             this.initialized = true;
17768         },
17769
17770         /**
17771          * In point mode, drag and drop interaction is defined by the
17772          * location of the cursor during the drag/drop
17773          * @property POINT
17774          * @type int
17775          * @static
17776          */
17777         POINT: 0,
17778
17779         /**
17780          * In intersect mode, drag and drop interactio nis defined by the
17781          * overlap of two or more drag and drop objects.
17782          * @property INTERSECT
17783          * @type int
17784          * @static
17785          */
17786         INTERSECT: 1,
17787
17788         /**
17789          * The current drag and drop mode.  Default: POINT
17790          * @property mode
17791          * @type int
17792          * @static
17793          */
17794         mode: 0,
17795
17796         /**
17797          * Runs method on all drag and drop objects
17798          * @method _execOnAll
17799          * @private
17800          * @static
17801          */
17802         _execOnAll: function(sMethod, args) {
17803             for (var i in this.ids) {
17804                 for (var j in this.ids[i]) {
17805                     var oDD = this.ids[i][j];
17806                     if (! this.isTypeOfDD(oDD)) {
17807                         continue;
17808                     }
17809                     oDD[sMethod].apply(oDD, args);
17810                 }
17811             }
17812         },
17813
17814         /**
17815          * Drag and drop initialization.  Sets up the global event handlers
17816          * @method _onLoad
17817          * @private
17818          * @static
17819          */
17820         _onLoad: function() {
17821
17822             this.init();
17823
17824             if (!Roo.isTouch) {
17825                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17826                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17827             }
17828             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17829             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17830             
17831             Event.on(window,   "unload",    this._onUnload, this, true);
17832             Event.on(window,   "resize",    this._onResize, this, true);
17833             // Event.on(window,   "mouseout",    this._test);
17834
17835         },
17836
17837         /**
17838          * Reset constraints on all drag and drop objs
17839          * @method _onResize
17840          * @private
17841          * @static
17842          */
17843         _onResize: function(e) {
17844             this._execOnAll("resetConstraints", []);
17845         },
17846
17847         /**
17848          * Lock all drag and drop functionality
17849          * @method lock
17850          * @static
17851          */
17852         lock: function() { this.locked = true; },
17853
17854         /**
17855          * Unlock all drag and drop functionality
17856          * @method unlock
17857          * @static
17858          */
17859         unlock: function() { this.locked = false; },
17860
17861         /**
17862          * Is drag and drop locked?
17863          * @method isLocked
17864          * @return {boolean} True if drag and drop is locked, false otherwise.
17865          * @static
17866          */
17867         isLocked: function() { return this.locked; },
17868
17869         /**
17870          * Location cache that is set for all drag drop objects when a drag is
17871          * initiated, cleared when the drag is finished.
17872          * @property locationCache
17873          * @private
17874          * @static
17875          */
17876         locationCache: {},
17877
17878         /**
17879          * Set useCache to false if you want to force object the lookup of each
17880          * drag and drop linked element constantly during a drag.
17881          * @property useCache
17882          * @type boolean
17883          * @static
17884          */
17885         useCache: true,
17886
17887         /**
17888          * The number of pixels that the mouse needs to move after the
17889          * mousedown before the drag is initiated.  Default=3;
17890          * @property clickPixelThresh
17891          * @type int
17892          * @static
17893          */
17894         clickPixelThresh: 3,
17895
17896         /**
17897          * The number of milliseconds after the mousedown event to initiate the
17898          * drag if we don't get a mouseup event. Default=1000
17899          * @property clickTimeThresh
17900          * @type int
17901          * @static
17902          */
17903         clickTimeThresh: 350,
17904
17905         /**
17906          * Flag that indicates that either the drag pixel threshold or the
17907          * mousdown time threshold has been met
17908          * @property dragThreshMet
17909          * @type boolean
17910          * @private
17911          * @static
17912          */
17913         dragThreshMet: false,
17914
17915         /**
17916          * Timeout used for the click time threshold
17917          * @property clickTimeout
17918          * @type Object
17919          * @private
17920          * @static
17921          */
17922         clickTimeout: null,
17923
17924         /**
17925          * The X position of the mousedown event stored for later use when a
17926          * drag threshold is met.
17927          * @property startX
17928          * @type int
17929          * @private
17930          * @static
17931          */
17932         startX: 0,
17933
17934         /**
17935          * The Y position of the mousedown event stored for later use when a
17936          * drag threshold is met.
17937          * @property startY
17938          * @type int
17939          * @private
17940          * @static
17941          */
17942         startY: 0,
17943
17944         /**
17945          * Each DragDrop instance must be registered with the DragDropMgr.
17946          * This is executed in DragDrop.init()
17947          * @method regDragDrop
17948          * @param {DragDrop} oDD the DragDrop object to register
17949          * @param {String} sGroup the name of the group this element belongs to
17950          * @static
17951          */
17952         regDragDrop: function(oDD, sGroup) {
17953             if (!this.initialized) { this.init(); }
17954
17955             if (!this.ids[sGroup]) {
17956                 this.ids[sGroup] = {};
17957             }
17958             this.ids[sGroup][oDD.id] = oDD;
17959         },
17960
17961         /**
17962          * Removes the supplied dd instance from the supplied group. Executed
17963          * by DragDrop.removeFromGroup, so don't call this function directly.
17964          * @method removeDDFromGroup
17965          * @private
17966          * @static
17967          */
17968         removeDDFromGroup: function(oDD, sGroup) {
17969             if (!this.ids[sGroup]) {
17970                 this.ids[sGroup] = {};
17971             }
17972
17973             var obj = this.ids[sGroup];
17974             if (obj && obj[oDD.id]) {
17975                 delete obj[oDD.id];
17976             }
17977         },
17978
17979         /**
17980          * Unregisters a drag and drop item.  This is executed in
17981          * DragDrop.unreg, use that method instead of calling this directly.
17982          * @method _remove
17983          * @private
17984          * @static
17985          */
17986         _remove: function(oDD) {
17987             for (var g in oDD.groups) {
17988                 if (g && this.ids[g][oDD.id]) {
17989                     delete this.ids[g][oDD.id];
17990                 }
17991             }
17992             delete this.handleIds[oDD.id];
17993         },
17994
17995         /**
17996          * Each DragDrop handle element must be registered.  This is done
17997          * automatically when executing DragDrop.setHandleElId()
17998          * @method regHandle
17999          * @param {String} sDDId the DragDrop id this element is a handle for
18000          * @param {String} sHandleId the id of the element that is the drag
18001          * handle
18002          * @static
18003          */
18004         regHandle: function(sDDId, sHandleId) {
18005             if (!this.handleIds[sDDId]) {
18006                 this.handleIds[sDDId] = {};
18007             }
18008             this.handleIds[sDDId][sHandleId] = sHandleId;
18009         },
18010
18011         /**
18012          * Utility function to determine if a given element has been
18013          * registered as a drag drop item.
18014          * @method isDragDrop
18015          * @param {String} id the element id to check
18016          * @return {boolean} true if this element is a DragDrop item,
18017          * false otherwise
18018          * @static
18019          */
18020         isDragDrop: function(id) {
18021             return ( this.getDDById(id) ) ? true : false;
18022         },
18023
18024         /**
18025          * Returns the drag and drop instances that are in all groups the
18026          * passed in instance belongs to.
18027          * @method getRelated
18028          * @param {DragDrop} p_oDD the obj to get related data for
18029          * @param {boolean} bTargetsOnly if true, only return targetable objs
18030          * @return {DragDrop[]} the related instances
18031          * @static
18032          */
18033         getRelated: function(p_oDD, bTargetsOnly) {
18034             var oDDs = [];
18035             for (var i in p_oDD.groups) {
18036                 for (j in this.ids[i]) {
18037                     var dd = this.ids[i][j];
18038                     if (! this.isTypeOfDD(dd)) {
18039                         continue;
18040                     }
18041                     if (!bTargetsOnly || dd.isTarget) {
18042                         oDDs[oDDs.length] = dd;
18043                     }
18044                 }
18045             }
18046
18047             return oDDs;
18048         },
18049
18050         /**
18051          * Returns true if the specified dd target is a legal target for
18052          * the specifice drag obj
18053          * @method isLegalTarget
18054          * @param {DragDrop} the drag obj
18055          * @param {DragDrop} the target
18056          * @return {boolean} true if the target is a legal target for the
18057          * dd obj
18058          * @static
18059          */
18060         isLegalTarget: function (oDD, oTargetDD) {
18061             var targets = this.getRelated(oDD, true);
18062             for (var i=0, len=targets.length;i<len;++i) {
18063                 if (targets[i].id == oTargetDD.id) {
18064                     return true;
18065                 }
18066             }
18067
18068             return false;
18069         },
18070
18071         /**
18072          * My goal is to be able to transparently determine if an object is
18073          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18074          * returns "object", oDD.constructor.toString() always returns
18075          * "DragDrop" and not the name of the subclass.  So for now it just
18076          * evaluates a well-known variable in DragDrop.
18077          * @method isTypeOfDD
18078          * @param {Object} the object to evaluate
18079          * @return {boolean} true if typeof oDD = DragDrop
18080          * @static
18081          */
18082         isTypeOfDD: function (oDD) {
18083             return (oDD && oDD.__ygDragDrop);
18084         },
18085
18086         /**
18087          * Utility function to determine if a given element has been
18088          * registered as a drag drop handle for the given Drag Drop object.
18089          * @method isHandle
18090          * @param {String} id the element id to check
18091          * @return {boolean} true if this element is a DragDrop handle, false
18092          * otherwise
18093          * @static
18094          */
18095         isHandle: function(sDDId, sHandleId) {
18096             return ( this.handleIds[sDDId] &&
18097                             this.handleIds[sDDId][sHandleId] );
18098         },
18099
18100         /**
18101          * Returns the DragDrop instance for a given id
18102          * @method getDDById
18103          * @param {String} id the id of the DragDrop object
18104          * @return {DragDrop} the drag drop object, null if it is not found
18105          * @static
18106          */
18107         getDDById: function(id) {
18108             for (var i in this.ids) {
18109                 if (this.ids[i][id]) {
18110                     return this.ids[i][id];
18111                 }
18112             }
18113             return null;
18114         },
18115
18116         /**
18117          * Fired after a registered DragDrop object gets the mousedown event.
18118          * Sets up the events required to track the object being dragged
18119          * @method handleMouseDown
18120          * @param {Event} e the event
18121          * @param oDD the DragDrop object being dragged
18122          * @private
18123          * @static
18124          */
18125         handleMouseDown: function(e, oDD) {
18126             if(Roo.QuickTips){
18127                 Roo.QuickTips.disable();
18128             }
18129             this.currentTarget = e.getTarget();
18130
18131             this.dragCurrent = oDD;
18132
18133             var el = oDD.getEl();
18134
18135             // track start position
18136             this.startX = e.getPageX();
18137             this.startY = e.getPageY();
18138
18139             this.deltaX = this.startX - el.offsetLeft;
18140             this.deltaY = this.startY - el.offsetTop;
18141
18142             this.dragThreshMet = false;
18143
18144             this.clickTimeout = setTimeout(
18145                     function() {
18146                         var DDM = Roo.dd.DDM;
18147                         DDM.startDrag(DDM.startX, DDM.startY);
18148                     },
18149                     this.clickTimeThresh );
18150         },
18151
18152         /**
18153          * Fired when either the drag pixel threshol or the mousedown hold
18154          * time threshold has been met.
18155          * @method startDrag
18156          * @param x {int} the X position of the original mousedown
18157          * @param y {int} the Y position of the original mousedown
18158          * @static
18159          */
18160         startDrag: function(x, y) {
18161             clearTimeout(this.clickTimeout);
18162             if (this.dragCurrent) {
18163                 this.dragCurrent.b4StartDrag(x, y);
18164                 this.dragCurrent.startDrag(x, y);
18165             }
18166             this.dragThreshMet = true;
18167         },
18168
18169         /**
18170          * Internal function to handle the mouseup event.  Will be invoked
18171          * from the context of the document.
18172          * @method handleMouseUp
18173          * @param {Event} e the event
18174          * @private
18175          * @static
18176          */
18177         handleMouseUp: function(e) {
18178
18179             if(Roo.QuickTips){
18180                 Roo.QuickTips.enable();
18181             }
18182             if (! this.dragCurrent) {
18183                 return;
18184             }
18185
18186             clearTimeout(this.clickTimeout);
18187
18188             if (this.dragThreshMet) {
18189                 this.fireEvents(e, true);
18190             } else {
18191             }
18192
18193             this.stopDrag(e);
18194
18195             this.stopEvent(e);
18196         },
18197
18198         /**
18199          * Utility to stop event propagation and event default, if these
18200          * features are turned on.
18201          * @method stopEvent
18202          * @param {Event} e the event as returned by this.getEvent()
18203          * @static
18204          */
18205         stopEvent: function(e){
18206             if(this.stopPropagation) {
18207                 e.stopPropagation();
18208             }
18209
18210             if (this.preventDefault) {
18211                 e.preventDefault();
18212             }
18213         },
18214
18215         /**
18216          * Internal function to clean up event handlers after the drag
18217          * operation is complete
18218          * @method stopDrag
18219          * @param {Event} e the event
18220          * @private
18221          * @static
18222          */
18223         stopDrag: function(e) {
18224             // Fire the drag end event for the item that was dragged
18225             if (this.dragCurrent) {
18226                 if (this.dragThreshMet) {
18227                     this.dragCurrent.b4EndDrag(e);
18228                     this.dragCurrent.endDrag(e);
18229                 }
18230
18231                 this.dragCurrent.onMouseUp(e);
18232             }
18233
18234             this.dragCurrent = null;
18235             this.dragOvers = {};
18236         },
18237
18238         /**
18239          * Internal function to handle the mousemove event.  Will be invoked
18240          * from the context of the html element.
18241          *
18242          * @TODO figure out what we can do about mouse events lost when the
18243          * user drags objects beyond the window boundary.  Currently we can
18244          * detect this in internet explorer by verifying that the mouse is
18245          * down during the mousemove event.  Firefox doesn't give us the
18246          * button state on the mousemove event.
18247          * @method handleMouseMove
18248          * @param {Event} e the event
18249          * @private
18250          * @static
18251          */
18252         handleMouseMove: function(e) {
18253             if (! this.dragCurrent) {
18254                 return true;
18255             }
18256
18257             // var button = e.which || e.button;
18258
18259             // check for IE mouseup outside of page boundary
18260             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18261                 this.stopEvent(e);
18262                 return this.handleMouseUp(e);
18263             }
18264
18265             if (!this.dragThreshMet) {
18266                 var diffX = Math.abs(this.startX - e.getPageX());
18267                 var diffY = Math.abs(this.startY - e.getPageY());
18268                 if (diffX > this.clickPixelThresh ||
18269                             diffY > this.clickPixelThresh) {
18270                     this.startDrag(this.startX, this.startY);
18271                 }
18272             }
18273
18274             if (this.dragThreshMet) {
18275                 this.dragCurrent.b4Drag(e);
18276                 this.dragCurrent.onDrag(e);
18277                 if(!this.dragCurrent.moveOnly){
18278                     this.fireEvents(e, false);
18279                 }
18280             }
18281
18282             this.stopEvent(e);
18283
18284             return true;
18285         },
18286
18287         /**
18288          * Iterates over all of the DragDrop elements to find ones we are
18289          * hovering over or dropping on
18290          * @method fireEvents
18291          * @param {Event} e the event
18292          * @param {boolean} isDrop is this a drop op or a mouseover op?
18293          * @private
18294          * @static
18295          */
18296         fireEvents: function(e, isDrop) {
18297             var dc = this.dragCurrent;
18298
18299             // If the user did the mouse up outside of the window, we could
18300             // get here even though we have ended the drag.
18301             if (!dc || dc.isLocked()) {
18302                 return;
18303             }
18304
18305             var pt = e.getPoint();
18306
18307             // cache the previous dragOver array
18308             var oldOvers = [];
18309
18310             var outEvts   = [];
18311             var overEvts  = [];
18312             var dropEvts  = [];
18313             var enterEvts = [];
18314
18315             // Check to see if the object(s) we were hovering over is no longer
18316             // being hovered over so we can fire the onDragOut event
18317             for (var i in this.dragOvers) {
18318
18319                 var ddo = this.dragOvers[i];
18320
18321                 if (! this.isTypeOfDD(ddo)) {
18322                     continue;
18323                 }
18324
18325                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18326                     outEvts.push( ddo );
18327                 }
18328
18329                 oldOvers[i] = true;
18330                 delete this.dragOvers[i];
18331             }
18332
18333             for (var sGroup in dc.groups) {
18334
18335                 if ("string" != typeof sGroup) {
18336                     continue;
18337                 }
18338
18339                 for (i in this.ids[sGroup]) {
18340                     var oDD = this.ids[sGroup][i];
18341                     if (! this.isTypeOfDD(oDD)) {
18342                         continue;
18343                     }
18344
18345                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18346                         if (this.isOverTarget(pt, oDD, this.mode)) {
18347                             // look for drop interactions
18348                             if (isDrop) {
18349                                 dropEvts.push( oDD );
18350                             // look for drag enter and drag over interactions
18351                             } else {
18352
18353                                 // initial drag over: dragEnter fires
18354                                 if (!oldOvers[oDD.id]) {
18355                                     enterEvts.push( oDD );
18356                                 // subsequent drag overs: dragOver fires
18357                                 } else {
18358                                     overEvts.push( oDD );
18359                                 }
18360
18361                                 this.dragOvers[oDD.id] = oDD;
18362                             }
18363                         }
18364                     }
18365                 }
18366             }
18367
18368             if (this.mode) {
18369                 if (outEvts.length) {
18370                     dc.b4DragOut(e, outEvts);
18371                     dc.onDragOut(e, outEvts);
18372                 }
18373
18374                 if (enterEvts.length) {
18375                     dc.onDragEnter(e, enterEvts);
18376                 }
18377
18378                 if (overEvts.length) {
18379                     dc.b4DragOver(e, overEvts);
18380                     dc.onDragOver(e, overEvts);
18381                 }
18382
18383                 if (dropEvts.length) {
18384                     dc.b4DragDrop(e, dropEvts);
18385                     dc.onDragDrop(e, dropEvts);
18386                 }
18387
18388             } else {
18389                 // fire dragout events
18390                 var len = 0;
18391                 for (i=0, len=outEvts.length; i<len; ++i) {
18392                     dc.b4DragOut(e, outEvts[i].id);
18393                     dc.onDragOut(e, outEvts[i].id);
18394                 }
18395
18396                 // fire enter events
18397                 for (i=0,len=enterEvts.length; i<len; ++i) {
18398                     // dc.b4DragEnter(e, oDD.id);
18399                     dc.onDragEnter(e, enterEvts[i].id);
18400                 }
18401
18402                 // fire over events
18403                 for (i=0,len=overEvts.length; i<len; ++i) {
18404                     dc.b4DragOver(e, overEvts[i].id);
18405                     dc.onDragOver(e, overEvts[i].id);
18406                 }
18407
18408                 // fire drop events
18409                 for (i=0, len=dropEvts.length; i<len; ++i) {
18410                     dc.b4DragDrop(e, dropEvts[i].id);
18411                     dc.onDragDrop(e, dropEvts[i].id);
18412                 }
18413
18414             }
18415
18416             // notify about a drop that did not find a target
18417             if (isDrop && !dropEvts.length) {
18418                 dc.onInvalidDrop(e);
18419             }
18420
18421         },
18422
18423         /**
18424          * Helper function for getting the best match from the list of drag
18425          * and drop objects returned by the drag and drop events when we are
18426          * in INTERSECT mode.  It returns either the first object that the
18427          * cursor is over, or the object that has the greatest overlap with
18428          * the dragged element.
18429          * @method getBestMatch
18430          * @param  {DragDrop[]} dds The array of drag and drop objects
18431          * targeted
18432          * @return {DragDrop}       The best single match
18433          * @static
18434          */
18435         getBestMatch: function(dds) {
18436             var winner = null;
18437             // Return null if the input is not what we expect
18438             //if (!dds || !dds.length || dds.length == 0) {
18439                // winner = null;
18440             // If there is only one item, it wins
18441             //} else if (dds.length == 1) {
18442
18443             var len = dds.length;
18444
18445             if (len == 1) {
18446                 winner = dds[0];
18447             } else {
18448                 // Loop through the targeted items
18449                 for (var i=0; i<len; ++i) {
18450                     var dd = dds[i];
18451                     // If the cursor is over the object, it wins.  If the
18452                     // cursor is over multiple matches, the first one we come
18453                     // to wins.
18454                     if (dd.cursorIsOver) {
18455                         winner = dd;
18456                         break;
18457                     // Otherwise the object with the most overlap wins
18458                     } else {
18459                         if (!winner ||
18460                             winner.overlap.getArea() < dd.overlap.getArea()) {
18461                             winner = dd;
18462                         }
18463                     }
18464                 }
18465             }
18466
18467             return winner;
18468         },
18469
18470         /**
18471          * Refreshes the cache of the top-left and bottom-right points of the
18472          * drag and drop objects in the specified group(s).  This is in the
18473          * format that is stored in the drag and drop instance, so typical
18474          * usage is:
18475          * <code>
18476          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18477          * </code>
18478          * Alternatively:
18479          * <code>
18480          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18481          * </code>
18482          * @TODO this really should be an indexed array.  Alternatively this
18483          * method could accept both.
18484          * @method refreshCache
18485          * @param {Object} groups an associative array of groups to refresh
18486          * @static
18487          */
18488         refreshCache: function(groups) {
18489             for (var sGroup in groups) {
18490                 if ("string" != typeof sGroup) {
18491                     continue;
18492                 }
18493                 for (var i in this.ids[sGroup]) {
18494                     var oDD = this.ids[sGroup][i];
18495
18496                     if (this.isTypeOfDD(oDD)) {
18497                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18498                         var loc = this.getLocation(oDD);
18499                         if (loc) {
18500                             this.locationCache[oDD.id] = loc;
18501                         } else {
18502                             delete this.locationCache[oDD.id];
18503                             // this will unregister the drag and drop object if
18504                             // the element is not in a usable state
18505                             // oDD.unreg();
18506                         }
18507                     }
18508                 }
18509             }
18510         },
18511
18512         /**
18513          * This checks to make sure an element exists and is in the DOM.  The
18514          * main purpose is to handle cases where innerHTML is used to remove
18515          * drag and drop objects from the DOM.  IE provides an 'unspecified
18516          * error' when trying to access the offsetParent of such an element
18517          * @method verifyEl
18518          * @param {HTMLElement} el the element to check
18519          * @return {boolean} true if the element looks usable
18520          * @static
18521          */
18522         verifyEl: function(el) {
18523             if (el) {
18524                 var parent;
18525                 if(Roo.isIE){
18526                     try{
18527                         parent = el.offsetParent;
18528                     }catch(e){}
18529                 }else{
18530                     parent = el.offsetParent;
18531                 }
18532                 if (parent) {
18533                     return true;
18534                 }
18535             }
18536
18537             return false;
18538         },
18539
18540         /**
18541          * Returns a Region object containing the drag and drop element's position
18542          * and size, including the padding configured for it
18543          * @method getLocation
18544          * @param {DragDrop} oDD the drag and drop object to get the
18545          *                       location for
18546          * @return {Roo.lib.Region} a Region object representing the total area
18547          *                             the element occupies, including any padding
18548          *                             the instance is configured for.
18549          * @static
18550          */
18551         getLocation: function(oDD) {
18552             if (! this.isTypeOfDD(oDD)) {
18553                 return null;
18554             }
18555
18556             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18557
18558             try {
18559                 pos= Roo.lib.Dom.getXY(el);
18560             } catch (e) { }
18561
18562             if (!pos) {
18563                 return null;
18564             }
18565
18566             x1 = pos[0];
18567             x2 = x1 + el.offsetWidth;
18568             y1 = pos[1];
18569             y2 = y1 + el.offsetHeight;
18570
18571             t = y1 - oDD.padding[0];
18572             r = x2 + oDD.padding[1];
18573             b = y2 + oDD.padding[2];
18574             l = x1 - oDD.padding[3];
18575
18576             return new Roo.lib.Region( t, r, b, l );
18577         },
18578
18579         /**
18580          * Checks the cursor location to see if it over the target
18581          * @method isOverTarget
18582          * @param {Roo.lib.Point} pt The point to evaluate
18583          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18584          * @return {boolean} true if the mouse is over the target
18585          * @private
18586          * @static
18587          */
18588         isOverTarget: function(pt, oTarget, intersect) {
18589             // use cache if available
18590             var loc = this.locationCache[oTarget.id];
18591             if (!loc || !this.useCache) {
18592                 loc = this.getLocation(oTarget);
18593                 this.locationCache[oTarget.id] = loc;
18594
18595             }
18596
18597             if (!loc) {
18598                 return false;
18599             }
18600
18601             oTarget.cursorIsOver = loc.contains( pt );
18602
18603             // DragDrop is using this as a sanity check for the initial mousedown
18604             // in this case we are done.  In POINT mode, if the drag obj has no
18605             // contraints, we are also done. Otherwise we need to evaluate the
18606             // location of the target as related to the actual location of the
18607             // dragged element.
18608             var dc = this.dragCurrent;
18609             if (!dc || !dc.getTargetCoord ||
18610                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18611                 return oTarget.cursorIsOver;
18612             }
18613
18614             oTarget.overlap = null;
18615
18616             // Get the current location of the drag element, this is the
18617             // location of the mouse event less the delta that represents
18618             // where the original mousedown happened on the element.  We
18619             // need to consider constraints and ticks as well.
18620             var pos = dc.getTargetCoord(pt.x, pt.y);
18621
18622             var el = dc.getDragEl();
18623             var curRegion = new Roo.lib.Region( pos.y,
18624                                                    pos.x + el.offsetWidth,
18625                                                    pos.y + el.offsetHeight,
18626                                                    pos.x );
18627
18628             var overlap = curRegion.intersect(loc);
18629
18630             if (overlap) {
18631                 oTarget.overlap = overlap;
18632                 return (intersect) ? true : oTarget.cursorIsOver;
18633             } else {
18634                 return false;
18635             }
18636         },
18637
18638         /**
18639          * unload event handler
18640          * @method _onUnload
18641          * @private
18642          * @static
18643          */
18644         _onUnload: function(e, me) {
18645             Roo.dd.DragDropMgr.unregAll();
18646         },
18647
18648         /**
18649          * Cleans up the drag and drop events and objects.
18650          * @method unregAll
18651          * @private
18652          * @static
18653          */
18654         unregAll: function() {
18655
18656             if (this.dragCurrent) {
18657                 this.stopDrag();
18658                 this.dragCurrent = null;
18659             }
18660
18661             this._execOnAll("unreg", []);
18662
18663             for (i in this.elementCache) {
18664                 delete this.elementCache[i];
18665             }
18666
18667             this.elementCache = {};
18668             this.ids = {};
18669         },
18670
18671         /**
18672          * A cache of DOM elements
18673          * @property elementCache
18674          * @private
18675          * @static
18676          */
18677         elementCache: {},
18678
18679         /**
18680          * Get the wrapper for the DOM element specified
18681          * @method getElWrapper
18682          * @param {String} id the id of the element to get
18683          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18684          * @private
18685          * @deprecated This wrapper isn't that useful
18686          * @static
18687          */
18688         getElWrapper: function(id) {
18689             var oWrapper = this.elementCache[id];
18690             if (!oWrapper || !oWrapper.el) {
18691                 oWrapper = this.elementCache[id] =
18692                     new this.ElementWrapper(Roo.getDom(id));
18693             }
18694             return oWrapper;
18695         },
18696
18697         /**
18698          * Returns the actual DOM element
18699          * @method getElement
18700          * @param {String} id the id of the elment to get
18701          * @return {Object} The element
18702          * @deprecated use Roo.getDom instead
18703          * @static
18704          */
18705         getElement: function(id) {
18706             return Roo.getDom(id);
18707         },
18708
18709         /**
18710          * Returns the style property for the DOM element (i.e.,
18711          * document.getElById(id).style)
18712          * @method getCss
18713          * @param {String} id the id of the elment to get
18714          * @return {Object} The style property of the element
18715          * @deprecated use Roo.getDom instead
18716          * @static
18717          */
18718         getCss: function(id) {
18719             var el = Roo.getDom(id);
18720             return (el) ? el.style : null;
18721         },
18722
18723         /**
18724          * Inner class for cached elements
18725          * @class DragDropMgr.ElementWrapper
18726          * @for DragDropMgr
18727          * @private
18728          * @deprecated
18729          */
18730         ElementWrapper: function(el) {
18731                 /**
18732                  * The element
18733                  * @property el
18734                  */
18735                 this.el = el || null;
18736                 /**
18737                  * The element id
18738                  * @property id
18739                  */
18740                 this.id = this.el && el.id;
18741                 /**
18742                  * A reference to the style property
18743                  * @property css
18744                  */
18745                 this.css = this.el && el.style;
18746             },
18747
18748         /**
18749          * Returns the X position of an html element
18750          * @method getPosX
18751          * @param el the element for which to get the position
18752          * @return {int} the X coordinate
18753          * @for DragDropMgr
18754          * @deprecated use Roo.lib.Dom.getX instead
18755          * @static
18756          */
18757         getPosX: function(el) {
18758             return Roo.lib.Dom.getX(el);
18759         },
18760
18761         /**
18762          * Returns the Y position of an html element
18763          * @method getPosY
18764          * @param el the element for which to get the position
18765          * @return {int} the Y coordinate
18766          * @deprecated use Roo.lib.Dom.getY instead
18767          * @static
18768          */
18769         getPosY: function(el) {
18770             return Roo.lib.Dom.getY(el);
18771         },
18772
18773         /**
18774          * Swap two nodes.  In IE, we use the native method, for others we
18775          * emulate the IE behavior
18776          * @method swapNode
18777          * @param n1 the first node to swap
18778          * @param n2 the other node to swap
18779          * @static
18780          */
18781         swapNode: function(n1, n2) {
18782             if (n1.swapNode) {
18783                 n1.swapNode(n2);
18784             } else {
18785                 var p = n2.parentNode;
18786                 var s = n2.nextSibling;
18787
18788                 if (s == n1) {
18789                     p.insertBefore(n1, n2);
18790                 } else if (n2 == n1.nextSibling) {
18791                     p.insertBefore(n2, n1);
18792                 } else {
18793                     n1.parentNode.replaceChild(n2, n1);
18794                     p.insertBefore(n1, s);
18795                 }
18796             }
18797         },
18798
18799         /**
18800          * Returns the current scroll position
18801          * @method getScroll
18802          * @private
18803          * @static
18804          */
18805         getScroll: function () {
18806             var t, l, dde=document.documentElement, db=document.body;
18807             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18808                 t = dde.scrollTop;
18809                 l = dde.scrollLeft;
18810             } else if (db) {
18811                 t = db.scrollTop;
18812                 l = db.scrollLeft;
18813             } else {
18814
18815             }
18816             return { top: t, left: l };
18817         },
18818
18819         /**
18820          * Returns the specified element style property
18821          * @method getStyle
18822          * @param {HTMLElement} el          the element
18823          * @param {string}      styleProp   the style property
18824          * @return {string} The value of the style property
18825          * @deprecated use Roo.lib.Dom.getStyle
18826          * @static
18827          */
18828         getStyle: function(el, styleProp) {
18829             return Roo.fly(el).getStyle(styleProp);
18830         },
18831
18832         /**
18833          * Gets the scrollTop
18834          * @method getScrollTop
18835          * @return {int} the document's scrollTop
18836          * @static
18837          */
18838         getScrollTop: function () { return this.getScroll().top; },
18839
18840         /**
18841          * Gets the scrollLeft
18842          * @method getScrollLeft
18843          * @return {int} the document's scrollTop
18844          * @static
18845          */
18846         getScrollLeft: function () { return this.getScroll().left; },
18847
18848         /**
18849          * Sets the x/y position of an element to the location of the
18850          * target element.
18851          * @method moveToEl
18852          * @param {HTMLElement} moveEl      The element to move
18853          * @param {HTMLElement} targetEl    The position reference element
18854          * @static
18855          */
18856         moveToEl: function (moveEl, targetEl) {
18857             var aCoord = Roo.lib.Dom.getXY(targetEl);
18858             Roo.lib.Dom.setXY(moveEl, aCoord);
18859         },
18860
18861         /**
18862          * Numeric array sort function
18863          * @method numericSort
18864          * @static
18865          */
18866         numericSort: function(a, b) { return (a - b); },
18867
18868         /**
18869          * Internal counter
18870          * @property _timeoutCount
18871          * @private
18872          * @static
18873          */
18874         _timeoutCount: 0,
18875
18876         /**
18877          * Trying to make the load order less important.  Without this we get
18878          * an error if this file is loaded before the Event Utility.
18879          * @method _addListeners
18880          * @private
18881          * @static
18882          */
18883         _addListeners: function() {
18884             var DDM = Roo.dd.DDM;
18885             if ( Roo.lib.Event && document ) {
18886                 DDM._onLoad();
18887             } else {
18888                 if (DDM._timeoutCount > 2000) {
18889                 } else {
18890                     setTimeout(DDM._addListeners, 10);
18891                     if (document && document.body) {
18892                         DDM._timeoutCount += 1;
18893                     }
18894                 }
18895             }
18896         },
18897
18898         /**
18899          * Recursively searches the immediate parent and all child nodes for
18900          * the handle element in order to determine wheter or not it was
18901          * clicked.
18902          * @method handleWasClicked
18903          * @param node the html element to inspect
18904          * @static
18905          */
18906         handleWasClicked: function(node, id) {
18907             if (this.isHandle(id, node.id)) {
18908                 return true;
18909             } else {
18910                 // check to see if this is a text node child of the one we want
18911                 var p = node.parentNode;
18912
18913                 while (p) {
18914                     if (this.isHandle(id, p.id)) {
18915                         return true;
18916                     } else {
18917                         p = p.parentNode;
18918                     }
18919                 }
18920             }
18921
18922             return false;
18923         }
18924
18925     };
18926
18927 }();
18928
18929 // shorter alias, save a few bytes
18930 Roo.dd.DDM = Roo.dd.DragDropMgr;
18931 Roo.dd.DDM._addListeners();
18932
18933 }/*
18934  * Based on:
18935  * Ext JS Library 1.1.1
18936  * Copyright(c) 2006-2007, Ext JS, LLC.
18937  *
18938  * Originally Released Under LGPL - original licence link has changed is not relivant.
18939  *
18940  * Fork - LGPL
18941  * <script type="text/javascript">
18942  */
18943
18944 /**
18945  * @class Roo.dd.DD
18946  * A DragDrop implementation where the linked element follows the
18947  * mouse cursor during a drag.
18948  * @extends Roo.dd.DragDrop
18949  * @constructor
18950  * @param {String} id the id of the linked element
18951  * @param {String} sGroup the group of related DragDrop items
18952  * @param {object} config an object containing configurable attributes
18953  *                Valid properties for DD:
18954  *                    scroll
18955  */
18956 Roo.dd.DD = function(id, sGroup, config) {
18957     if (id) {
18958         this.init(id, sGroup, config);
18959     }
18960 };
18961
18962 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18963
18964     /**
18965      * When set to true, the utility automatically tries to scroll the browser
18966      * window wehn a drag and drop element is dragged near the viewport boundary.
18967      * Defaults to true.
18968      * @property scroll
18969      * @type boolean
18970      */
18971     scroll: true,
18972
18973     /**
18974      * Sets the pointer offset to the distance between the linked element's top
18975      * left corner and the location the element was clicked
18976      * @method autoOffset
18977      * @param {int} iPageX the X coordinate of the click
18978      * @param {int} iPageY the Y coordinate of the click
18979      */
18980     autoOffset: function(iPageX, iPageY) {
18981         var x = iPageX - this.startPageX;
18982         var y = iPageY - this.startPageY;
18983         this.setDelta(x, y);
18984     },
18985
18986     /**
18987      * Sets the pointer offset.  You can call this directly to force the
18988      * offset to be in a particular location (e.g., pass in 0,0 to set it
18989      * to the center of the object)
18990      * @method setDelta
18991      * @param {int} iDeltaX the distance from the left
18992      * @param {int} iDeltaY the distance from the top
18993      */
18994     setDelta: function(iDeltaX, iDeltaY) {
18995         this.deltaX = iDeltaX;
18996         this.deltaY = iDeltaY;
18997     },
18998
18999     /**
19000      * Sets the drag element to the location of the mousedown or click event,
19001      * maintaining the cursor location relative to the location on the element
19002      * that was clicked.  Override this if you want to place the element in a
19003      * location other than where the cursor is.
19004      * @method setDragElPos
19005      * @param {int} iPageX the X coordinate of the mousedown or drag event
19006      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19007      */
19008     setDragElPos: function(iPageX, iPageY) {
19009         // the first time we do this, we are going to check to make sure
19010         // the element has css positioning
19011
19012         var el = this.getDragEl();
19013         this.alignElWithMouse(el, iPageX, iPageY);
19014     },
19015
19016     /**
19017      * Sets the element to the location of the mousedown or click event,
19018      * maintaining the cursor location relative to the location on the element
19019      * that was clicked.  Override this if you want to place the element in a
19020      * location other than where the cursor is.
19021      * @method alignElWithMouse
19022      * @param {HTMLElement} el the element to move
19023      * @param {int} iPageX the X coordinate of the mousedown or drag event
19024      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19025      */
19026     alignElWithMouse: function(el, iPageX, iPageY) {
19027         var oCoord = this.getTargetCoord(iPageX, iPageY);
19028         var fly = el.dom ? el : Roo.fly(el);
19029         if (!this.deltaSetXY) {
19030             var aCoord = [oCoord.x, oCoord.y];
19031             fly.setXY(aCoord);
19032             var newLeft = fly.getLeft(true);
19033             var newTop  = fly.getTop(true);
19034             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19035         } else {
19036             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19037         }
19038
19039         this.cachePosition(oCoord.x, oCoord.y);
19040         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19041         return oCoord;
19042     },
19043
19044     /**
19045      * Saves the most recent position so that we can reset the constraints and
19046      * tick marks on-demand.  We need to know this so that we can calculate the
19047      * number of pixels the element is offset from its original position.
19048      * @method cachePosition
19049      * @param iPageX the current x position (optional, this just makes it so we
19050      * don't have to look it up again)
19051      * @param iPageY the current y position (optional, this just makes it so we
19052      * don't have to look it up again)
19053      */
19054     cachePosition: function(iPageX, iPageY) {
19055         if (iPageX) {
19056             this.lastPageX = iPageX;
19057             this.lastPageY = iPageY;
19058         } else {
19059             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19060             this.lastPageX = aCoord[0];
19061             this.lastPageY = aCoord[1];
19062         }
19063     },
19064
19065     /**
19066      * Auto-scroll the window if the dragged object has been moved beyond the
19067      * visible window boundary.
19068      * @method autoScroll
19069      * @param {int} x the drag element's x position
19070      * @param {int} y the drag element's y position
19071      * @param {int} h the height of the drag element
19072      * @param {int} w the width of the drag element
19073      * @private
19074      */
19075     autoScroll: function(x, y, h, w) {
19076
19077         if (this.scroll) {
19078             // The client height
19079             var clientH = Roo.lib.Dom.getViewWidth();
19080
19081             // The client width
19082             var clientW = Roo.lib.Dom.getViewHeight();
19083
19084             // The amt scrolled down
19085             var st = this.DDM.getScrollTop();
19086
19087             // The amt scrolled right
19088             var sl = this.DDM.getScrollLeft();
19089
19090             // Location of the bottom of the element
19091             var bot = h + y;
19092
19093             // Location of the right of the element
19094             var right = w + x;
19095
19096             // The distance from the cursor to the bottom of the visible area,
19097             // adjusted so that we don't scroll if the cursor is beyond the
19098             // element drag constraints
19099             var toBot = (clientH + st - y - this.deltaY);
19100
19101             // The distance from the cursor to the right of the visible area
19102             var toRight = (clientW + sl - x - this.deltaX);
19103
19104
19105             // How close to the edge the cursor must be before we scroll
19106             // var thresh = (document.all) ? 100 : 40;
19107             var thresh = 40;
19108
19109             // How many pixels to scroll per autoscroll op.  This helps to reduce
19110             // clunky scrolling. IE is more sensitive about this ... it needs this
19111             // value to be higher.
19112             var scrAmt = (document.all) ? 80 : 30;
19113
19114             // Scroll down if we are near the bottom of the visible page and the
19115             // obj extends below the crease
19116             if ( bot > clientH && toBot < thresh ) {
19117                 window.scrollTo(sl, st + scrAmt);
19118             }
19119
19120             // Scroll up if the window is scrolled down and the top of the object
19121             // goes above the top border
19122             if ( y < st && st > 0 && y - st < thresh ) {
19123                 window.scrollTo(sl, st - scrAmt);
19124             }
19125
19126             // Scroll right if the obj is beyond the right border and the cursor is
19127             // near the border.
19128             if ( right > clientW && toRight < thresh ) {
19129                 window.scrollTo(sl + scrAmt, st);
19130             }
19131
19132             // Scroll left if the window has been scrolled to the right and the obj
19133             // extends past the left border
19134             if ( x < sl && sl > 0 && x - sl < thresh ) {
19135                 window.scrollTo(sl - scrAmt, st);
19136             }
19137         }
19138     },
19139
19140     /**
19141      * Finds the location the element should be placed if we want to move
19142      * it to where the mouse location less the click offset would place us.
19143      * @method getTargetCoord
19144      * @param {int} iPageX the X coordinate of the click
19145      * @param {int} iPageY the Y coordinate of the click
19146      * @return an object that contains the coordinates (Object.x and Object.y)
19147      * @private
19148      */
19149     getTargetCoord: function(iPageX, iPageY) {
19150
19151
19152         var x = iPageX - this.deltaX;
19153         var y = iPageY - this.deltaY;
19154
19155         if (this.constrainX) {
19156             if (x < this.minX) { x = this.minX; }
19157             if (x > this.maxX) { x = this.maxX; }
19158         }
19159
19160         if (this.constrainY) {
19161             if (y < this.minY) { y = this.minY; }
19162             if (y > this.maxY) { y = this.maxY; }
19163         }
19164
19165         x = this.getTick(x, this.xTicks);
19166         y = this.getTick(y, this.yTicks);
19167
19168
19169         return {x:x, y:y};
19170     },
19171
19172     /*
19173      * Sets up config options specific to this class. Overrides
19174      * Roo.dd.DragDrop, but all versions of this method through the
19175      * inheritance chain are called
19176      */
19177     applyConfig: function() {
19178         Roo.dd.DD.superclass.applyConfig.call(this);
19179         this.scroll = (this.config.scroll !== false);
19180     },
19181
19182     /*
19183      * Event that fires prior to the onMouseDown event.  Overrides
19184      * Roo.dd.DragDrop.
19185      */
19186     b4MouseDown: function(e) {
19187         // this.resetConstraints();
19188         this.autoOffset(e.getPageX(),
19189                             e.getPageY());
19190     },
19191
19192     /*
19193      * Event that fires prior to the onDrag event.  Overrides
19194      * Roo.dd.DragDrop.
19195      */
19196     b4Drag: function(e) {
19197         this.setDragElPos(e.getPageX(),
19198                             e.getPageY());
19199     },
19200
19201     toString: function() {
19202         return ("DD " + this.id);
19203     }
19204
19205     //////////////////////////////////////////////////////////////////////////
19206     // Debugging ygDragDrop events that can be overridden
19207     //////////////////////////////////////////////////////////////////////////
19208     /*
19209     startDrag: function(x, y) {
19210     },
19211
19212     onDrag: function(e) {
19213     },
19214
19215     onDragEnter: function(e, id) {
19216     },
19217
19218     onDragOver: function(e, id) {
19219     },
19220
19221     onDragOut: function(e, id) {
19222     },
19223
19224     onDragDrop: function(e, id) {
19225     },
19226
19227     endDrag: function(e) {
19228     }
19229
19230     */
19231
19232 });/*
19233  * Based on:
19234  * Ext JS Library 1.1.1
19235  * Copyright(c) 2006-2007, Ext JS, LLC.
19236  *
19237  * Originally Released Under LGPL - original licence link has changed is not relivant.
19238  *
19239  * Fork - LGPL
19240  * <script type="text/javascript">
19241  */
19242
19243 /**
19244  * @class Roo.dd.DDProxy
19245  * A DragDrop implementation that inserts an empty, bordered div into
19246  * the document that follows the cursor during drag operations.  At the time of
19247  * the click, the frame div is resized to the dimensions of the linked html
19248  * element, and moved to the exact location of the linked element.
19249  *
19250  * References to the "frame" element refer to the single proxy element that
19251  * was created to be dragged in place of all DDProxy elements on the
19252  * page.
19253  *
19254  * @extends Roo.dd.DD
19255  * @constructor
19256  * @param {String} id the id of the linked html element
19257  * @param {String} sGroup the group of related DragDrop objects
19258  * @param {object} config an object containing configurable attributes
19259  *                Valid properties for DDProxy in addition to those in DragDrop:
19260  *                   resizeFrame, centerFrame, dragElId
19261  */
19262 Roo.dd.DDProxy = function(id, sGroup, config) {
19263     if (id) {
19264         this.init(id, sGroup, config);
19265         this.initFrame();
19266     }
19267 };
19268
19269 /**
19270  * The default drag frame div id
19271  * @property Roo.dd.DDProxy.dragElId
19272  * @type String
19273  * @static
19274  */
19275 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19276
19277 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19278
19279     /**
19280      * By default we resize the drag frame to be the same size as the element
19281      * we want to drag (this is to get the frame effect).  We can turn it off
19282      * if we want a different behavior.
19283      * @property resizeFrame
19284      * @type boolean
19285      */
19286     resizeFrame: true,
19287
19288     /**
19289      * By default the frame is positioned exactly where the drag element is, so
19290      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19291      * you do not have constraints on the obj is to have the drag frame centered
19292      * around the cursor.  Set centerFrame to true for this effect.
19293      * @property centerFrame
19294      * @type boolean
19295      */
19296     centerFrame: false,
19297
19298     /**
19299      * Creates the proxy element if it does not yet exist
19300      * @method createFrame
19301      */
19302     createFrame: function() {
19303         var self = this;
19304         var body = document.body;
19305
19306         if (!body || !body.firstChild) {
19307             setTimeout( function() { self.createFrame(); }, 50 );
19308             return;
19309         }
19310
19311         var div = this.getDragEl();
19312
19313         if (!div) {
19314             div    = document.createElement("div");
19315             div.id = this.dragElId;
19316             var s  = div.style;
19317
19318             s.position   = "absolute";
19319             s.visibility = "hidden";
19320             s.cursor     = "move";
19321             s.border     = "2px solid #aaa";
19322             s.zIndex     = 999;
19323
19324             // appendChild can blow up IE if invoked prior to the window load event
19325             // while rendering a table.  It is possible there are other scenarios
19326             // that would cause this to happen as well.
19327             body.insertBefore(div, body.firstChild);
19328         }
19329     },
19330
19331     /**
19332      * Initialization for the drag frame element.  Must be called in the
19333      * constructor of all subclasses
19334      * @method initFrame
19335      */
19336     initFrame: function() {
19337         this.createFrame();
19338     },
19339
19340     applyConfig: function() {
19341         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19342
19343         this.resizeFrame = (this.config.resizeFrame !== false);
19344         this.centerFrame = (this.config.centerFrame);
19345         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19346     },
19347
19348     /**
19349      * Resizes the drag frame to the dimensions of the clicked object, positions
19350      * it over the object, and finally displays it
19351      * @method showFrame
19352      * @param {int} iPageX X click position
19353      * @param {int} iPageY Y click position
19354      * @private
19355      */
19356     showFrame: function(iPageX, iPageY) {
19357         var el = this.getEl();
19358         var dragEl = this.getDragEl();
19359         var s = dragEl.style;
19360
19361         this._resizeProxy();
19362
19363         if (this.centerFrame) {
19364             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19365                            Math.round(parseInt(s.height, 10)/2) );
19366         }
19367
19368         this.setDragElPos(iPageX, iPageY);
19369
19370         Roo.fly(dragEl).show();
19371     },
19372
19373     /**
19374      * The proxy is automatically resized to the dimensions of the linked
19375      * element when a drag is initiated, unless resizeFrame is set to false
19376      * @method _resizeProxy
19377      * @private
19378      */
19379     _resizeProxy: function() {
19380         if (this.resizeFrame) {
19381             var el = this.getEl();
19382             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19383         }
19384     },
19385
19386     // overrides Roo.dd.DragDrop
19387     b4MouseDown: function(e) {
19388         var x = e.getPageX();
19389         var y = e.getPageY();
19390         this.autoOffset(x, y);
19391         this.setDragElPos(x, y);
19392     },
19393
19394     // overrides Roo.dd.DragDrop
19395     b4StartDrag: function(x, y) {
19396         // show the drag frame
19397         this.showFrame(x, y);
19398     },
19399
19400     // overrides Roo.dd.DragDrop
19401     b4EndDrag: function(e) {
19402         Roo.fly(this.getDragEl()).hide();
19403     },
19404
19405     // overrides Roo.dd.DragDrop
19406     // By default we try to move the element to the last location of the frame.
19407     // This is so that the default behavior mirrors that of Roo.dd.DD.
19408     endDrag: function(e) {
19409
19410         var lel = this.getEl();
19411         var del = this.getDragEl();
19412
19413         // Show the drag frame briefly so we can get its position
19414         del.style.visibility = "";
19415
19416         this.beforeMove();
19417         // Hide the linked element before the move to get around a Safari
19418         // rendering bug.
19419         lel.style.visibility = "hidden";
19420         Roo.dd.DDM.moveToEl(lel, del);
19421         del.style.visibility = "hidden";
19422         lel.style.visibility = "";
19423
19424         this.afterDrag();
19425     },
19426
19427     beforeMove : function(){
19428
19429     },
19430
19431     afterDrag : function(){
19432
19433     },
19434
19435     toString: function() {
19436         return ("DDProxy " + this.id);
19437     }
19438
19439 });
19440 /*
19441  * Based on:
19442  * Ext JS Library 1.1.1
19443  * Copyright(c) 2006-2007, Ext JS, LLC.
19444  *
19445  * Originally Released Under LGPL - original licence link has changed is not relivant.
19446  *
19447  * Fork - LGPL
19448  * <script type="text/javascript">
19449  */
19450
19451  /**
19452  * @class Roo.dd.DDTarget
19453  * A DragDrop implementation that does not move, but can be a drop
19454  * target.  You would get the same result by simply omitting implementation
19455  * for the event callbacks, but this way we reduce the processing cost of the
19456  * event listener and the callbacks.
19457  * @extends Roo.dd.DragDrop
19458  * @constructor
19459  * @param {String} id the id of the element that is a drop target
19460  * @param {String} sGroup the group of related DragDrop objects
19461  * @param {object} config an object containing configurable attributes
19462  *                 Valid properties for DDTarget in addition to those in
19463  *                 DragDrop:
19464  *                    none
19465  */
19466 Roo.dd.DDTarget = function(id, sGroup, config) {
19467     if (id) {
19468         this.initTarget(id, sGroup, config);
19469     }
19470     if (config.listeners || config.events) { 
19471        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19472             listeners : config.listeners || {}, 
19473             events : config.events || {} 
19474         });    
19475     }
19476 };
19477
19478 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19479 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19480     toString: function() {
19481         return ("DDTarget " + this.id);
19482     }
19483 });
19484 /*
19485  * Based on:
19486  * Ext JS Library 1.1.1
19487  * Copyright(c) 2006-2007, Ext JS, LLC.
19488  *
19489  * Originally Released Under LGPL - original licence link has changed is not relivant.
19490  *
19491  * Fork - LGPL
19492  * <script type="text/javascript">
19493  */
19494  
19495
19496 /**
19497  * @class Roo.dd.ScrollManager
19498  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19499  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19500  * @singleton
19501  */
19502 Roo.dd.ScrollManager = function(){
19503     var ddm = Roo.dd.DragDropMgr;
19504     var els = {};
19505     var dragEl = null;
19506     var proc = {};
19507     
19508     
19509     
19510     var onStop = function(e){
19511         dragEl = null;
19512         clearProc();
19513     };
19514     
19515     var triggerRefresh = function(){
19516         if(ddm.dragCurrent){
19517              ddm.refreshCache(ddm.dragCurrent.groups);
19518         }
19519     };
19520     
19521     var doScroll = function(){
19522         if(ddm.dragCurrent){
19523             var dds = Roo.dd.ScrollManager;
19524             if(!dds.animate){
19525                 if(proc.el.scroll(proc.dir, dds.increment)){
19526                     triggerRefresh();
19527                 }
19528             }else{
19529                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19530             }
19531         }
19532     };
19533     
19534     var clearProc = function(){
19535         if(proc.id){
19536             clearInterval(proc.id);
19537         }
19538         proc.id = 0;
19539         proc.el = null;
19540         proc.dir = "";
19541     };
19542     
19543     var startProc = function(el, dir){
19544          Roo.log('scroll startproc');
19545         clearProc();
19546         proc.el = el;
19547         proc.dir = dir;
19548         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19549     };
19550     
19551     var onFire = function(e, isDrop){
19552        
19553         if(isDrop || !ddm.dragCurrent){ return; }
19554         var dds = Roo.dd.ScrollManager;
19555         if(!dragEl || dragEl != ddm.dragCurrent){
19556             dragEl = ddm.dragCurrent;
19557             // refresh regions on drag start
19558             dds.refreshCache();
19559         }
19560         
19561         var xy = Roo.lib.Event.getXY(e);
19562         var pt = new Roo.lib.Point(xy[0], xy[1]);
19563         for(var id in els){
19564             var el = els[id], r = el._region;
19565             if(r && r.contains(pt) && el.isScrollable()){
19566                 if(r.bottom - pt.y <= dds.thresh){
19567                     if(proc.el != el){
19568                         startProc(el, "down");
19569                     }
19570                     return;
19571                 }else if(r.right - pt.x <= dds.thresh){
19572                     if(proc.el != el){
19573                         startProc(el, "left");
19574                     }
19575                     return;
19576                 }else if(pt.y - r.top <= dds.thresh){
19577                     if(proc.el != el){
19578                         startProc(el, "up");
19579                     }
19580                     return;
19581                 }else if(pt.x - r.left <= dds.thresh){
19582                     if(proc.el != el){
19583                         startProc(el, "right");
19584                     }
19585                     return;
19586                 }
19587             }
19588         }
19589         clearProc();
19590     };
19591     
19592     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19593     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19594     
19595     return {
19596         /**
19597          * Registers new overflow element(s) to auto scroll
19598          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19599          */
19600         register : function(el){
19601             if(el instanceof Array){
19602                 for(var i = 0, len = el.length; i < len; i++) {
19603                         this.register(el[i]);
19604                 }
19605             }else{
19606                 el = Roo.get(el);
19607                 els[el.id] = el;
19608             }
19609             Roo.dd.ScrollManager.els = els;
19610         },
19611         
19612         /**
19613          * Unregisters overflow element(s) so they are no longer scrolled
19614          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19615          */
19616         unregister : function(el){
19617             if(el instanceof Array){
19618                 for(var i = 0, len = el.length; i < len; i++) {
19619                         this.unregister(el[i]);
19620                 }
19621             }else{
19622                 el = Roo.get(el);
19623                 delete els[el.id];
19624             }
19625         },
19626         
19627         /**
19628          * The number of pixels from the edge of a container the pointer needs to be to 
19629          * trigger scrolling (defaults to 25)
19630          * @type Number
19631          */
19632         thresh : 25,
19633         
19634         /**
19635          * The number of pixels to scroll in each scroll increment (defaults to 50)
19636          * @type Number
19637          */
19638         increment : 100,
19639         
19640         /**
19641          * The frequency of scrolls in milliseconds (defaults to 500)
19642          * @type Number
19643          */
19644         frequency : 500,
19645         
19646         /**
19647          * True to animate the scroll (defaults to true)
19648          * @type Boolean
19649          */
19650         animate: true,
19651         
19652         /**
19653          * The animation duration in seconds - 
19654          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19655          * @type Number
19656          */
19657         animDuration: .4,
19658         
19659         /**
19660          * Manually trigger a cache refresh.
19661          */
19662         refreshCache : function(){
19663             for(var id in els){
19664                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19665                     els[id]._region = els[id].getRegion();
19666                 }
19667             }
19668         }
19669     };
19670 }();/*
19671  * Based on:
19672  * Ext JS Library 1.1.1
19673  * Copyright(c) 2006-2007, Ext JS, LLC.
19674  *
19675  * Originally Released Under LGPL - original licence link has changed is not relivant.
19676  *
19677  * Fork - LGPL
19678  * <script type="text/javascript">
19679  */
19680  
19681
19682 /**
19683  * @class Roo.dd.Registry
19684  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19685  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19686  * @singleton
19687  */
19688 Roo.dd.Registry = function(){
19689     var elements = {}; 
19690     var handles = {}; 
19691     var autoIdSeed = 0;
19692
19693     var getId = function(el, autogen){
19694         if(typeof el == "string"){
19695             return el;
19696         }
19697         var id = el.id;
19698         if(!id && autogen !== false){
19699             id = "roodd-" + (++autoIdSeed);
19700             el.id = id;
19701         }
19702         return id;
19703     };
19704     
19705     return {
19706     /**
19707      * Register a drag drop element
19708      * @param {String|HTMLElement} element The id or DOM node to register
19709      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19710      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19711      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19712      * populated in the data object (if applicable):
19713      * <pre>
19714 Value      Description<br />
19715 ---------  ------------------------------------------<br />
19716 handles    Array of DOM nodes that trigger dragging<br />
19717            for the element being registered<br />
19718 isHandle   True if the element passed in triggers<br />
19719            dragging itself, else false
19720 </pre>
19721      */
19722         register : function(el, data){
19723             data = data || {};
19724             if(typeof el == "string"){
19725                 el = document.getElementById(el);
19726             }
19727             data.ddel = el;
19728             elements[getId(el)] = data;
19729             if(data.isHandle !== false){
19730                 handles[data.ddel.id] = data;
19731             }
19732             if(data.handles){
19733                 var hs = data.handles;
19734                 for(var i = 0, len = hs.length; i < len; i++){
19735                         handles[getId(hs[i])] = data;
19736                 }
19737             }
19738         },
19739
19740     /**
19741      * Unregister a drag drop element
19742      * @param {String|HTMLElement}  element The id or DOM node to unregister
19743      */
19744         unregister : function(el){
19745             var id = getId(el, false);
19746             var data = elements[id];
19747             if(data){
19748                 delete elements[id];
19749                 if(data.handles){
19750                     var hs = data.handles;
19751                     for(var i = 0, len = hs.length; i < len; i++){
19752                         delete handles[getId(hs[i], false)];
19753                     }
19754                 }
19755             }
19756         },
19757
19758     /**
19759      * Returns the handle registered for a DOM Node by id
19760      * @param {String|HTMLElement} id The DOM node or id to look up
19761      * @return {Object} handle The custom handle data
19762      */
19763         getHandle : function(id){
19764             if(typeof id != "string"){ // must be element?
19765                 id = id.id;
19766             }
19767             return handles[id];
19768         },
19769
19770     /**
19771      * Returns the handle that is registered for the DOM node that is the target of the event
19772      * @param {Event} e The event
19773      * @return {Object} handle The custom handle data
19774      */
19775         getHandleFromEvent : function(e){
19776             var t = Roo.lib.Event.getTarget(e);
19777             return t ? handles[t.id] : null;
19778         },
19779
19780     /**
19781      * Returns a custom data object that is registered for a DOM node by id
19782      * @param {String|HTMLElement} id The DOM node or id to look up
19783      * @return {Object} data The custom data
19784      */
19785         getTarget : function(id){
19786             if(typeof id != "string"){ // must be element?
19787                 id = id.id;
19788             }
19789             return elements[id];
19790         },
19791
19792     /**
19793      * Returns a custom data object that is registered for the DOM node that is the target of the event
19794      * @param {Event} e The event
19795      * @return {Object} data The custom data
19796      */
19797         getTargetFromEvent : function(e){
19798             var t = Roo.lib.Event.getTarget(e);
19799             return t ? elements[t.id] || handles[t.id] : null;
19800         }
19801     };
19802 }();/*
19803  * Based on:
19804  * Ext JS Library 1.1.1
19805  * Copyright(c) 2006-2007, Ext JS, LLC.
19806  *
19807  * Originally Released Under LGPL - original licence link has changed is not relivant.
19808  *
19809  * Fork - LGPL
19810  * <script type="text/javascript">
19811  */
19812  
19813
19814 /**
19815  * @class Roo.dd.StatusProxy
19816  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19817  * default drag proxy used by all Roo.dd components.
19818  * @constructor
19819  * @param {Object} config
19820  */
19821 Roo.dd.StatusProxy = function(config){
19822     Roo.apply(this, config);
19823     this.id = this.id || Roo.id();
19824     this.el = new Roo.Layer({
19825         dh: {
19826             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19827                 {tag: "div", cls: "x-dd-drop-icon"},
19828                 {tag: "div", cls: "x-dd-drag-ghost"}
19829             ]
19830         }, 
19831         shadow: !config || config.shadow !== false
19832     });
19833     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19834     this.dropStatus = this.dropNotAllowed;
19835 };
19836
19837 Roo.dd.StatusProxy.prototype = {
19838     /**
19839      * @cfg {String} dropAllowed
19840      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19841      */
19842     dropAllowed : "x-dd-drop-ok",
19843     /**
19844      * @cfg {String} dropNotAllowed
19845      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19846      */
19847     dropNotAllowed : "x-dd-drop-nodrop",
19848
19849     /**
19850      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19851      * over the current target element.
19852      * @param {String} cssClass The css class for the new drop status indicator image
19853      */
19854     setStatus : function(cssClass){
19855         cssClass = cssClass || this.dropNotAllowed;
19856         if(this.dropStatus != cssClass){
19857             this.el.replaceClass(this.dropStatus, cssClass);
19858             this.dropStatus = cssClass;
19859         }
19860     },
19861
19862     /**
19863      * Resets the status indicator to the default dropNotAllowed value
19864      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19865      */
19866     reset : function(clearGhost){
19867         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19868         this.dropStatus = this.dropNotAllowed;
19869         if(clearGhost){
19870             this.ghost.update("");
19871         }
19872     },
19873
19874     /**
19875      * Updates the contents of the ghost element
19876      * @param {String} html The html that will replace the current innerHTML of the ghost element
19877      */
19878     update : function(html){
19879         if(typeof html == "string"){
19880             this.ghost.update(html);
19881         }else{
19882             this.ghost.update("");
19883             html.style.margin = "0";
19884             this.ghost.dom.appendChild(html);
19885         }
19886         // ensure float = none set?? cant remember why though.
19887         var el = this.ghost.dom.firstChild;
19888                 if(el){
19889                         Roo.fly(el).setStyle('float', 'none');
19890                 }
19891     },
19892     
19893     /**
19894      * Returns the underlying proxy {@link Roo.Layer}
19895      * @return {Roo.Layer} el
19896     */
19897     getEl : function(){
19898         return this.el;
19899     },
19900
19901     /**
19902      * Returns the ghost element
19903      * @return {Roo.Element} el
19904      */
19905     getGhost : function(){
19906         return this.ghost;
19907     },
19908
19909     /**
19910      * Hides the proxy
19911      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19912      */
19913     hide : function(clear){
19914         this.el.hide();
19915         if(clear){
19916             this.reset(true);
19917         }
19918     },
19919
19920     /**
19921      * Stops the repair animation if it's currently running
19922      */
19923     stop : function(){
19924         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19925             this.anim.stop();
19926         }
19927     },
19928
19929     /**
19930      * Displays this proxy
19931      */
19932     show : function(){
19933         this.el.show();
19934     },
19935
19936     /**
19937      * Force the Layer to sync its shadow and shim positions to the element
19938      */
19939     sync : function(){
19940         this.el.sync();
19941     },
19942
19943     /**
19944      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19945      * invalid drop operation by the item being dragged.
19946      * @param {Array} xy The XY position of the element ([x, y])
19947      * @param {Function} callback The function to call after the repair is complete
19948      * @param {Object} scope The scope in which to execute the callback
19949      */
19950     repair : function(xy, callback, scope){
19951         this.callback = callback;
19952         this.scope = scope;
19953         if(xy && this.animRepair !== false){
19954             this.el.addClass("x-dd-drag-repair");
19955             this.el.hideUnders(true);
19956             this.anim = this.el.shift({
19957                 duration: this.repairDuration || .5,
19958                 easing: 'easeOut',
19959                 xy: xy,
19960                 stopFx: true,
19961                 callback: this.afterRepair,
19962                 scope: this
19963             });
19964         }else{
19965             this.afterRepair();
19966         }
19967     },
19968
19969     // private
19970     afterRepair : function(){
19971         this.hide(true);
19972         if(typeof this.callback == "function"){
19973             this.callback.call(this.scope || this);
19974         }
19975         this.callback = null;
19976         this.scope = null;
19977     }
19978 };/*
19979  * Based on:
19980  * Ext JS Library 1.1.1
19981  * Copyright(c) 2006-2007, Ext JS, LLC.
19982  *
19983  * Originally Released Under LGPL - original licence link has changed is not relivant.
19984  *
19985  * Fork - LGPL
19986  * <script type="text/javascript">
19987  */
19988
19989 /**
19990  * @class Roo.dd.DragSource
19991  * @extends Roo.dd.DDProxy
19992  * A simple class that provides the basic implementation needed to make any element draggable.
19993  * @constructor
19994  * @param {String/HTMLElement/Element} el The container element
19995  * @param {Object} config
19996  */
19997 Roo.dd.DragSource = function(el, config){
19998     this.el = Roo.get(el);
19999     this.dragData = {};
20000     
20001     Roo.apply(this, config);
20002     
20003     if(!this.proxy){
20004         this.proxy = new Roo.dd.StatusProxy();
20005     }
20006
20007     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20008           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20009     
20010     this.dragging = false;
20011 };
20012
20013 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20014     /**
20015      * @cfg {String} dropAllowed
20016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20017      */
20018     dropAllowed : "x-dd-drop-ok",
20019     /**
20020      * @cfg {String} dropNotAllowed
20021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20022      */
20023     dropNotAllowed : "x-dd-drop-nodrop",
20024
20025     /**
20026      * Returns the data object associated with this drag source
20027      * @return {Object} data An object containing arbitrary data
20028      */
20029     getDragData : function(e){
20030         return this.dragData;
20031     },
20032
20033     // private
20034     onDragEnter : function(e, id){
20035         var target = Roo.dd.DragDropMgr.getDDById(id);
20036         this.cachedTarget = target;
20037         if(this.beforeDragEnter(target, e, id) !== false){
20038             if(target.isNotifyTarget){
20039                 var status = target.notifyEnter(this, e, this.dragData);
20040                 this.proxy.setStatus(status);
20041             }else{
20042                 this.proxy.setStatus(this.dropAllowed);
20043             }
20044             
20045             if(this.afterDragEnter){
20046                 /**
20047                  * An empty function by default, but provided so that you can perform a custom action
20048                  * when the dragged item enters the drop target by providing an implementation.
20049                  * @param {Roo.dd.DragDrop} target The drop target
20050                  * @param {Event} e The event object
20051                  * @param {String} id The id of the dragged element
20052                  * @method afterDragEnter
20053                  */
20054                 this.afterDragEnter(target, e, id);
20055             }
20056         }
20057     },
20058
20059     /**
20060      * An empty function by default, but provided so that you can perform a custom action
20061      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20062      * @param {Roo.dd.DragDrop} target The drop target
20063      * @param {Event} e The event object
20064      * @param {String} id The id of the dragged element
20065      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20066      */
20067     beforeDragEnter : function(target, e, id){
20068         return true;
20069     },
20070
20071     // private
20072     alignElWithMouse: function() {
20073         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20074         this.proxy.sync();
20075     },
20076
20077     // private
20078     onDragOver : function(e, id){
20079         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20080         if(this.beforeDragOver(target, e, id) !== false){
20081             if(target.isNotifyTarget){
20082                 var status = target.notifyOver(this, e, this.dragData);
20083                 this.proxy.setStatus(status);
20084             }
20085
20086             if(this.afterDragOver){
20087                 /**
20088                  * An empty function by default, but provided so that you can perform a custom action
20089                  * while the dragged item is over the drop target by providing an implementation.
20090                  * @param {Roo.dd.DragDrop} target The drop target
20091                  * @param {Event} e The event object
20092                  * @param {String} id The id of the dragged element
20093                  * @method afterDragOver
20094                  */
20095                 this.afterDragOver(target, e, id);
20096             }
20097         }
20098     },
20099
20100     /**
20101      * An empty function by default, but provided so that you can perform a custom action
20102      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20103      * @param {Roo.dd.DragDrop} target The drop target
20104      * @param {Event} e The event object
20105      * @param {String} id The id of the dragged element
20106      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20107      */
20108     beforeDragOver : function(target, e, id){
20109         return true;
20110     },
20111
20112     // private
20113     onDragOut : function(e, id){
20114         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20115         if(this.beforeDragOut(target, e, id) !== false){
20116             if(target.isNotifyTarget){
20117                 target.notifyOut(this, e, this.dragData);
20118             }
20119             this.proxy.reset();
20120             if(this.afterDragOut){
20121                 /**
20122                  * An empty function by default, but provided so that you can perform a custom action
20123                  * after the dragged item is dragged out of the target without dropping.
20124                  * @param {Roo.dd.DragDrop} target The drop target
20125                  * @param {Event} e The event object
20126                  * @param {String} id The id of the dragged element
20127                  * @method afterDragOut
20128                  */
20129                 this.afterDragOut(target, e, id);
20130             }
20131         }
20132         this.cachedTarget = null;
20133     },
20134
20135     /**
20136      * An empty function by default, but provided so that you can perform a custom action before the dragged
20137      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20138      * @param {Roo.dd.DragDrop} target The drop target
20139      * @param {Event} e The event object
20140      * @param {String} id The id of the dragged element
20141      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20142      */
20143     beforeDragOut : function(target, e, id){
20144         return true;
20145     },
20146     
20147     // private
20148     onDragDrop : function(e, id){
20149         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20150         if(this.beforeDragDrop(target, e, id) !== false){
20151             if(target.isNotifyTarget){
20152                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20153                     this.onValidDrop(target, e, id);
20154                 }else{
20155                     this.onInvalidDrop(target, e, id);
20156                 }
20157             }else{
20158                 this.onValidDrop(target, e, id);
20159             }
20160             
20161             if(this.afterDragDrop){
20162                 /**
20163                  * An empty function by default, but provided so that you can perform a custom action
20164                  * after a valid drag drop has occurred by providing an implementation.
20165                  * @param {Roo.dd.DragDrop} target The drop target
20166                  * @param {Event} e The event object
20167                  * @param {String} id The id of the dropped element
20168                  * @method afterDragDrop
20169                  */
20170                 this.afterDragDrop(target, e, id);
20171             }
20172         }
20173         delete this.cachedTarget;
20174     },
20175
20176     /**
20177      * An empty function by default, but provided so that you can perform a custom action before the dragged
20178      * item is dropped onto the target and optionally cancel the onDragDrop.
20179      * @param {Roo.dd.DragDrop} target The drop target
20180      * @param {Event} e The event object
20181      * @param {String} id The id of the dragged element
20182      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20183      */
20184     beforeDragDrop : function(target, e, id){
20185         return true;
20186     },
20187
20188     // private
20189     onValidDrop : function(target, e, id){
20190         this.hideProxy();
20191         if(this.afterValidDrop){
20192             /**
20193              * An empty function by default, but provided so that you can perform a custom action
20194              * after a valid drop has occurred by providing an implementation.
20195              * @param {Object} target The target DD 
20196              * @param {Event} e The event object
20197              * @param {String} id The id of the dropped element
20198              * @method afterInvalidDrop
20199              */
20200             this.afterValidDrop(target, e, id);
20201         }
20202     },
20203
20204     // private
20205     getRepairXY : function(e, data){
20206         return this.el.getXY();  
20207     },
20208
20209     // private
20210     onInvalidDrop : function(target, e, id){
20211         this.beforeInvalidDrop(target, e, id);
20212         if(this.cachedTarget){
20213             if(this.cachedTarget.isNotifyTarget){
20214                 this.cachedTarget.notifyOut(this, e, this.dragData);
20215             }
20216             this.cacheTarget = null;
20217         }
20218         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20219
20220         if(this.afterInvalidDrop){
20221             /**
20222              * An empty function by default, but provided so that you can perform a custom action
20223              * after an invalid drop has occurred by providing an implementation.
20224              * @param {Event} e The event object
20225              * @param {String} id The id of the dropped element
20226              * @method afterInvalidDrop
20227              */
20228             this.afterInvalidDrop(e, id);
20229         }
20230     },
20231
20232     // private
20233     afterRepair : function(){
20234         if(Roo.enableFx){
20235             this.el.highlight(this.hlColor || "c3daf9");
20236         }
20237         this.dragging = false;
20238     },
20239
20240     /**
20241      * An empty function by default, but provided so that you can perform a custom action after an invalid
20242      * drop has occurred.
20243      * @param {Roo.dd.DragDrop} target The drop target
20244      * @param {Event} e The event object
20245      * @param {String} id The id of the dragged element
20246      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20247      */
20248     beforeInvalidDrop : function(target, e, id){
20249         return true;
20250     },
20251
20252     // private
20253     handleMouseDown : function(e){
20254         if(this.dragging) {
20255             return;
20256         }
20257         var data = this.getDragData(e);
20258         if(data && this.onBeforeDrag(data, e) !== false){
20259             this.dragData = data;
20260             this.proxy.stop();
20261             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20262         } 
20263     },
20264
20265     /**
20266      * An empty function by default, but provided so that you can perform a custom action before the initial
20267      * drag event begins and optionally cancel it.
20268      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20269      * @param {Event} e The event object
20270      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20271      */
20272     onBeforeDrag : function(data, e){
20273         return true;
20274     },
20275
20276     /**
20277      * An empty function by default, but provided so that you can perform a custom action once the initial
20278      * drag event has begun.  The drag cannot be canceled from this function.
20279      * @param {Number} x The x position of the click on the dragged object
20280      * @param {Number} y The y position of the click on the dragged object
20281      */
20282     onStartDrag : Roo.emptyFn,
20283
20284     // private - YUI override
20285     startDrag : function(x, y){
20286         this.proxy.reset();
20287         this.dragging = true;
20288         this.proxy.update("");
20289         this.onInitDrag(x, y);
20290         this.proxy.show();
20291     },
20292
20293     // private
20294     onInitDrag : function(x, y){
20295         var clone = this.el.dom.cloneNode(true);
20296         clone.id = Roo.id(); // prevent duplicate ids
20297         this.proxy.update(clone);
20298         this.onStartDrag(x, y);
20299         return true;
20300     },
20301
20302     /**
20303      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20304      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20305      */
20306     getProxy : function(){
20307         return this.proxy;  
20308     },
20309
20310     /**
20311      * Hides the drag source's {@link Roo.dd.StatusProxy}
20312      */
20313     hideProxy : function(){
20314         this.proxy.hide();  
20315         this.proxy.reset(true);
20316         this.dragging = false;
20317     },
20318
20319     // private
20320     triggerCacheRefresh : function(){
20321         Roo.dd.DDM.refreshCache(this.groups);
20322     },
20323
20324     // private - override to prevent hiding
20325     b4EndDrag: function(e) {
20326     },
20327
20328     // private - override to prevent moving
20329     endDrag : function(e){
20330         this.onEndDrag(this.dragData, e);
20331     },
20332
20333     // private
20334     onEndDrag : function(data, e){
20335     },
20336     
20337     // private - pin to cursor
20338     autoOffset : function(x, y) {
20339         this.setDelta(-12, -20);
20340     }    
20341 });/*
20342  * Based on:
20343  * Ext JS Library 1.1.1
20344  * Copyright(c) 2006-2007, Ext JS, LLC.
20345  *
20346  * Originally Released Under LGPL - original licence link has changed is not relivant.
20347  *
20348  * Fork - LGPL
20349  * <script type="text/javascript">
20350  */
20351
20352
20353 /**
20354  * @class Roo.dd.DropTarget
20355  * @extends Roo.dd.DDTarget
20356  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20357  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20358  * @constructor
20359  * @param {String/HTMLElement/Element} el The container element
20360  * @param {Object} config
20361  */
20362 Roo.dd.DropTarget = function(el, config){
20363     this.el = Roo.get(el);
20364     
20365     var listeners = false; ;
20366     if (config && config.listeners) {
20367         listeners= config.listeners;
20368         delete config.listeners;
20369     }
20370     Roo.apply(this, config);
20371     
20372     if(this.containerScroll){
20373         Roo.dd.ScrollManager.register(this.el);
20374     }
20375     this.addEvents( {
20376          /**
20377          * @scope Roo.dd.DropTarget
20378          */
20379          
20380          /**
20381          * @event enter
20382          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20383          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20384          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20385          * 
20386          * IMPORTANT : it should set this.overClass and this.dropAllowed
20387          * 
20388          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20389          * @param {Event} e The event
20390          * @param {Object} data An object containing arbitrary data supplied by the drag source
20391          */
20392         "enter" : true,
20393         
20394          /**
20395          * @event over
20396          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20397          * This method will be called on every mouse movement while the drag source is over the drop target.
20398          * This default implementation simply returns the dropAllowed config value.
20399          * 
20400          * IMPORTANT : it should set this.dropAllowed
20401          * 
20402          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20403          * @param {Event} e The event
20404          * @param {Object} data An object containing arbitrary data supplied by the drag source
20405          
20406          */
20407         "over" : true,
20408         /**
20409          * @event out
20410          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20411          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20412          * overClass (if any) from the drop element.
20413          * 
20414          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20415          * @param {Event} e The event
20416          * @param {Object} data An object containing arbitrary data supplied by the drag source
20417          */
20418          "out" : true,
20419          
20420         /**
20421          * @event drop
20422          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20423          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20424          * implementation that does something to process the drop event and returns true so that the drag source's
20425          * repair action does not run.
20426          * 
20427          * IMPORTANT : it should set this.success
20428          * 
20429          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20430          * @param {Event} e The event
20431          * @param {Object} data An object containing arbitrary data supplied by the drag source
20432         */
20433          "drop" : true
20434     });
20435             
20436      
20437     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20438         this.el.dom, 
20439         this.ddGroup || this.group,
20440         {
20441             isTarget: true,
20442             listeners : listeners || {} 
20443            
20444         
20445         }
20446     );
20447
20448 };
20449
20450 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20451     /**
20452      * @cfg {String} overClass
20453      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20454      */
20455      /**
20456      * @cfg {String} ddGroup
20457      * The drag drop group to handle drop events for
20458      */
20459      
20460     /**
20461      * @cfg {String} dropAllowed
20462      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20463      */
20464     dropAllowed : "x-dd-drop-ok",
20465     /**
20466      * @cfg {String} dropNotAllowed
20467      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20468      */
20469     dropNotAllowed : "x-dd-drop-nodrop",
20470     /**
20471      * @cfg {boolean} success
20472      * set this after drop listener.. 
20473      */
20474     success : false,
20475     /**
20476      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20477      * if the drop point is valid for over/enter..
20478      */
20479     valid : false,
20480     // private
20481     isTarget : true,
20482
20483     // private
20484     isNotifyTarget : true,
20485     
20486     /**
20487      * @hide
20488      */
20489     notifyEnter : function(dd, e, data)
20490     {
20491         this.valid = true;
20492         this.fireEvent('enter', dd, e, data);
20493         if(this.overClass){
20494             this.el.addClass(this.overClass);
20495         }
20496         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20497             this.valid ? this.dropAllowed : this.dropNotAllowed
20498         );
20499     },
20500
20501     /**
20502      * @hide
20503      */
20504     notifyOver : function(dd, e, data)
20505     {
20506         this.valid = true;
20507         this.fireEvent('over', dd, e, data);
20508         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20509             this.valid ? this.dropAllowed : this.dropNotAllowed
20510         );
20511     },
20512
20513     /**
20514      * @hide
20515      */
20516     notifyOut : function(dd, e, data)
20517     {
20518         this.fireEvent('out', dd, e, data);
20519         if(this.overClass){
20520             this.el.removeClass(this.overClass);
20521         }
20522     },
20523
20524     /**
20525      * @hide
20526      */
20527     notifyDrop : function(dd, e, data)
20528     {
20529         this.success = false;
20530         this.fireEvent('drop', dd, e, data);
20531         return this.success;
20532     }
20533 });/*
20534  * Based on:
20535  * Ext JS Library 1.1.1
20536  * Copyright(c) 2006-2007, Ext JS, LLC.
20537  *
20538  * Originally Released Under LGPL - original licence link has changed is not relivant.
20539  *
20540  * Fork - LGPL
20541  * <script type="text/javascript">
20542  */
20543
20544
20545 /**
20546  * @class Roo.dd.DragZone
20547  * @extends Roo.dd.DragSource
20548  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20549  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20550  * @constructor
20551  * @param {String/HTMLElement/Element} el The container element
20552  * @param {Object} config
20553  */
20554 Roo.dd.DragZone = function(el, config){
20555     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20556     if(this.containerScroll){
20557         Roo.dd.ScrollManager.register(this.el);
20558     }
20559 };
20560
20561 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20562     /**
20563      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20564      * for auto scrolling during drag operations.
20565      */
20566     /**
20567      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20568      * method after a failed drop (defaults to "c3daf9" - light blue)
20569      */
20570
20571     /**
20572      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20573      * for a valid target to drag based on the mouse down. Override this method
20574      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20575      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20576      * @param {EventObject} e The mouse down event
20577      * @return {Object} The dragData
20578      */
20579     getDragData : function(e){
20580         return Roo.dd.Registry.getHandleFromEvent(e);
20581     },
20582     
20583     /**
20584      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20585      * this.dragData.ddel
20586      * @param {Number} x The x position of the click on the dragged object
20587      * @param {Number} y The y position of the click on the dragged object
20588      * @return {Boolean} true to continue the drag, false to cancel
20589      */
20590     onInitDrag : function(x, y){
20591         this.proxy.update(this.dragData.ddel.cloneNode(true));
20592         this.onStartDrag(x, y);
20593         return true;
20594     },
20595     
20596     /**
20597      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20598      */
20599     afterRepair : function(){
20600         if(Roo.enableFx){
20601             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20602         }
20603         this.dragging = false;
20604     },
20605
20606     /**
20607      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20608      * the XY of this.dragData.ddel
20609      * @param {EventObject} e The mouse up event
20610      * @return {Array} The xy location (e.g. [100, 200])
20611      */
20612     getRepairXY : function(e){
20613         return Roo.Element.fly(this.dragData.ddel).getXY();  
20614     }
20615 });/*
20616  * Based on:
20617  * Ext JS Library 1.1.1
20618  * Copyright(c) 2006-2007, Ext JS, LLC.
20619  *
20620  * Originally Released Under LGPL - original licence link has changed is not relivant.
20621  *
20622  * Fork - LGPL
20623  * <script type="text/javascript">
20624  */
20625 /**
20626  * @class Roo.dd.DropZone
20627  * @extends Roo.dd.DropTarget
20628  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20629  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20630  * @constructor
20631  * @param {String/HTMLElement/Element} el The container element
20632  * @param {Object} config
20633  */
20634 Roo.dd.DropZone = function(el, config){
20635     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20636 };
20637
20638 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20639     /**
20640      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20641      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20642      * provide your own custom lookup.
20643      * @param {Event} e The event
20644      * @return {Object} data The custom data
20645      */
20646     getTargetFromEvent : function(e){
20647         return Roo.dd.Registry.getTargetFromEvent(e);
20648     },
20649
20650     /**
20651      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20652      * that it has registered.  This method has no default implementation and should be overridden to provide
20653      * node-specific processing if necessary.
20654      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20655      * {@link #getTargetFromEvent} for this node)
20656      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20657      * @param {Event} e The event
20658      * @param {Object} data An object containing arbitrary data supplied by the drag source
20659      */
20660     onNodeEnter : function(n, dd, e, data){
20661         
20662     },
20663
20664     /**
20665      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20666      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20667      * overridden to provide the proper feedback.
20668      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20669      * {@link #getTargetFromEvent} for this node)
20670      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20671      * @param {Event} e The event
20672      * @param {Object} data An object containing arbitrary data supplied by the drag source
20673      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20674      * underlying {@link Roo.dd.StatusProxy} can be updated
20675      */
20676     onNodeOver : function(n, dd, e, data){
20677         return this.dropAllowed;
20678     },
20679
20680     /**
20681      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20682      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20683      * node-specific processing if necessary.
20684      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20685      * {@link #getTargetFromEvent} for this node)
20686      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20687      * @param {Event} e The event
20688      * @param {Object} data An object containing arbitrary data supplied by the drag source
20689      */
20690     onNodeOut : function(n, dd, e, data){
20691         
20692     },
20693
20694     /**
20695      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20696      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20697      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20698      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20699      * {@link #getTargetFromEvent} for this node)
20700      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20701      * @param {Event} e The event
20702      * @param {Object} data An object containing arbitrary data supplied by the drag source
20703      * @return {Boolean} True if the drop was valid, else false
20704      */
20705     onNodeDrop : function(n, dd, e, data){
20706         return false;
20707     },
20708
20709     /**
20710      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20711      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20712      * it should be overridden to provide the proper feedback if necessary.
20713      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20714      * @param {Event} e The event
20715      * @param {Object} data An object containing arbitrary data supplied by the drag source
20716      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20717      * underlying {@link Roo.dd.StatusProxy} can be updated
20718      */
20719     onContainerOver : function(dd, e, data){
20720         return this.dropNotAllowed;
20721     },
20722
20723     /**
20724      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20725      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20726      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20727      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20728      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20729      * @param {Event} e The event
20730      * @param {Object} data An object containing arbitrary data supplied by the drag source
20731      * @return {Boolean} True if the drop was valid, else false
20732      */
20733     onContainerDrop : function(dd, e, data){
20734         return false;
20735     },
20736
20737     /**
20738      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20739      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20740      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20741      * you should override this method and provide a custom implementation.
20742      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20743      * @param {Event} e The event
20744      * @param {Object} data An object containing arbitrary data supplied by the drag source
20745      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20746      * underlying {@link Roo.dd.StatusProxy} can be updated
20747      */
20748     notifyEnter : function(dd, e, data){
20749         return this.dropNotAllowed;
20750     },
20751
20752     /**
20753      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20754      * This method will be called on every mouse movement while the drag source is over the drop zone.
20755      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20756      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20757      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20758      * registered node, it will call {@link #onContainerOver}.
20759      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20760      * @param {Event} e The event
20761      * @param {Object} data An object containing arbitrary data supplied by the drag source
20762      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20763      * underlying {@link Roo.dd.StatusProxy} can be updated
20764      */
20765     notifyOver : function(dd, e, data){
20766         var n = this.getTargetFromEvent(e);
20767         if(!n){ // not over valid drop target
20768             if(this.lastOverNode){
20769                 this.onNodeOut(this.lastOverNode, dd, e, data);
20770                 this.lastOverNode = null;
20771             }
20772             return this.onContainerOver(dd, e, data);
20773         }
20774         if(this.lastOverNode != n){
20775             if(this.lastOverNode){
20776                 this.onNodeOut(this.lastOverNode, dd, e, data);
20777             }
20778             this.onNodeEnter(n, dd, e, data);
20779             this.lastOverNode = n;
20780         }
20781         return this.onNodeOver(n, dd, e, data);
20782     },
20783
20784     /**
20785      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20786      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20787      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20788      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20789      * @param {Event} e The event
20790      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20791      */
20792     notifyOut : function(dd, e, data){
20793         if(this.lastOverNode){
20794             this.onNodeOut(this.lastOverNode, dd, e, data);
20795             this.lastOverNode = null;
20796         }
20797     },
20798
20799     /**
20800      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20801      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20802      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20803      * otherwise it will call {@link #onContainerDrop}.
20804      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20805      * @param {Event} e The event
20806      * @param {Object} data An object containing arbitrary data supplied by the drag source
20807      * @return {Boolean} True if the drop was valid, else false
20808      */
20809     notifyDrop : function(dd, e, data){
20810         if(this.lastOverNode){
20811             this.onNodeOut(this.lastOverNode, dd, e, data);
20812             this.lastOverNode = null;
20813         }
20814         var n = this.getTargetFromEvent(e);
20815         return n ?
20816             this.onNodeDrop(n, dd, e, data) :
20817             this.onContainerDrop(dd, e, data);
20818     },
20819
20820     // private
20821     triggerCacheRefresh : function(){
20822         Roo.dd.DDM.refreshCache(this.groups);
20823     }  
20824 });/*
20825  * Based on:
20826  * Ext JS Library 1.1.1
20827  * Copyright(c) 2006-2007, Ext JS, LLC.
20828  *
20829  * Originally Released Under LGPL - original licence link has changed is not relivant.
20830  *
20831  * Fork - LGPL
20832  * <script type="text/javascript">
20833  */
20834
20835
20836 /**
20837  * @class Roo.data.SortTypes
20838  * @singleton
20839  * Defines the default sorting (casting?) comparison functions used when sorting data.
20840  */
20841 Roo.data.SortTypes = {
20842     /**
20843      * Default sort that does nothing
20844      * @param {Mixed} s The value being converted
20845      * @return {Mixed} The comparison value
20846      */
20847     none : function(s){
20848         return s;
20849     },
20850     
20851     /**
20852      * The regular expression used to strip tags
20853      * @type {RegExp}
20854      * @property
20855      */
20856     stripTagsRE : /<\/?[^>]+>/gi,
20857     
20858     /**
20859      * Strips all HTML tags to sort on text only
20860      * @param {Mixed} s The value being converted
20861      * @return {String} The comparison value
20862      */
20863     asText : function(s){
20864         return String(s).replace(this.stripTagsRE, "");
20865     },
20866     
20867     /**
20868      * Strips all HTML tags to sort on text only - Case insensitive
20869      * @param {Mixed} s The value being converted
20870      * @return {String} The comparison value
20871      */
20872     asUCText : function(s){
20873         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20874     },
20875     
20876     /**
20877      * Case insensitive string
20878      * @param {Mixed} s The value being converted
20879      * @return {String} The comparison value
20880      */
20881     asUCString : function(s) {
20882         return String(s).toUpperCase();
20883     },
20884     
20885     /**
20886      * Date sorting
20887      * @param {Mixed} s The value being converted
20888      * @return {Number} The comparison value
20889      */
20890     asDate : function(s) {
20891         if(!s){
20892             return 0;
20893         }
20894         if(s instanceof Date){
20895             return s.getTime();
20896         }
20897         return Date.parse(String(s));
20898     },
20899     
20900     /**
20901      * Float sorting
20902      * @param {Mixed} s The value being converted
20903      * @return {Float} The comparison value
20904      */
20905     asFloat : function(s) {
20906         var val = parseFloat(String(s).replace(/,/g, ""));
20907         if(isNaN(val)) val = 0;
20908         return val;
20909     },
20910     
20911     /**
20912      * Integer sorting
20913      * @param {Mixed} s The value being converted
20914      * @return {Number} The comparison value
20915      */
20916     asInt : function(s) {
20917         var val = parseInt(String(s).replace(/,/g, ""));
20918         if(isNaN(val)) val = 0;
20919         return val;
20920     }
20921 };/*
20922  * Based on:
20923  * Ext JS Library 1.1.1
20924  * Copyright(c) 2006-2007, Ext JS, LLC.
20925  *
20926  * Originally Released Under LGPL - original licence link has changed is not relivant.
20927  *
20928  * Fork - LGPL
20929  * <script type="text/javascript">
20930  */
20931
20932 /**
20933 * @class Roo.data.Record
20934  * Instances of this class encapsulate both record <em>definition</em> information, and record
20935  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20936  * to access Records cached in an {@link Roo.data.Store} object.<br>
20937  * <p>
20938  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20939  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20940  * objects.<br>
20941  * <p>
20942  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20943  * @constructor
20944  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20945  * {@link #create}. The parameters are the same.
20946  * @param {Array} data An associative Array of data values keyed by the field name.
20947  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20948  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20949  * not specified an integer id is generated.
20950  */
20951 Roo.data.Record = function(data, id){
20952     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20953     this.data = data;
20954 };
20955
20956 /**
20957  * Generate a constructor for a specific record layout.
20958  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20959  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20960  * Each field definition object may contain the following properties: <ul>
20961  * <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,
20962  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20963  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20964  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20965  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20966  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20967  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20968  * this may be omitted.</p></li>
20969  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20970  * <ul><li>auto (Default, implies no conversion)</li>
20971  * <li>string</li>
20972  * <li>int</li>
20973  * <li>float</li>
20974  * <li>boolean</li>
20975  * <li>date</li></ul></p></li>
20976  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20977  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20978  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20979  * by the Reader into an object that will be stored in the Record. It is passed the
20980  * following parameters:<ul>
20981  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20982  * </ul></p></li>
20983  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20984  * </ul>
20985  * <br>usage:<br><pre><code>
20986 var TopicRecord = Roo.data.Record.create(
20987     {name: 'title', mapping: 'topic_title'},
20988     {name: 'author', mapping: 'username'},
20989     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20990     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20991     {name: 'lastPoster', mapping: 'user2'},
20992     {name: 'excerpt', mapping: 'post_text'}
20993 );
20994
20995 var myNewRecord = new TopicRecord({
20996     title: 'Do my job please',
20997     author: 'noobie',
20998     totalPosts: 1,
20999     lastPost: new Date(),
21000     lastPoster: 'Animal',
21001     excerpt: 'No way dude!'
21002 });
21003 myStore.add(myNewRecord);
21004 </code></pre>
21005  * @method create
21006  * @static
21007  */
21008 Roo.data.Record.create = function(o){
21009     var f = function(){
21010         f.superclass.constructor.apply(this, arguments);
21011     };
21012     Roo.extend(f, Roo.data.Record);
21013     var p = f.prototype;
21014     p.fields = new Roo.util.MixedCollection(false, function(field){
21015         return field.name;
21016     });
21017     for(var i = 0, len = o.length; i < len; i++){
21018         p.fields.add(new Roo.data.Field(o[i]));
21019     }
21020     f.getField = function(name){
21021         return p.fields.get(name);  
21022     };
21023     return f;
21024 };
21025
21026 Roo.data.Record.AUTO_ID = 1000;
21027 Roo.data.Record.EDIT = 'edit';
21028 Roo.data.Record.REJECT = 'reject';
21029 Roo.data.Record.COMMIT = 'commit';
21030
21031 Roo.data.Record.prototype = {
21032     /**
21033      * Readonly flag - true if this record has been modified.
21034      * @type Boolean
21035      */
21036     dirty : false,
21037     editing : false,
21038     error: null,
21039     modified: null,
21040
21041     // private
21042     join : function(store){
21043         this.store = store;
21044     },
21045
21046     /**
21047      * Set the named field to the specified value.
21048      * @param {String} name The name of the field to set.
21049      * @param {Object} value The value to set the field to.
21050      */
21051     set : function(name, value){
21052         if(this.data[name] == value){
21053             return;
21054         }
21055         this.dirty = true;
21056         if(!this.modified){
21057             this.modified = {};
21058         }
21059         if(typeof this.modified[name] == 'undefined'){
21060             this.modified[name] = this.data[name];
21061         }
21062         this.data[name] = value;
21063         if(!this.editing && this.store){
21064             this.store.afterEdit(this);
21065         }       
21066     },
21067
21068     /**
21069      * Get the value of the named field.
21070      * @param {String} name The name of the field to get the value of.
21071      * @return {Object} The value of the field.
21072      */
21073     get : function(name){
21074         return this.data[name]; 
21075     },
21076
21077     // private
21078     beginEdit : function(){
21079         this.editing = true;
21080         this.modified = {}; 
21081     },
21082
21083     // private
21084     cancelEdit : function(){
21085         this.editing = false;
21086         delete this.modified;
21087     },
21088
21089     // private
21090     endEdit : function(){
21091         this.editing = false;
21092         if(this.dirty && this.store){
21093             this.store.afterEdit(this);
21094         }
21095     },
21096
21097     /**
21098      * Usually called by the {@link Roo.data.Store} which owns the Record.
21099      * Rejects all changes made to the Record since either creation, or the last commit operation.
21100      * Modified fields are reverted to their original values.
21101      * <p>
21102      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21103      * of reject operations.
21104      */
21105     reject : function(){
21106         var m = this.modified;
21107         for(var n in m){
21108             if(typeof m[n] != "function"){
21109                 this.data[n] = m[n];
21110             }
21111         }
21112         this.dirty = false;
21113         delete this.modified;
21114         this.editing = false;
21115         if(this.store){
21116             this.store.afterReject(this);
21117         }
21118     },
21119
21120     /**
21121      * Usually called by the {@link Roo.data.Store} which owns the Record.
21122      * Commits all changes made to the Record since either creation, or the last commit operation.
21123      * <p>
21124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21125      * of commit operations.
21126      */
21127     commit : function(){
21128         this.dirty = false;
21129         delete this.modified;
21130         this.editing = false;
21131         if(this.store){
21132             this.store.afterCommit(this);
21133         }
21134     },
21135
21136     // private
21137     hasError : function(){
21138         return this.error != null;
21139     },
21140
21141     // private
21142     clearError : function(){
21143         this.error = null;
21144     },
21145
21146     /**
21147      * Creates a copy of this record.
21148      * @param {String} id (optional) A new record id if you don't want to use this record's id
21149      * @return {Record}
21150      */
21151     copy : function(newId) {
21152         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21153     }
21154 };/*
21155  * Based on:
21156  * Ext JS Library 1.1.1
21157  * Copyright(c) 2006-2007, Ext JS, LLC.
21158  *
21159  * Originally Released Under LGPL - original licence link has changed is not relivant.
21160  *
21161  * Fork - LGPL
21162  * <script type="text/javascript">
21163  */
21164
21165
21166
21167 /**
21168  * @class Roo.data.Store
21169  * @extends Roo.util.Observable
21170  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21171  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21172  * <p>
21173  * 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
21174  * has no knowledge of the format of the data returned by the Proxy.<br>
21175  * <p>
21176  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21177  * instances from the data object. These records are cached and made available through accessor functions.
21178  * @constructor
21179  * Creates a new Store.
21180  * @param {Object} config A config object containing the objects needed for the Store to access data,
21181  * and read the data into Records.
21182  */
21183 Roo.data.Store = function(config){
21184     this.data = new Roo.util.MixedCollection(false);
21185     this.data.getKey = function(o){
21186         return o.id;
21187     };
21188     this.baseParams = {};
21189     // private
21190     this.paramNames = {
21191         "start" : "start",
21192         "limit" : "limit",
21193         "sort" : "sort",
21194         "dir" : "dir",
21195         "multisort" : "_multisort"
21196     };
21197
21198     if(config && config.data){
21199         this.inlineData = config.data;
21200         delete config.data;
21201     }
21202
21203     Roo.apply(this, config);
21204     
21205     if(this.reader){ // reader passed
21206         this.reader = Roo.factory(this.reader, Roo.data);
21207         this.reader.xmodule = this.xmodule || false;
21208         if(!this.recordType){
21209             this.recordType = this.reader.recordType;
21210         }
21211         if(this.reader.onMetaChange){
21212             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21213         }
21214     }
21215
21216     if(this.recordType){
21217         this.fields = this.recordType.prototype.fields;
21218     }
21219     this.modified = [];
21220
21221     this.addEvents({
21222         /**
21223          * @event datachanged
21224          * Fires when the data cache has changed, and a widget which is using this Store
21225          * as a Record cache should refresh its view.
21226          * @param {Store} this
21227          */
21228         datachanged : true,
21229         /**
21230          * @event metachange
21231          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21232          * @param {Store} this
21233          * @param {Object} meta The JSON metadata
21234          */
21235         metachange : true,
21236         /**
21237          * @event add
21238          * Fires when Records have been added to the Store
21239          * @param {Store} this
21240          * @param {Roo.data.Record[]} records The array of Records added
21241          * @param {Number} index The index at which the record(s) were added
21242          */
21243         add : true,
21244         /**
21245          * @event remove
21246          * Fires when a Record has been removed from the Store
21247          * @param {Store} this
21248          * @param {Roo.data.Record} record The Record that was removed
21249          * @param {Number} index The index at which the record was removed
21250          */
21251         remove : true,
21252         /**
21253          * @event update
21254          * Fires when a Record has been updated
21255          * @param {Store} this
21256          * @param {Roo.data.Record} record The Record that was updated
21257          * @param {String} operation The update operation being performed.  Value may be one of:
21258          * <pre><code>
21259  Roo.data.Record.EDIT
21260  Roo.data.Record.REJECT
21261  Roo.data.Record.COMMIT
21262          * </code></pre>
21263          */
21264         update : true,
21265         /**
21266          * @event clear
21267          * Fires when the data cache has been cleared.
21268          * @param {Store} this
21269          */
21270         clear : true,
21271         /**
21272          * @event beforeload
21273          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21274          * the load action will be canceled.
21275          * @param {Store} this
21276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21277          */
21278         beforeload : true,
21279         /**
21280          * @event beforeloadadd
21281          * Fires after a new set of Records has been loaded.
21282          * @param {Store} this
21283          * @param {Roo.data.Record[]} records The Records that were loaded
21284          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21285          */
21286         beforeloadadd : true,
21287         /**
21288          * @event load
21289          * Fires after a new set of Records has been loaded, before they are added to the store.
21290          * @param {Store} this
21291          * @param {Roo.data.Record[]} records The Records that were loaded
21292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21293          * @params {Object} return from reader
21294          */
21295         load : true,
21296         /**
21297          * @event loadexception
21298          * Fires if an exception occurs in the Proxy during loading.
21299          * Called with the signature of the Proxy's "loadexception" event.
21300          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21301          * 
21302          * @param {Proxy} 
21303          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21304          * @param {Object} load options 
21305          * @param {Object} jsonData from your request (normally this contains the Exception)
21306          */
21307         loadexception : true
21308     });
21309     
21310     if(this.proxy){
21311         this.proxy = Roo.factory(this.proxy, Roo.data);
21312         this.proxy.xmodule = this.xmodule || false;
21313         this.relayEvents(this.proxy,  ["loadexception"]);
21314     }
21315     this.sortToggle = {};
21316     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21317
21318     Roo.data.Store.superclass.constructor.call(this);
21319
21320     if(this.inlineData){
21321         this.loadData(this.inlineData);
21322         delete this.inlineData;
21323     }
21324 };
21325
21326 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21327      /**
21328     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21329     * without a remote query - used by combo/forms at present.
21330     */
21331     
21332     /**
21333     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21334     */
21335     /**
21336     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21337     */
21338     /**
21339     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21340     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21341     */
21342     /**
21343     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21344     * on any HTTP request
21345     */
21346     /**
21347     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21348     */
21349     /**
21350     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21351     */
21352     multiSort: false,
21353     /**
21354     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21355     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21356     */
21357     remoteSort : false,
21358
21359     /**
21360     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21361      * loaded or when a record is removed. (defaults to false).
21362     */
21363     pruneModifiedRecords : false,
21364
21365     // private
21366     lastOptions : null,
21367
21368     /**
21369      * Add Records to the Store and fires the add event.
21370      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21371      */
21372     add : function(records){
21373         records = [].concat(records);
21374         for(var i = 0, len = records.length; i < len; i++){
21375             records[i].join(this);
21376         }
21377         var index = this.data.length;
21378         this.data.addAll(records);
21379         this.fireEvent("add", this, records, index);
21380     },
21381
21382     /**
21383      * Remove a Record from the Store and fires the remove event.
21384      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21385      */
21386     remove : function(record){
21387         var index = this.data.indexOf(record);
21388         this.data.removeAt(index);
21389         if(this.pruneModifiedRecords){
21390             this.modified.remove(record);
21391         }
21392         this.fireEvent("remove", this, record, index);
21393     },
21394
21395     /**
21396      * Remove all Records from the Store and fires the clear event.
21397      */
21398     removeAll : function(){
21399         this.data.clear();
21400         if(this.pruneModifiedRecords){
21401             this.modified = [];
21402         }
21403         this.fireEvent("clear", this);
21404     },
21405
21406     /**
21407      * Inserts Records to the Store at the given index and fires the add event.
21408      * @param {Number} index The start index at which to insert the passed Records.
21409      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21410      */
21411     insert : function(index, records){
21412         records = [].concat(records);
21413         for(var i = 0, len = records.length; i < len; i++){
21414             this.data.insert(index, records[i]);
21415             records[i].join(this);
21416         }
21417         this.fireEvent("add", this, records, index);
21418     },
21419
21420     /**
21421      * Get the index within the cache of the passed Record.
21422      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21423      * @return {Number} The index of the passed Record. Returns -1 if not found.
21424      */
21425     indexOf : function(record){
21426         return this.data.indexOf(record);
21427     },
21428
21429     /**
21430      * Get the index within the cache of the Record with the passed id.
21431      * @param {String} id The id of the Record to find.
21432      * @return {Number} The index of the Record. Returns -1 if not found.
21433      */
21434     indexOfId : function(id){
21435         return this.data.indexOfKey(id);
21436     },
21437
21438     /**
21439      * Get the Record with the specified id.
21440      * @param {String} id The id of the Record to find.
21441      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21442      */
21443     getById : function(id){
21444         return this.data.key(id);
21445     },
21446
21447     /**
21448      * Get the Record at the specified index.
21449      * @param {Number} index The index of the Record to find.
21450      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21451      */
21452     getAt : function(index){
21453         return this.data.itemAt(index);
21454     },
21455
21456     /**
21457      * Returns a range of Records between specified indices.
21458      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21459      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21460      * @return {Roo.data.Record[]} An array of Records
21461      */
21462     getRange : function(start, end){
21463         return this.data.getRange(start, end);
21464     },
21465
21466     // private
21467     storeOptions : function(o){
21468         o = Roo.apply({}, o);
21469         delete o.callback;
21470         delete o.scope;
21471         this.lastOptions = o;
21472     },
21473
21474     /**
21475      * Loads the Record cache from the configured Proxy using the configured Reader.
21476      * <p>
21477      * If using remote paging, then the first load call must specify the <em>start</em>
21478      * and <em>limit</em> properties in the options.params property to establish the initial
21479      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21480      * <p>
21481      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21482      * and this call will return before the new data has been loaded. Perform any post-processing
21483      * in a callback function, or in a "load" event handler.</strong>
21484      * <p>
21485      * @param {Object} options An object containing properties which control loading options:<ul>
21486      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21487      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21488      * passed the following arguments:<ul>
21489      * <li>r : Roo.data.Record[]</li>
21490      * <li>options: Options object from the load call</li>
21491      * <li>success: Boolean success indicator</li></ul></li>
21492      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21493      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21494      * </ul>
21495      */
21496     load : function(options){
21497         options = options || {};
21498         if(this.fireEvent("beforeload", this, options) !== false){
21499             this.storeOptions(options);
21500             var p = Roo.apply(options.params || {}, this.baseParams);
21501             // if meta was not loaded from remote source.. try requesting it.
21502             if (!this.reader.metaFromRemote) {
21503                 p._requestMeta = 1;
21504             }
21505             if(this.sortInfo && this.remoteSort){
21506                 var pn = this.paramNames;
21507                 p[pn["sort"]] = this.sortInfo.field;
21508                 p[pn["dir"]] = this.sortInfo.direction;
21509             }
21510             if (this.multiSort) {
21511                 var pn = this.paramNames;
21512                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21513             }
21514             
21515             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21516         }
21517     },
21518
21519     /**
21520      * Reloads the Record cache from the configured Proxy using the configured Reader and
21521      * the options from the last load operation performed.
21522      * @param {Object} options (optional) An object containing properties which may override the options
21523      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21524      * the most recently used options are reused).
21525      */
21526     reload : function(options){
21527         this.load(Roo.applyIf(options||{}, this.lastOptions));
21528     },
21529
21530     // private
21531     // Called as a callback by the Reader during a load operation.
21532     loadRecords : function(o, options, success){
21533         if(!o || success === false){
21534             if(success !== false){
21535                 this.fireEvent("load", this, [], options, o);
21536             }
21537             if(options.callback){
21538                 options.callback.call(options.scope || this, [], options, false);
21539             }
21540             return;
21541         }
21542         // if data returned failure - throw an exception.
21543         if (o.success === false) {
21544             // show a message if no listener is registered.
21545             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21546                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21547             }
21548             // loadmask wil be hooked into this..
21549             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21550             return;
21551         }
21552         var r = o.records, t = o.totalRecords || r.length;
21553         
21554         this.fireEvent("beforeloadadd", this, r, options, o);
21555         
21556         if(!options || options.add !== true){
21557             if(this.pruneModifiedRecords){
21558                 this.modified = [];
21559             }
21560             for(var i = 0, len = r.length; i < len; i++){
21561                 r[i].join(this);
21562             }
21563             if(this.snapshot){
21564                 this.data = this.snapshot;
21565                 delete this.snapshot;
21566             }
21567             this.data.clear();
21568             this.data.addAll(r);
21569             this.totalLength = t;
21570             this.applySort();
21571             this.fireEvent("datachanged", this);
21572         }else{
21573             this.totalLength = Math.max(t, this.data.length+r.length);
21574             this.add(r);
21575         }
21576         this.fireEvent("load", this, r, options, o);
21577         if(options.callback){
21578             options.callback.call(options.scope || this, r, options, true);
21579         }
21580     },
21581
21582
21583     /**
21584      * Loads data from a passed data block. A Reader which understands the format of the data
21585      * must have been configured in the constructor.
21586      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21587      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21588      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21589      */
21590     loadData : function(o, append){
21591         var r = this.reader.readRecords(o);
21592         this.loadRecords(r, {add: append}, true);
21593     },
21594
21595     /**
21596      * Gets the number of cached records.
21597      * <p>
21598      * <em>If using paging, this may not be the total size of the dataset. If the data object
21599      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21600      * the data set size</em>
21601      */
21602     getCount : function(){
21603         return this.data.length || 0;
21604     },
21605
21606     /**
21607      * Gets the total number of records in the dataset as returned by the server.
21608      * <p>
21609      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21610      * the dataset size</em>
21611      */
21612     getTotalCount : function(){
21613         return this.totalLength || 0;
21614     },
21615
21616     /**
21617      * Returns the sort state of the Store as an object with two properties:
21618      * <pre><code>
21619  field {String} The name of the field by which the Records are sorted
21620  direction {String} The sort order, "ASC" or "DESC"
21621      * </code></pre>
21622      */
21623     getSortState : function(){
21624         return this.sortInfo;
21625     },
21626
21627     // private
21628     applySort : function(){
21629         if(this.sortInfo && !this.remoteSort){
21630             var s = this.sortInfo, f = s.field;
21631             var st = this.fields.get(f).sortType;
21632             var fn = function(r1, r2){
21633                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21634                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21635             };
21636             this.data.sort(s.direction, fn);
21637             if(this.snapshot && this.snapshot != this.data){
21638                 this.snapshot.sort(s.direction, fn);
21639             }
21640         }
21641     },
21642
21643     /**
21644      * Sets the default sort column and order to be used by the next load operation.
21645      * @param {String} fieldName The name of the field to sort by.
21646      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21647      */
21648     setDefaultSort : function(field, dir){
21649         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21650     },
21651
21652     /**
21653      * Sort the Records.
21654      * If remote sorting is used, the sort is performed on the server, and the cache is
21655      * reloaded. If local sorting is used, the cache is sorted internally.
21656      * @param {String} fieldName The name of the field to sort by.
21657      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21658      */
21659     sort : function(fieldName, dir){
21660         var f = this.fields.get(fieldName);
21661         if(!dir){
21662             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21663             
21664             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21665                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21666             }else{
21667                 dir = f.sortDir;
21668             }
21669         }
21670         this.sortToggle[f.name] = dir;
21671         this.sortInfo = {field: f.name, direction: dir};
21672         if(!this.remoteSort){
21673             this.applySort();
21674             this.fireEvent("datachanged", this);
21675         }else{
21676             this.load(this.lastOptions);
21677         }
21678     },
21679
21680     /**
21681      * Calls the specified function for each of the Records in the cache.
21682      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21683      * Returning <em>false</em> aborts and exits the iteration.
21684      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21685      */
21686     each : function(fn, scope){
21687         this.data.each(fn, scope);
21688     },
21689
21690     /**
21691      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21692      * (e.g., during paging).
21693      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21694      */
21695     getModifiedRecords : function(){
21696         return this.modified;
21697     },
21698
21699     // private
21700     createFilterFn : function(property, value, anyMatch){
21701         if(!value.exec){ // not a regex
21702             value = String(value);
21703             if(value.length == 0){
21704                 return false;
21705             }
21706             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21707         }
21708         return function(r){
21709             return value.test(r.data[property]);
21710         };
21711     },
21712
21713     /**
21714      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21715      * @param {String} property A field on your records
21716      * @param {Number} start The record index to start at (defaults to 0)
21717      * @param {Number} end The last record index to include (defaults to length - 1)
21718      * @return {Number} The sum
21719      */
21720     sum : function(property, start, end){
21721         var rs = this.data.items, v = 0;
21722         start = start || 0;
21723         end = (end || end === 0) ? end : rs.length-1;
21724
21725         for(var i = start; i <= end; i++){
21726             v += (rs[i].data[property] || 0);
21727         }
21728         return v;
21729     },
21730
21731     /**
21732      * Filter the records by a specified property.
21733      * @param {String} field A field on your records
21734      * @param {String/RegExp} value Either a string that the field
21735      * should start with or a RegExp to test against the field
21736      * @param {Boolean} anyMatch True to match any part not just the beginning
21737      */
21738     filter : function(property, value, anyMatch){
21739         var fn = this.createFilterFn(property, value, anyMatch);
21740         return fn ? this.filterBy(fn) : this.clearFilter();
21741     },
21742
21743     /**
21744      * Filter by a function. The specified function will be called with each
21745      * record in this data source. If the function returns true the record is included,
21746      * otherwise it is filtered.
21747      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21748      * @param {Object} scope (optional) The scope of the function (defaults to this)
21749      */
21750     filterBy : function(fn, scope){
21751         this.snapshot = this.snapshot || this.data;
21752         this.data = this.queryBy(fn, scope||this);
21753         this.fireEvent("datachanged", this);
21754     },
21755
21756     /**
21757      * Query the records by a specified property.
21758      * @param {String} field A field on your records
21759      * @param {String/RegExp} value Either a string that the field
21760      * should start with or a RegExp to test against the field
21761      * @param {Boolean} anyMatch True to match any part not just the beginning
21762      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21763      */
21764     query : function(property, value, anyMatch){
21765         var fn = this.createFilterFn(property, value, anyMatch);
21766         return fn ? this.queryBy(fn) : this.data.clone();
21767     },
21768
21769     /**
21770      * Query by a function. The specified function will be called with each
21771      * record in this data source. If the function returns true the record is included
21772      * in the results.
21773      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21774      * @param {Object} scope (optional) The scope of the function (defaults to this)
21775       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21776      **/
21777     queryBy : function(fn, scope){
21778         var data = this.snapshot || this.data;
21779         return data.filterBy(fn, scope||this);
21780     },
21781
21782     /**
21783      * Collects unique values for a particular dataIndex from this store.
21784      * @param {String} dataIndex The property to collect
21785      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21786      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21787      * @return {Array} An array of the unique values
21788      **/
21789     collect : function(dataIndex, allowNull, bypassFilter){
21790         var d = (bypassFilter === true && this.snapshot) ?
21791                 this.snapshot.items : this.data.items;
21792         var v, sv, r = [], l = {};
21793         for(var i = 0, len = d.length; i < len; i++){
21794             v = d[i].data[dataIndex];
21795             sv = String(v);
21796             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21797                 l[sv] = true;
21798                 r[r.length] = v;
21799             }
21800         }
21801         return r;
21802     },
21803
21804     /**
21805      * Revert to a view of the Record cache with no filtering applied.
21806      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21807      */
21808     clearFilter : function(suppressEvent){
21809         if(this.snapshot && this.snapshot != this.data){
21810             this.data = this.snapshot;
21811             delete this.snapshot;
21812             if(suppressEvent !== true){
21813                 this.fireEvent("datachanged", this);
21814             }
21815         }
21816     },
21817
21818     // private
21819     afterEdit : function(record){
21820         if(this.modified.indexOf(record) == -1){
21821             this.modified.push(record);
21822         }
21823         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21824     },
21825     
21826     // private
21827     afterReject : function(record){
21828         this.modified.remove(record);
21829         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21830     },
21831
21832     // private
21833     afterCommit : function(record){
21834         this.modified.remove(record);
21835         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21836     },
21837
21838     /**
21839      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21840      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21841      */
21842     commitChanges : function(){
21843         var m = this.modified.slice(0);
21844         this.modified = [];
21845         for(var i = 0, len = m.length; i < len; i++){
21846             m[i].commit();
21847         }
21848     },
21849
21850     /**
21851      * Cancel outstanding changes on all changed records.
21852      */
21853     rejectChanges : function(){
21854         var m = this.modified.slice(0);
21855         this.modified = [];
21856         for(var i = 0, len = m.length; i < len; i++){
21857             m[i].reject();
21858         }
21859     },
21860
21861     onMetaChange : function(meta, rtype, o){
21862         this.recordType = rtype;
21863         this.fields = rtype.prototype.fields;
21864         delete this.snapshot;
21865         this.sortInfo = meta.sortInfo || this.sortInfo;
21866         this.modified = [];
21867         this.fireEvent('metachange', this, this.reader.meta);
21868     },
21869     
21870     moveIndex : function(data, type)
21871     {
21872         var index = this.indexOf(data);
21873         
21874         var newIndex = index + type;
21875         
21876         this.remove(data);
21877         
21878         this.insert(newIndex, data);
21879         
21880     }
21881 });/*
21882  * Based on:
21883  * Ext JS Library 1.1.1
21884  * Copyright(c) 2006-2007, Ext JS, LLC.
21885  *
21886  * Originally Released Under LGPL - original licence link has changed is not relivant.
21887  *
21888  * Fork - LGPL
21889  * <script type="text/javascript">
21890  */
21891
21892 /**
21893  * @class Roo.data.SimpleStore
21894  * @extends Roo.data.Store
21895  * Small helper class to make creating Stores from Array data easier.
21896  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21897  * @cfg {Array} fields An array of field definition objects, or field name strings.
21898  * @cfg {Array} data The multi-dimensional array of data
21899  * @constructor
21900  * @param {Object} config
21901  */
21902 Roo.data.SimpleStore = function(config){
21903     Roo.data.SimpleStore.superclass.constructor.call(this, {
21904         isLocal : true,
21905         reader: new Roo.data.ArrayReader({
21906                 id: config.id
21907             },
21908             Roo.data.Record.create(config.fields)
21909         ),
21910         proxy : new Roo.data.MemoryProxy(config.data)
21911     });
21912     this.load();
21913 };
21914 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21915  * Based on:
21916  * Ext JS Library 1.1.1
21917  * Copyright(c) 2006-2007, Ext JS, LLC.
21918  *
21919  * Originally Released Under LGPL - original licence link has changed is not relivant.
21920  *
21921  * Fork - LGPL
21922  * <script type="text/javascript">
21923  */
21924
21925 /**
21926 /**
21927  * @extends Roo.data.Store
21928  * @class Roo.data.JsonStore
21929  * Small helper class to make creating Stores for JSON data easier. <br/>
21930 <pre><code>
21931 var store = new Roo.data.JsonStore({
21932     url: 'get-images.php',
21933     root: 'images',
21934     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21935 });
21936 </code></pre>
21937  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21938  * JsonReader and HttpProxy (unless inline data is provided).</b>
21939  * @cfg {Array} fields An array of field definition objects, or field name strings.
21940  * @constructor
21941  * @param {Object} config
21942  */
21943 Roo.data.JsonStore = function(c){
21944     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21945         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21946         reader: new Roo.data.JsonReader(c, c.fields)
21947     }));
21948 };
21949 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21950  * Based on:
21951  * Ext JS Library 1.1.1
21952  * Copyright(c) 2006-2007, Ext JS, LLC.
21953  *
21954  * Originally Released Under LGPL - original licence link has changed is not relivant.
21955  *
21956  * Fork - LGPL
21957  * <script type="text/javascript">
21958  */
21959
21960  
21961 Roo.data.Field = function(config){
21962     if(typeof config == "string"){
21963         config = {name: config};
21964     }
21965     Roo.apply(this, config);
21966     
21967     if(!this.type){
21968         this.type = "auto";
21969     }
21970     
21971     var st = Roo.data.SortTypes;
21972     // named sortTypes are supported, here we look them up
21973     if(typeof this.sortType == "string"){
21974         this.sortType = st[this.sortType];
21975     }
21976     
21977     // set default sortType for strings and dates
21978     if(!this.sortType){
21979         switch(this.type){
21980             case "string":
21981                 this.sortType = st.asUCString;
21982                 break;
21983             case "date":
21984                 this.sortType = st.asDate;
21985                 break;
21986             default:
21987                 this.sortType = st.none;
21988         }
21989     }
21990
21991     // define once
21992     var stripRe = /[\$,%]/g;
21993
21994     // prebuilt conversion function for this field, instead of
21995     // switching every time we're reading a value
21996     if(!this.convert){
21997         var cv, dateFormat = this.dateFormat;
21998         switch(this.type){
21999             case "":
22000             case "auto":
22001             case undefined:
22002                 cv = function(v){ return v; };
22003                 break;
22004             case "string":
22005                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22006                 break;
22007             case "int":
22008                 cv = function(v){
22009                     return v !== undefined && v !== null && v !== '' ?
22010                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22011                     };
22012                 break;
22013             case "float":
22014                 cv = function(v){
22015                     return v !== undefined && v !== null && v !== '' ?
22016                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22017                     };
22018                 break;
22019             case "bool":
22020             case "boolean":
22021                 cv = function(v){ return v === true || v === "true" || v == 1; };
22022                 break;
22023             case "date":
22024                 cv = function(v){
22025                     if(!v){
22026                         return '';
22027                     }
22028                     if(v instanceof Date){
22029                         return v;
22030                     }
22031                     if(dateFormat){
22032                         if(dateFormat == "timestamp"){
22033                             return new Date(v*1000);
22034                         }
22035                         return Date.parseDate(v, dateFormat);
22036                     }
22037                     var parsed = Date.parse(v);
22038                     return parsed ? new Date(parsed) : null;
22039                 };
22040              break;
22041             
22042         }
22043         this.convert = cv;
22044     }
22045 };
22046
22047 Roo.data.Field.prototype = {
22048     dateFormat: null,
22049     defaultValue: "",
22050     mapping: null,
22051     sortType : null,
22052     sortDir : "ASC"
22053 };/*
22054  * Based on:
22055  * Ext JS Library 1.1.1
22056  * Copyright(c) 2006-2007, Ext JS, LLC.
22057  *
22058  * Originally Released Under LGPL - original licence link has changed is not relivant.
22059  *
22060  * Fork - LGPL
22061  * <script type="text/javascript">
22062  */
22063  
22064 // Base class for reading structured data from a data source.  This class is intended to be
22065 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22066
22067 /**
22068  * @class Roo.data.DataReader
22069  * Base class for reading structured data from a data source.  This class is intended to be
22070  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22071  */
22072
22073 Roo.data.DataReader = function(meta, recordType){
22074     
22075     this.meta = meta;
22076     
22077     this.recordType = recordType instanceof Array ? 
22078         Roo.data.Record.create(recordType) : recordType;
22079 };
22080
22081 Roo.data.DataReader.prototype = {
22082      /**
22083      * Create an empty record
22084      * @param {Object} data (optional) - overlay some values
22085      * @return {Roo.data.Record} record created.
22086      */
22087     newRow :  function(d) {
22088         var da =  {};
22089         this.recordType.prototype.fields.each(function(c) {
22090             switch( c.type) {
22091                 case 'int' : da[c.name] = 0; break;
22092                 case 'date' : da[c.name] = new Date(); break;
22093                 case 'float' : da[c.name] = 0.0; break;
22094                 case 'boolean' : da[c.name] = false; break;
22095                 default : da[c.name] = ""; break;
22096             }
22097             
22098         });
22099         return new this.recordType(Roo.apply(da, d));
22100     }
22101     
22102 };/*
22103  * Based on:
22104  * Ext JS Library 1.1.1
22105  * Copyright(c) 2006-2007, Ext JS, LLC.
22106  *
22107  * Originally Released Under LGPL - original licence link has changed is not relivant.
22108  *
22109  * Fork - LGPL
22110  * <script type="text/javascript">
22111  */
22112
22113 /**
22114  * @class Roo.data.DataProxy
22115  * @extends Roo.data.Observable
22116  * This class is an abstract base class for implementations which provide retrieval of
22117  * unformatted data objects.<br>
22118  * <p>
22119  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22120  * (of the appropriate type which knows how to parse the data object) to provide a block of
22121  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22122  * <p>
22123  * Custom implementations must implement the load method as described in
22124  * {@link Roo.data.HttpProxy#load}.
22125  */
22126 Roo.data.DataProxy = function(){
22127     this.addEvents({
22128         /**
22129          * @event beforeload
22130          * Fires before a network request is made to retrieve a data object.
22131          * @param {Object} This DataProxy object.
22132          * @param {Object} params The params parameter to the load function.
22133          */
22134         beforeload : true,
22135         /**
22136          * @event load
22137          * Fires before the load method's callback is called.
22138          * @param {Object} This DataProxy object.
22139          * @param {Object} o The data object.
22140          * @param {Object} arg The callback argument object passed to the load function.
22141          */
22142         load : true,
22143         /**
22144          * @event loadexception
22145          * Fires if an Exception occurs during data retrieval.
22146          * @param {Object} This DataProxy object.
22147          * @param {Object} o The data object.
22148          * @param {Object} arg The callback argument object passed to the load function.
22149          * @param {Object} e The Exception.
22150          */
22151         loadexception : true
22152     });
22153     Roo.data.DataProxy.superclass.constructor.call(this);
22154 };
22155
22156 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22157
22158     /**
22159      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22160      */
22161 /*
22162  * Based on:
22163  * Ext JS Library 1.1.1
22164  * Copyright(c) 2006-2007, Ext JS, LLC.
22165  *
22166  * Originally Released Under LGPL - original licence link has changed is not relivant.
22167  *
22168  * Fork - LGPL
22169  * <script type="text/javascript">
22170  */
22171 /**
22172  * @class Roo.data.MemoryProxy
22173  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22174  * to the Reader when its load method is called.
22175  * @constructor
22176  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22177  */
22178 Roo.data.MemoryProxy = function(data){
22179     if (data.data) {
22180         data = data.data;
22181     }
22182     Roo.data.MemoryProxy.superclass.constructor.call(this);
22183     this.data = data;
22184 };
22185
22186 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22187     /**
22188      * Load data from the requested source (in this case an in-memory
22189      * data object passed to the constructor), read the data object into
22190      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22191      * process that block using the passed callback.
22192      * @param {Object} params This parameter is not used by the MemoryProxy class.
22193      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22194      * object into a block of Roo.data.Records.
22195      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22196      * The function must be passed <ul>
22197      * <li>The Record block object</li>
22198      * <li>The "arg" argument from the load function</li>
22199      * <li>A boolean success indicator</li>
22200      * </ul>
22201      * @param {Object} scope The scope in which to call the callback
22202      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22203      */
22204     load : function(params, reader, callback, scope, arg){
22205         params = params || {};
22206         var result;
22207         try {
22208             result = reader.readRecords(this.data);
22209         }catch(e){
22210             this.fireEvent("loadexception", this, arg, null, e);
22211             callback.call(scope, null, arg, false);
22212             return;
22213         }
22214         callback.call(scope, result, arg, true);
22215     },
22216     
22217     // private
22218     update : function(params, records){
22219         
22220     }
22221 });/*
22222  * Based on:
22223  * Ext JS Library 1.1.1
22224  * Copyright(c) 2006-2007, Ext JS, LLC.
22225  *
22226  * Originally Released Under LGPL - original licence link has changed is not relivant.
22227  *
22228  * Fork - LGPL
22229  * <script type="text/javascript">
22230  */
22231 /**
22232  * @class Roo.data.HttpProxy
22233  * @extends Roo.data.DataProxy
22234  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22235  * configured to reference a certain URL.<br><br>
22236  * <p>
22237  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22238  * from which the running page was served.<br><br>
22239  * <p>
22240  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22241  * <p>
22242  * Be aware that to enable the browser to parse an XML document, the server must set
22243  * the Content-Type header in the HTTP response to "text/xml".
22244  * @constructor
22245  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22246  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22247  * will be used to make the request.
22248  */
22249 Roo.data.HttpProxy = function(conn){
22250     Roo.data.HttpProxy.superclass.constructor.call(this);
22251     // is conn a conn config or a real conn?
22252     this.conn = conn;
22253     this.useAjax = !conn || !conn.events;
22254   
22255 };
22256
22257 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22258     // thse are take from connection...
22259     
22260     /**
22261      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22262      */
22263     /**
22264      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22265      * extra parameters to each request made by this object. (defaults to undefined)
22266      */
22267     /**
22268      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22269      *  to each request made by this object. (defaults to undefined)
22270      */
22271     /**
22272      * @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)
22273      */
22274     /**
22275      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22276      */
22277      /**
22278      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22279      * @type Boolean
22280      */
22281   
22282
22283     /**
22284      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22285      * @type Boolean
22286      */
22287     /**
22288      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22289      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22290      * a finer-grained basis than the DataProxy events.
22291      */
22292     getConnection : function(){
22293         return this.useAjax ? Roo.Ajax : this.conn;
22294     },
22295
22296     /**
22297      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22298      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22299      * process that block using the passed callback.
22300      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22301      * for the request to the remote server.
22302      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22303      * object into a block of Roo.data.Records.
22304      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22305      * The function must be passed <ul>
22306      * <li>The Record block object</li>
22307      * <li>The "arg" argument from the load function</li>
22308      * <li>A boolean success indicator</li>
22309      * </ul>
22310      * @param {Object} scope The scope in which to call the callback
22311      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22312      */
22313     load : function(params, reader, callback, scope, arg){
22314         if(this.fireEvent("beforeload", this, params) !== false){
22315             var  o = {
22316                 params : params || {},
22317                 request: {
22318                     callback : callback,
22319                     scope : scope,
22320                     arg : arg
22321                 },
22322                 reader: reader,
22323                 callback : this.loadResponse,
22324                 scope: this
22325             };
22326             if(this.useAjax){
22327                 Roo.applyIf(o, this.conn);
22328                 if(this.activeRequest){
22329                     Roo.Ajax.abort(this.activeRequest);
22330                 }
22331                 this.activeRequest = Roo.Ajax.request(o);
22332             }else{
22333                 this.conn.request(o);
22334             }
22335         }else{
22336             callback.call(scope||this, null, arg, false);
22337         }
22338     },
22339
22340     // private
22341     loadResponse : function(o, success, response){
22342         delete this.activeRequest;
22343         if(!success){
22344             this.fireEvent("loadexception", this, o, response);
22345             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22346             return;
22347         }
22348         var result;
22349         try {
22350             result = o.reader.read(response);
22351         }catch(e){
22352             this.fireEvent("loadexception", this, o, response, e);
22353             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22354             return;
22355         }
22356         
22357         this.fireEvent("load", this, o, o.request.arg);
22358         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22359     },
22360
22361     // private
22362     update : function(dataSet){
22363
22364     },
22365
22366     // private
22367     updateResponse : function(dataSet){
22368
22369     }
22370 });/*
22371  * Based on:
22372  * Ext JS Library 1.1.1
22373  * Copyright(c) 2006-2007, Ext JS, LLC.
22374  *
22375  * Originally Released Under LGPL - original licence link has changed is not relivant.
22376  *
22377  * Fork - LGPL
22378  * <script type="text/javascript">
22379  */
22380
22381 /**
22382  * @class Roo.data.ScriptTagProxy
22383  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22384  * other than the originating domain of the running page.<br><br>
22385  * <p>
22386  * <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
22387  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22388  * <p>
22389  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22390  * source code that is used as the source inside a &lt;script> tag.<br><br>
22391  * <p>
22392  * In order for the browser to process the returned data, the server must wrap the data object
22393  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22394  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22395  * depending on whether the callback name was passed:
22396  * <p>
22397  * <pre><code>
22398 boolean scriptTag = false;
22399 String cb = request.getParameter("callback");
22400 if (cb != null) {
22401     scriptTag = true;
22402     response.setContentType("text/javascript");
22403 } else {
22404     response.setContentType("application/x-json");
22405 }
22406 Writer out = response.getWriter();
22407 if (scriptTag) {
22408     out.write(cb + "(");
22409 }
22410 out.print(dataBlock.toJsonString());
22411 if (scriptTag) {
22412     out.write(");");
22413 }
22414 </pre></code>
22415  *
22416  * @constructor
22417  * @param {Object} config A configuration object.
22418  */
22419 Roo.data.ScriptTagProxy = function(config){
22420     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22421     Roo.apply(this, config);
22422     this.head = document.getElementsByTagName("head")[0];
22423 };
22424
22425 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22426
22427 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22428     /**
22429      * @cfg {String} url The URL from which to request the data object.
22430      */
22431     /**
22432      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22433      */
22434     timeout : 30000,
22435     /**
22436      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22437      * the server the name of the callback function set up by the load call to process the returned data object.
22438      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22439      * javascript output which calls this named function passing the data object as its only parameter.
22440      */
22441     callbackParam : "callback",
22442     /**
22443      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22444      * name to the request.
22445      */
22446     nocache : true,
22447
22448     /**
22449      * Load data from the configured URL, read the data object into
22450      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22451      * process that block using the passed callback.
22452      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22453      * for the request to the remote server.
22454      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22455      * object into a block of Roo.data.Records.
22456      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22457      * The function must be passed <ul>
22458      * <li>The Record block object</li>
22459      * <li>The "arg" argument from the load function</li>
22460      * <li>A boolean success indicator</li>
22461      * </ul>
22462      * @param {Object} scope The scope in which to call the callback
22463      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22464      */
22465     load : function(params, reader, callback, scope, arg){
22466         if(this.fireEvent("beforeload", this, params) !== false){
22467
22468             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22469
22470             var url = this.url;
22471             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22472             if(this.nocache){
22473                 url += "&_dc=" + (new Date().getTime());
22474             }
22475             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22476             var trans = {
22477                 id : transId,
22478                 cb : "stcCallback"+transId,
22479                 scriptId : "stcScript"+transId,
22480                 params : params,
22481                 arg : arg,
22482                 url : url,
22483                 callback : callback,
22484                 scope : scope,
22485                 reader : reader
22486             };
22487             var conn = this;
22488
22489             window[trans.cb] = function(o){
22490                 conn.handleResponse(o, trans);
22491             };
22492
22493             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22494
22495             if(this.autoAbort !== false){
22496                 this.abort();
22497             }
22498
22499             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22500
22501             var script = document.createElement("script");
22502             script.setAttribute("src", url);
22503             script.setAttribute("type", "text/javascript");
22504             script.setAttribute("id", trans.scriptId);
22505             this.head.appendChild(script);
22506
22507             this.trans = trans;
22508         }else{
22509             callback.call(scope||this, null, arg, false);
22510         }
22511     },
22512
22513     // private
22514     isLoading : function(){
22515         return this.trans ? true : false;
22516     },
22517
22518     /**
22519      * Abort the current server request.
22520      */
22521     abort : function(){
22522         if(this.isLoading()){
22523             this.destroyTrans(this.trans);
22524         }
22525     },
22526
22527     // private
22528     destroyTrans : function(trans, isLoaded){
22529         this.head.removeChild(document.getElementById(trans.scriptId));
22530         clearTimeout(trans.timeoutId);
22531         if(isLoaded){
22532             window[trans.cb] = undefined;
22533             try{
22534                 delete window[trans.cb];
22535             }catch(e){}
22536         }else{
22537             // if hasn't been loaded, wait for load to remove it to prevent script error
22538             window[trans.cb] = function(){
22539                 window[trans.cb] = undefined;
22540                 try{
22541                     delete window[trans.cb];
22542                 }catch(e){}
22543             };
22544         }
22545     },
22546
22547     // private
22548     handleResponse : function(o, trans){
22549         this.trans = false;
22550         this.destroyTrans(trans, true);
22551         var result;
22552         try {
22553             result = trans.reader.readRecords(o);
22554         }catch(e){
22555             this.fireEvent("loadexception", this, o, trans.arg, e);
22556             trans.callback.call(trans.scope||window, null, trans.arg, false);
22557             return;
22558         }
22559         this.fireEvent("load", this, o, trans.arg);
22560         trans.callback.call(trans.scope||window, result, trans.arg, true);
22561     },
22562
22563     // private
22564     handleFailure : function(trans){
22565         this.trans = false;
22566         this.destroyTrans(trans, false);
22567         this.fireEvent("loadexception", this, null, trans.arg);
22568         trans.callback.call(trans.scope||window, null, trans.arg, false);
22569     }
22570 });/*
22571  * Based on:
22572  * Ext JS Library 1.1.1
22573  * Copyright(c) 2006-2007, Ext JS, LLC.
22574  *
22575  * Originally Released Under LGPL - original licence link has changed is not relivant.
22576  *
22577  * Fork - LGPL
22578  * <script type="text/javascript">
22579  */
22580
22581 /**
22582  * @class Roo.data.JsonReader
22583  * @extends Roo.data.DataReader
22584  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22585  * based on mappings in a provided Roo.data.Record constructor.
22586  * 
22587  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22588  * in the reply previously. 
22589  * 
22590  * <p>
22591  * Example code:
22592  * <pre><code>
22593 var RecordDef = Roo.data.Record.create([
22594     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22595     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22596 ]);
22597 var myReader = new Roo.data.JsonReader({
22598     totalProperty: "results",    // The property which contains the total dataset size (optional)
22599     root: "rows",                // The property which contains an Array of row objects
22600     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22601 }, RecordDef);
22602 </code></pre>
22603  * <p>
22604  * This would consume a JSON file like this:
22605  * <pre><code>
22606 { 'results': 2, 'rows': [
22607     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22608     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22609 }
22610 </code></pre>
22611  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22612  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22613  * paged from the remote server.
22614  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22615  * @cfg {String} root name of the property which contains the Array of row objects.
22616  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22617  * @constructor
22618  * Create a new JsonReader
22619  * @param {Object} meta Metadata configuration options
22620  * @param {Object} recordType Either an Array of field definition objects,
22621  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22622  */
22623 Roo.data.JsonReader = function(meta, recordType){
22624     
22625     meta = meta || {};
22626     // set some defaults:
22627     Roo.applyIf(meta, {
22628         totalProperty: 'total',
22629         successProperty : 'success',
22630         root : 'data',
22631         id : 'id'
22632     });
22633     
22634     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22635 };
22636 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22637     
22638     /**
22639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22640      * Used by Store query builder to append _requestMeta to params.
22641      * 
22642      */
22643     metaFromRemote : false,
22644     /**
22645      * This method is only used by a DataProxy which has retrieved data from a remote server.
22646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22647      * @return {Object} data A data block which is used by an Roo.data.Store object as
22648      * a cache of Roo.data.Records.
22649      */
22650     read : function(response){
22651         var json = response.responseText;
22652        
22653         var o = /* eval:var:o */ eval("("+json+")");
22654         if(!o) {
22655             throw {message: "JsonReader.read: Json object not found"};
22656         }
22657         
22658         if(o.metaData){
22659             
22660             delete this.ef;
22661             this.metaFromRemote = true;
22662             this.meta = o.metaData;
22663             this.recordType = Roo.data.Record.create(o.metaData.fields);
22664             this.onMetaChange(this.meta, this.recordType, o);
22665         }
22666         return this.readRecords(o);
22667     },
22668
22669     // private function a store will implement
22670     onMetaChange : function(meta, recordType, o){
22671
22672     },
22673
22674     /**
22675          * @ignore
22676          */
22677     simpleAccess: function(obj, subsc) {
22678         return obj[subsc];
22679     },
22680
22681         /**
22682          * @ignore
22683          */
22684     getJsonAccessor: function(){
22685         var re = /[\[\.]/;
22686         return function(expr) {
22687             try {
22688                 return(re.test(expr))
22689                     ? new Function("obj", "return obj." + expr)
22690                     : function(obj){
22691                         return obj[expr];
22692                     };
22693             } catch(e){}
22694             return Roo.emptyFn;
22695         };
22696     }(),
22697
22698     /**
22699      * Create a data block containing Roo.data.Records from an XML document.
22700      * @param {Object} o An object which contains an Array of row objects in the property specified
22701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22702      * which contains the total size of the dataset.
22703      * @return {Object} data A data block which is used by an Roo.data.Store object as
22704      * a cache of Roo.data.Records.
22705      */
22706     readRecords : function(o){
22707         /**
22708          * After any data loads, the raw JSON data is available for further custom processing.
22709          * @type Object
22710          */
22711         this.o = o;
22712         var s = this.meta, Record = this.recordType,
22713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22714
22715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22716         if (!this.ef) {
22717             if(s.totalProperty) {
22718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22719                 }
22720                 if(s.successProperty) {
22721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22722                 }
22723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22724                 if (s.id) {
22725                         var g = this.getJsonAccessor(s.id);
22726                         this.getId = function(rec) {
22727                                 var r = g(rec);  
22728                                 return (r === undefined || r === "") ? null : r;
22729                         };
22730                 } else {
22731                         this.getId = function(){return null;};
22732                 }
22733             this.ef = [];
22734             for(var jj = 0; jj < fl; jj++){
22735                 f = fi[jj];
22736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22737                 this.ef[jj] = this.getJsonAccessor(map);
22738             }
22739         }
22740
22741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22742         if(s.totalProperty){
22743             var vt = parseInt(this.getTotal(o), 10);
22744             if(!isNaN(vt)){
22745                 totalRecords = vt;
22746             }
22747         }
22748         if(s.successProperty){
22749             var vs = this.getSuccess(o);
22750             if(vs === false || vs === 'false'){
22751                 success = false;
22752             }
22753         }
22754         var records = [];
22755         for(var i = 0; i < c; i++){
22756                 var n = root[i];
22757             var values = {};
22758             var id = this.getId(n);
22759             for(var j = 0; j < fl; j++){
22760                 f = fi[j];
22761             var v = this.ef[j](n);
22762             if (!f.convert) {
22763                 Roo.log('missing convert for ' + f.name);
22764                 Roo.log(f);
22765                 continue;
22766             }
22767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22768             }
22769             var record = new Record(values, id);
22770             record.json = n;
22771             records[i] = record;
22772         }
22773         return {
22774             raw : o,
22775             success : success,
22776             records : records,
22777             totalRecords : totalRecords
22778         };
22779     }
22780 });/*
22781  * Based on:
22782  * Ext JS Library 1.1.1
22783  * Copyright(c) 2006-2007, Ext JS, LLC.
22784  *
22785  * Originally Released Under LGPL - original licence link has changed is not relivant.
22786  *
22787  * Fork - LGPL
22788  * <script type="text/javascript">
22789  */
22790
22791 /**
22792  * @class Roo.data.XmlReader
22793  * @extends Roo.data.DataReader
22794  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22795  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22796  * <p>
22797  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22798  * header in the HTTP response must be set to "text/xml".</em>
22799  * <p>
22800  * Example code:
22801  * <pre><code>
22802 var RecordDef = Roo.data.Record.create([
22803    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22804    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22805 ]);
22806 var myReader = new Roo.data.XmlReader({
22807    totalRecords: "results", // The element which contains the total dataset size (optional)
22808    record: "row",           // The repeated element which contains row information
22809    id: "id"                 // The element within the row that provides an ID for the record (optional)
22810 }, RecordDef);
22811 </code></pre>
22812  * <p>
22813  * This would consume an XML file like this:
22814  * <pre><code>
22815 &lt;?xml?>
22816 &lt;dataset>
22817  &lt;results>2&lt;/results>
22818  &lt;row>
22819    &lt;id>1&lt;/id>
22820    &lt;name>Bill&lt;/name>
22821    &lt;occupation>Gardener&lt;/occupation>
22822  &lt;/row>
22823  &lt;row>
22824    &lt;id>2&lt;/id>
22825    &lt;name>Ben&lt;/name>
22826    &lt;occupation>Horticulturalist&lt;/occupation>
22827  &lt;/row>
22828 &lt;/dataset>
22829 </code></pre>
22830  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22831  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22832  * paged from the remote server.
22833  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22834  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22835  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22836  * a record identifier value.
22837  * @constructor
22838  * Create a new XmlReader
22839  * @param {Object} meta Metadata configuration options
22840  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22841  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22842  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22843  */
22844 Roo.data.XmlReader = function(meta, recordType){
22845     meta = meta || {};
22846     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22847 };
22848 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22849     /**
22850      * This method is only used by a DataProxy which has retrieved data from a remote server.
22851          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22852          * to contain a method called 'responseXML' that returns an XML document object.
22853      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22854      * a cache of Roo.data.Records.
22855      */
22856     read : function(response){
22857         var doc = response.responseXML;
22858         if(!doc) {
22859             throw {message: "XmlReader.read: XML Document not available"};
22860         }
22861         return this.readRecords(doc);
22862     },
22863
22864     /**
22865      * Create a data block containing Roo.data.Records from an XML document.
22866          * @param {Object} doc A parsed XML document.
22867      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22868      * a cache of Roo.data.Records.
22869      */
22870     readRecords : function(doc){
22871         /**
22872          * After any data loads/reads, the raw XML Document is available for further custom processing.
22873          * @type XMLDocument
22874          */
22875         this.xmlData = doc;
22876         var root = doc.documentElement || doc;
22877         var q = Roo.DomQuery;
22878         var recordType = this.recordType, fields = recordType.prototype.fields;
22879         var sid = this.meta.id;
22880         var totalRecords = 0, success = true;
22881         if(this.meta.totalRecords){
22882             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22883         }
22884         
22885         if(this.meta.success){
22886             var sv = q.selectValue(this.meta.success, root, true);
22887             success = sv !== false && sv !== 'false';
22888         }
22889         var records = [];
22890         var ns = q.select(this.meta.record, root);
22891         for(var i = 0, len = ns.length; i < len; i++) {
22892                 var n = ns[i];
22893                 var values = {};
22894                 var id = sid ? q.selectValue(sid, n) : undefined;
22895                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22896                     var f = fields.items[j];
22897                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22898                     v = f.convert(v);
22899                     values[f.name] = v;
22900                 }
22901                 var record = new recordType(values, id);
22902                 record.node = n;
22903                 records[records.length] = record;
22904             }
22905
22906             return {
22907                 success : success,
22908                 records : records,
22909                 totalRecords : totalRecords || records.length
22910             };
22911     }
22912 });/*
22913  * Based on:
22914  * Ext JS Library 1.1.1
22915  * Copyright(c) 2006-2007, Ext JS, LLC.
22916  *
22917  * Originally Released Under LGPL - original licence link has changed is not relivant.
22918  *
22919  * Fork - LGPL
22920  * <script type="text/javascript">
22921  */
22922
22923 /**
22924  * @class Roo.data.ArrayReader
22925  * @extends Roo.data.DataReader
22926  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22927  * Each element of that Array represents a row of data fields. The
22928  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22929  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22930  * <p>
22931  * Example code:.
22932  * <pre><code>
22933 var RecordDef = Roo.data.Record.create([
22934     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22935     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22936 ]);
22937 var myReader = new Roo.data.ArrayReader({
22938     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22939 }, RecordDef);
22940 </code></pre>
22941  * <p>
22942  * This would consume an Array like this:
22943  * <pre><code>
22944 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22945   </code></pre>
22946  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22947  * @constructor
22948  * Create a new JsonReader
22949  * @param {Object} meta Metadata configuration options.
22950  * @param {Object} recordType Either an Array of field definition objects
22951  * as specified to {@link Roo.data.Record#create},
22952  * or an {@link Roo.data.Record} object
22953  * created using {@link Roo.data.Record#create}.
22954  */
22955 Roo.data.ArrayReader = function(meta, recordType){
22956     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22957 };
22958
22959 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22960     /**
22961      * Create a data block containing Roo.data.Records from an XML document.
22962      * @param {Object} o An Array of row objects which represents the dataset.
22963      * @return {Object} data A data block which is used by an Roo.data.Store object as
22964      * a cache of Roo.data.Records.
22965      */
22966     readRecords : function(o){
22967         var sid = this.meta ? this.meta.id : null;
22968         var recordType = this.recordType, fields = recordType.prototype.fields;
22969         var records = [];
22970         var root = o;
22971             for(var i = 0; i < root.length; i++){
22972                     var n = root[i];
22973                 var values = {};
22974                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22975                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22976                 var f = fields.items[j];
22977                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22978                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22979                 v = f.convert(v);
22980                 values[f.name] = v;
22981             }
22982                 var record = new recordType(values, id);
22983                 record.json = n;
22984                 records[records.length] = record;
22985             }
22986             return {
22987                 records : records,
22988                 totalRecords : records.length
22989             };
22990     }
22991 });/*
22992  * Based on:
22993  * Ext JS Library 1.1.1
22994  * Copyright(c) 2006-2007, Ext JS, LLC.
22995  *
22996  * Originally Released Under LGPL - original licence link has changed is not relivant.
22997  *
22998  * Fork - LGPL
22999  * <script type="text/javascript">
23000  */
23001
23002
23003 /**
23004  * @class Roo.data.Tree
23005  * @extends Roo.util.Observable
23006  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23007  * in the tree have most standard DOM functionality.
23008  * @constructor
23009  * @param {Node} root (optional) The root node
23010  */
23011 Roo.data.Tree = function(root){
23012    this.nodeHash = {};
23013    /**
23014     * The root node for this tree
23015     * @type Node
23016     */
23017    this.root = null;
23018    if(root){
23019        this.setRootNode(root);
23020    }
23021    this.addEvents({
23022        /**
23023         * @event append
23024         * Fires when a new child node is appended to a node in this tree.
23025         * @param {Tree} tree The owner tree
23026         * @param {Node} parent The parent node
23027         * @param {Node} node The newly appended node
23028         * @param {Number} index The index of the newly appended node
23029         */
23030        "append" : true,
23031        /**
23032         * @event remove
23033         * Fires when a child node is removed from a node in this tree.
23034         * @param {Tree} tree The owner tree
23035         * @param {Node} parent The parent node
23036         * @param {Node} node The child node removed
23037         */
23038        "remove" : true,
23039        /**
23040         * @event move
23041         * Fires when a node is moved to a new location in the tree
23042         * @param {Tree} tree The owner tree
23043         * @param {Node} node The node moved
23044         * @param {Node} oldParent The old parent of this node
23045         * @param {Node} newParent The new parent of this node
23046         * @param {Number} index The index it was moved to
23047         */
23048        "move" : true,
23049        /**
23050         * @event insert
23051         * Fires when a new child node is inserted in a node in this tree.
23052         * @param {Tree} tree The owner tree
23053         * @param {Node} parent The parent node
23054         * @param {Node} node The child node inserted
23055         * @param {Node} refNode The child node the node was inserted before
23056         */
23057        "insert" : true,
23058        /**
23059         * @event beforeappend
23060         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23061         * @param {Tree} tree The owner tree
23062         * @param {Node} parent The parent node
23063         * @param {Node} node The child node to be appended
23064         */
23065        "beforeappend" : true,
23066        /**
23067         * @event beforeremove
23068         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} parent The parent node
23071         * @param {Node} node The child node to be removed
23072         */
23073        "beforeremove" : true,
23074        /**
23075         * @event beforemove
23076         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23077         * @param {Tree} tree The owner tree
23078         * @param {Node} node The node being moved
23079         * @param {Node} oldParent The parent of the node
23080         * @param {Node} newParent The new parent the node is moving to
23081         * @param {Number} index The index it is being moved to
23082         */
23083        "beforemove" : true,
23084        /**
23085         * @event beforeinsert
23086         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23087         * @param {Tree} tree The owner tree
23088         * @param {Node} parent The parent node
23089         * @param {Node} node The child node to be inserted
23090         * @param {Node} refNode The child node the node is being inserted before
23091         */
23092        "beforeinsert" : true
23093    });
23094
23095     Roo.data.Tree.superclass.constructor.call(this);
23096 };
23097
23098 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23099     pathSeparator: "/",
23100
23101     proxyNodeEvent : function(){
23102         return this.fireEvent.apply(this, arguments);
23103     },
23104
23105     /**
23106      * Returns the root node for this tree.
23107      * @return {Node}
23108      */
23109     getRootNode : function(){
23110         return this.root;
23111     },
23112
23113     /**
23114      * Sets the root node for this tree.
23115      * @param {Node} node
23116      * @return {Node}
23117      */
23118     setRootNode : function(node){
23119         this.root = node;
23120         node.ownerTree = this;
23121         node.isRoot = true;
23122         this.registerNode(node);
23123         return node;
23124     },
23125
23126     /**
23127      * Gets a node in this tree by its id.
23128      * @param {String} id
23129      * @return {Node}
23130      */
23131     getNodeById : function(id){
23132         return this.nodeHash[id];
23133     },
23134
23135     registerNode : function(node){
23136         this.nodeHash[node.id] = node;
23137     },
23138
23139     unregisterNode : function(node){
23140         delete this.nodeHash[node.id];
23141     },
23142
23143     toString : function(){
23144         return "[Tree"+(this.id?" "+this.id:"")+"]";
23145     }
23146 });
23147
23148 /**
23149  * @class Roo.data.Node
23150  * @extends Roo.util.Observable
23151  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23152  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23153  * @constructor
23154  * @param {Object} attributes The attributes/config for the node
23155  */
23156 Roo.data.Node = function(attributes){
23157     /**
23158      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23159      * @type {Object}
23160      */
23161     this.attributes = attributes || {};
23162     this.leaf = this.attributes.leaf;
23163     /**
23164      * The node id. @type String
23165      */
23166     this.id = this.attributes.id;
23167     if(!this.id){
23168         this.id = Roo.id(null, "ynode-");
23169         this.attributes.id = this.id;
23170     }
23171      
23172     
23173     /**
23174      * All child nodes of this node. @type Array
23175      */
23176     this.childNodes = [];
23177     if(!this.childNodes.indexOf){ // indexOf is a must
23178         this.childNodes.indexOf = function(o){
23179             for(var i = 0, len = this.length; i < len; i++){
23180                 if(this[i] == o) {
23181                     return i;
23182                 }
23183             }
23184             return -1;
23185         };
23186     }
23187     /**
23188      * The parent node for this node. @type Node
23189      */
23190     this.parentNode = null;
23191     /**
23192      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23193      */
23194     this.firstChild = null;
23195     /**
23196      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23197      */
23198     this.lastChild = null;
23199     /**
23200      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23201      */
23202     this.previousSibling = null;
23203     /**
23204      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23205      */
23206     this.nextSibling = null;
23207
23208     this.addEvents({
23209        /**
23210         * @event append
23211         * Fires when a new child node is appended
23212         * @param {Tree} tree The owner tree
23213         * @param {Node} this This node
23214         * @param {Node} node The newly appended node
23215         * @param {Number} index The index of the newly appended node
23216         */
23217        "append" : true,
23218        /**
23219         * @event remove
23220         * Fires when a child node is removed
23221         * @param {Tree} tree The owner tree
23222         * @param {Node} this This node
23223         * @param {Node} node The removed node
23224         */
23225        "remove" : true,
23226        /**
23227         * @event move
23228         * Fires when this node is moved to a new location in the tree
23229         * @param {Tree} tree The owner tree
23230         * @param {Node} this This node
23231         * @param {Node} oldParent The old parent of this node
23232         * @param {Node} newParent The new parent of this node
23233         * @param {Number} index The index it was moved to
23234         */
23235        "move" : true,
23236        /**
23237         * @event insert
23238         * Fires when a new child node is inserted.
23239         * @param {Tree} tree The owner tree
23240         * @param {Node} this This node
23241         * @param {Node} node The child node inserted
23242         * @param {Node} refNode The child node the node was inserted before
23243         */
23244        "insert" : true,
23245        /**
23246         * @event beforeappend
23247         * Fires before a new child is appended, return false to cancel the append.
23248         * @param {Tree} tree The owner tree
23249         * @param {Node} this This node
23250         * @param {Node} node The child node to be appended
23251         */
23252        "beforeappend" : true,
23253        /**
23254         * @event beforeremove
23255         * Fires before a child is removed, return false to cancel the remove.
23256         * @param {Tree} tree The owner tree
23257         * @param {Node} this This node
23258         * @param {Node} node The child node to be removed
23259         */
23260        "beforeremove" : true,
23261        /**
23262         * @event beforemove
23263         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23264         * @param {Tree} tree The owner tree
23265         * @param {Node} this This node
23266         * @param {Node} oldParent The parent of this node
23267         * @param {Node} newParent The new parent this node is moving to
23268         * @param {Number} index The index it is being moved to
23269         */
23270        "beforemove" : true,
23271        /**
23272         * @event beforeinsert
23273         * Fires before a new child is inserted, return false to cancel the insert.
23274         * @param {Tree} tree The owner tree
23275         * @param {Node} this This node
23276         * @param {Node} node The child node to be inserted
23277         * @param {Node} refNode The child node the node is being inserted before
23278         */
23279        "beforeinsert" : true
23280    });
23281     this.listeners = this.attributes.listeners;
23282     Roo.data.Node.superclass.constructor.call(this);
23283 };
23284
23285 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23286     fireEvent : function(evtName){
23287         // first do standard event for this node
23288         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23289             return false;
23290         }
23291         // then bubble it up to the tree if the event wasn't cancelled
23292         var ot = this.getOwnerTree();
23293         if(ot){
23294             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23295                 return false;
23296             }
23297         }
23298         return true;
23299     },
23300
23301     /**
23302      * Returns true if this node is a leaf
23303      * @return {Boolean}
23304      */
23305     isLeaf : function(){
23306         return this.leaf === true;
23307     },
23308
23309     // private
23310     setFirstChild : function(node){
23311         this.firstChild = node;
23312     },
23313
23314     //private
23315     setLastChild : function(node){
23316         this.lastChild = node;
23317     },
23318
23319
23320     /**
23321      * Returns true if this node is the last child of its parent
23322      * @return {Boolean}
23323      */
23324     isLast : function(){
23325        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23326     },
23327
23328     /**
23329      * Returns true if this node is the first child of its parent
23330      * @return {Boolean}
23331      */
23332     isFirst : function(){
23333        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23334     },
23335
23336     hasChildNodes : function(){
23337         return !this.isLeaf() && this.childNodes.length > 0;
23338     },
23339
23340     /**
23341      * Insert node(s) as the last child node of this node.
23342      * @param {Node/Array} node The node or Array of nodes to append
23343      * @return {Node} The appended node if single append, or null if an array was passed
23344      */
23345     appendChild : function(node){
23346         var multi = false;
23347         if(node instanceof Array){
23348             multi = node;
23349         }else if(arguments.length > 1){
23350             multi = arguments;
23351         }
23352         // if passed an array or multiple args do them one by one
23353         if(multi){
23354             for(var i = 0, len = multi.length; i < len; i++) {
23355                 this.appendChild(multi[i]);
23356             }
23357         }else{
23358             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23359                 return false;
23360             }
23361             var index = this.childNodes.length;
23362             var oldParent = node.parentNode;
23363             // it's a move, make sure we move it cleanly
23364             if(oldParent){
23365                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23366                     return false;
23367                 }
23368                 oldParent.removeChild(node);
23369             }
23370             index = this.childNodes.length;
23371             if(index == 0){
23372                 this.setFirstChild(node);
23373             }
23374             this.childNodes.push(node);
23375             node.parentNode = this;
23376             var ps = this.childNodes[index-1];
23377             if(ps){
23378                 node.previousSibling = ps;
23379                 ps.nextSibling = node;
23380             }else{
23381                 node.previousSibling = null;
23382             }
23383             node.nextSibling = null;
23384             this.setLastChild(node);
23385             node.setOwnerTree(this.getOwnerTree());
23386             this.fireEvent("append", this.ownerTree, this, node, index);
23387             if(oldParent){
23388                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23389             }
23390             return node;
23391         }
23392     },
23393
23394     /**
23395      * Removes a child node from this node.
23396      * @param {Node} node The node to remove
23397      * @return {Node} The removed node
23398      */
23399     removeChild : function(node){
23400         var index = this.childNodes.indexOf(node);
23401         if(index == -1){
23402             return false;
23403         }
23404         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23405             return false;
23406         }
23407
23408         // remove it from childNodes collection
23409         this.childNodes.splice(index, 1);
23410
23411         // update siblings
23412         if(node.previousSibling){
23413             node.previousSibling.nextSibling = node.nextSibling;
23414         }
23415         if(node.nextSibling){
23416             node.nextSibling.previousSibling = node.previousSibling;
23417         }
23418
23419         // update child refs
23420         if(this.firstChild == node){
23421             this.setFirstChild(node.nextSibling);
23422         }
23423         if(this.lastChild == node){
23424             this.setLastChild(node.previousSibling);
23425         }
23426
23427         node.setOwnerTree(null);
23428         // clear any references from the node
23429         node.parentNode = null;
23430         node.previousSibling = null;
23431         node.nextSibling = null;
23432         this.fireEvent("remove", this.ownerTree, this, node);
23433         return node;
23434     },
23435
23436     /**
23437      * Inserts the first node before the second node in this nodes childNodes collection.
23438      * @param {Node} node The node to insert
23439      * @param {Node} refNode The node to insert before (if null the node is appended)
23440      * @return {Node} The inserted node
23441      */
23442     insertBefore : function(node, refNode){
23443         if(!refNode){ // like standard Dom, refNode can be null for append
23444             return this.appendChild(node);
23445         }
23446         // nothing to do
23447         if(node == refNode){
23448             return false;
23449         }
23450
23451         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23452             return false;
23453         }
23454         var index = this.childNodes.indexOf(refNode);
23455         var oldParent = node.parentNode;
23456         var refIndex = index;
23457
23458         // when moving internally, indexes will change after remove
23459         if(oldParent == this && this.childNodes.indexOf(node) < index){
23460             refIndex--;
23461         }
23462
23463         // it's a move, make sure we move it cleanly
23464         if(oldParent){
23465             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23466                 return false;
23467             }
23468             oldParent.removeChild(node);
23469         }
23470         if(refIndex == 0){
23471             this.setFirstChild(node);
23472         }
23473         this.childNodes.splice(refIndex, 0, node);
23474         node.parentNode = this;
23475         var ps = this.childNodes[refIndex-1];
23476         if(ps){
23477             node.previousSibling = ps;
23478             ps.nextSibling = node;
23479         }else{
23480             node.previousSibling = null;
23481         }
23482         node.nextSibling = refNode;
23483         refNode.previousSibling = node;
23484         node.setOwnerTree(this.getOwnerTree());
23485         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23486         if(oldParent){
23487             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23488         }
23489         return node;
23490     },
23491
23492     /**
23493      * Returns the child node at the specified index.
23494      * @param {Number} index
23495      * @return {Node}
23496      */
23497     item : function(index){
23498         return this.childNodes[index];
23499     },
23500
23501     /**
23502      * Replaces one child node in this node with another.
23503      * @param {Node} newChild The replacement node
23504      * @param {Node} oldChild The node to replace
23505      * @return {Node} The replaced node
23506      */
23507     replaceChild : function(newChild, oldChild){
23508         this.insertBefore(newChild, oldChild);
23509         this.removeChild(oldChild);
23510         return oldChild;
23511     },
23512
23513     /**
23514      * Returns the index of a child node
23515      * @param {Node} node
23516      * @return {Number} The index of the node or -1 if it was not found
23517      */
23518     indexOf : function(child){
23519         return this.childNodes.indexOf(child);
23520     },
23521
23522     /**
23523      * Returns the tree this node is in.
23524      * @return {Tree}
23525      */
23526     getOwnerTree : function(){
23527         // if it doesn't have one, look for one
23528         if(!this.ownerTree){
23529             var p = this;
23530             while(p){
23531                 if(p.ownerTree){
23532                     this.ownerTree = p.ownerTree;
23533                     break;
23534                 }
23535                 p = p.parentNode;
23536             }
23537         }
23538         return this.ownerTree;
23539     },
23540
23541     /**
23542      * Returns depth of this node (the root node has a depth of 0)
23543      * @return {Number}
23544      */
23545     getDepth : function(){
23546         var depth = 0;
23547         var p = this;
23548         while(p.parentNode){
23549             ++depth;
23550             p = p.parentNode;
23551         }
23552         return depth;
23553     },
23554
23555     // private
23556     setOwnerTree : function(tree){
23557         // if it's move, we need to update everyone
23558         if(tree != this.ownerTree){
23559             if(this.ownerTree){
23560                 this.ownerTree.unregisterNode(this);
23561             }
23562             this.ownerTree = tree;
23563             var cs = this.childNodes;
23564             for(var i = 0, len = cs.length; i < len; i++) {
23565                 cs[i].setOwnerTree(tree);
23566             }
23567             if(tree){
23568                 tree.registerNode(this);
23569             }
23570         }
23571     },
23572
23573     /**
23574      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23575      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23576      * @return {String} The path
23577      */
23578     getPath : function(attr){
23579         attr = attr || "id";
23580         var p = this.parentNode;
23581         var b = [this.attributes[attr]];
23582         while(p){
23583             b.unshift(p.attributes[attr]);
23584             p = p.parentNode;
23585         }
23586         var sep = this.getOwnerTree().pathSeparator;
23587         return sep + b.join(sep);
23588     },
23589
23590     /**
23591      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23592      * function call will be the scope provided or the current node. The arguments to the function
23593      * will be the args provided or the current node. If the function returns false at any point,
23594      * the bubble is stopped.
23595      * @param {Function} fn The function to call
23596      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23597      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23598      */
23599     bubble : function(fn, scope, args){
23600         var p = this;
23601         while(p){
23602             if(fn.call(scope || p, args || p) === false){
23603                 break;
23604             }
23605             p = p.parentNode;
23606         }
23607     },
23608
23609     /**
23610      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23611      * function call will be the scope provided or the current node. The arguments to the function
23612      * will be the args provided or the current node. If the function returns false at any point,
23613      * the cascade is stopped on that branch.
23614      * @param {Function} fn The function to call
23615      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23616      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23617      */
23618     cascade : function(fn, scope, args){
23619         if(fn.call(scope || this, args || this) !== false){
23620             var cs = this.childNodes;
23621             for(var i = 0, len = cs.length; i < len; i++) {
23622                 cs[i].cascade(fn, scope, args);
23623             }
23624         }
23625     },
23626
23627     /**
23628      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23629      * function call will be the scope provided or the current node. The arguments to the function
23630      * will be the args provided or the current node. If the function returns false at any point,
23631      * the iteration stops.
23632      * @param {Function} fn The function to call
23633      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23634      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23635      */
23636     eachChild : function(fn, scope, args){
23637         var cs = this.childNodes;
23638         for(var i = 0, len = cs.length; i < len; i++) {
23639                 if(fn.call(scope || this, args || cs[i]) === false){
23640                     break;
23641                 }
23642         }
23643     },
23644
23645     /**
23646      * Finds the first child that has the attribute with the specified value.
23647      * @param {String} attribute The attribute name
23648      * @param {Mixed} value The value to search for
23649      * @return {Node} The found child or null if none was found
23650      */
23651     findChild : function(attribute, value){
23652         var cs = this.childNodes;
23653         for(var i = 0, len = cs.length; i < len; i++) {
23654                 if(cs[i].attributes[attribute] == value){
23655                     return cs[i];
23656                 }
23657         }
23658         return null;
23659     },
23660
23661     /**
23662      * Finds the first child by a custom function. The child matches if the function passed
23663      * returns true.
23664      * @param {Function} fn
23665      * @param {Object} scope (optional)
23666      * @return {Node} The found child or null if none was found
23667      */
23668     findChildBy : function(fn, scope){
23669         var cs = this.childNodes;
23670         for(var i = 0, len = cs.length; i < len; i++) {
23671                 if(fn.call(scope||cs[i], cs[i]) === true){
23672                     return cs[i];
23673                 }
23674         }
23675         return null;
23676     },
23677
23678     /**
23679      * Sorts this nodes children using the supplied sort function
23680      * @param {Function} fn
23681      * @param {Object} scope (optional)
23682      */
23683     sort : function(fn, scope){
23684         var cs = this.childNodes;
23685         var len = cs.length;
23686         if(len > 0){
23687             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23688             cs.sort(sortFn);
23689             for(var i = 0; i < len; i++){
23690                 var n = cs[i];
23691                 n.previousSibling = cs[i-1];
23692                 n.nextSibling = cs[i+1];
23693                 if(i == 0){
23694                     this.setFirstChild(n);
23695                 }
23696                 if(i == len-1){
23697                     this.setLastChild(n);
23698                 }
23699             }
23700         }
23701     },
23702
23703     /**
23704      * Returns true if this node is an ancestor (at any point) of the passed node.
23705      * @param {Node} node
23706      * @return {Boolean}
23707      */
23708     contains : function(node){
23709         return node.isAncestor(this);
23710     },
23711
23712     /**
23713      * Returns true if the passed node is an ancestor (at any point) of this node.
23714      * @param {Node} node
23715      * @return {Boolean}
23716      */
23717     isAncestor : function(node){
23718         var p = this.parentNode;
23719         while(p){
23720             if(p == node){
23721                 return true;
23722             }
23723             p = p.parentNode;
23724         }
23725         return false;
23726     },
23727
23728     toString : function(){
23729         return "[Node"+(this.id?" "+this.id:"")+"]";
23730     }
23731 });/*
23732  * Based on:
23733  * Ext JS Library 1.1.1
23734  * Copyright(c) 2006-2007, Ext JS, LLC.
23735  *
23736  * Originally Released Under LGPL - original licence link has changed is not relivant.
23737  *
23738  * Fork - LGPL
23739  * <script type="text/javascript">
23740  */
23741  (function(){ 
23742 /**
23743  * @class Roo.Layer
23744  * @extends Roo.Element
23745  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23746  * automatic maintaining of shadow/shim positions.
23747  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23748  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23749  * you can pass a string with a CSS class name. False turns off the shadow.
23750  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23751  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23752  * @cfg {String} cls CSS class to add to the element
23753  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23754  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23755  * @constructor
23756  * @param {Object} config An object with config options.
23757  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23758  */
23759
23760 Roo.Layer = function(config, existingEl){
23761     config = config || {};
23762     var dh = Roo.DomHelper;
23763     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23764     if(existingEl){
23765         this.dom = Roo.getDom(existingEl);
23766     }
23767     if(!this.dom){
23768         var o = config.dh || {tag: "div", cls: "x-layer"};
23769         this.dom = dh.append(pel, o);
23770     }
23771     if(config.cls){
23772         this.addClass(config.cls);
23773     }
23774     this.constrain = config.constrain !== false;
23775     this.visibilityMode = Roo.Element.VISIBILITY;
23776     if(config.id){
23777         this.id = this.dom.id = config.id;
23778     }else{
23779         this.id = Roo.id(this.dom);
23780     }
23781     this.zindex = config.zindex || this.getZIndex();
23782     this.position("absolute", this.zindex);
23783     if(config.shadow){
23784         this.shadowOffset = config.shadowOffset || 4;
23785         this.shadow = new Roo.Shadow({
23786             offset : this.shadowOffset,
23787             mode : config.shadow
23788         });
23789     }else{
23790         this.shadowOffset = 0;
23791     }
23792     this.useShim = config.shim !== false && Roo.useShims;
23793     this.useDisplay = config.useDisplay;
23794     this.hide();
23795 };
23796
23797 var supr = Roo.Element.prototype;
23798
23799 // shims are shared among layer to keep from having 100 iframes
23800 var shims = [];
23801
23802 Roo.extend(Roo.Layer, Roo.Element, {
23803
23804     getZIndex : function(){
23805         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23806     },
23807
23808     getShim : function(){
23809         if(!this.useShim){
23810             return null;
23811         }
23812         if(this.shim){
23813             return this.shim;
23814         }
23815         var shim = shims.shift();
23816         if(!shim){
23817             shim = this.createShim();
23818             shim.enableDisplayMode('block');
23819             shim.dom.style.display = 'none';
23820             shim.dom.style.visibility = 'visible';
23821         }
23822         var pn = this.dom.parentNode;
23823         if(shim.dom.parentNode != pn){
23824             pn.insertBefore(shim.dom, this.dom);
23825         }
23826         shim.setStyle('z-index', this.getZIndex()-2);
23827         this.shim = shim;
23828         return shim;
23829     },
23830
23831     hideShim : function(){
23832         if(this.shim){
23833             this.shim.setDisplayed(false);
23834             shims.push(this.shim);
23835             delete this.shim;
23836         }
23837     },
23838
23839     disableShadow : function(){
23840         if(this.shadow){
23841             this.shadowDisabled = true;
23842             this.shadow.hide();
23843             this.lastShadowOffset = this.shadowOffset;
23844             this.shadowOffset = 0;
23845         }
23846     },
23847
23848     enableShadow : function(show){
23849         if(this.shadow){
23850             this.shadowDisabled = false;
23851             this.shadowOffset = this.lastShadowOffset;
23852             delete this.lastShadowOffset;
23853             if(show){
23854                 this.sync(true);
23855             }
23856         }
23857     },
23858
23859     // private
23860     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23861     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23862     sync : function(doShow){
23863         var sw = this.shadow;
23864         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23865             var sh = this.getShim();
23866
23867             var w = this.getWidth(),
23868                 h = this.getHeight();
23869
23870             var l = this.getLeft(true),
23871                 t = this.getTop(true);
23872
23873             if(sw && !this.shadowDisabled){
23874                 if(doShow && !sw.isVisible()){
23875                     sw.show(this);
23876                 }else{
23877                     sw.realign(l, t, w, h);
23878                 }
23879                 if(sh){
23880                     if(doShow){
23881                        sh.show();
23882                     }
23883                     // fit the shim behind the shadow, so it is shimmed too
23884                     var a = sw.adjusts, s = sh.dom.style;
23885                     s.left = (Math.min(l, l+a.l))+"px";
23886                     s.top = (Math.min(t, t+a.t))+"px";
23887                     s.width = (w+a.w)+"px";
23888                     s.height = (h+a.h)+"px";
23889                 }
23890             }else if(sh){
23891                 if(doShow){
23892                    sh.show();
23893                 }
23894                 sh.setSize(w, h);
23895                 sh.setLeftTop(l, t);
23896             }
23897             
23898         }
23899     },
23900
23901     // private
23902     destroy : function(){
23903         this.hideShim();
23904         if(this.shadow){
23905             this.shadow.hide();
23906         }
23907         this.removeAllListeners();
23908         var pn = this.dom.parentNode;
23909         if(pn){
23910             pn.removeChild(this.dom);
23911         }
23912         Roo.Element.uncache(this.id);
23913     },
23914
23915     remove : function(){
23916         this.destroy();
23917     },
23918
23919     // private
23920     beginUpdate : function(){
23921         this.updating = true;
23922     },
23923
23924     // private
23925     endUpdate : function(){
23926         this.updating = false;
23927         this.sync(true);
23928     },
23929
23930     // private
23931     hideUnders : function(negOffset){
23932         if(this.shadow){
23933             this.shadow.hide();
23934         }
23935         this.hideShim();
23936     },
23937
23938     // private
23939     constrainXY : function(){
23940         if(this.constrain){
23941             var vw = Roo.lib.Dom.getViewWidth(),
23942                 vh = Roo.lib.Dom.getViewHeight();
23943             var s = Roo.get(document).getScroll();
23944
23945             var xy = this.getXY();
23946             var x = xy[0], y = xy[1];   
23947             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23948             // only move it if it needs it
23949             var moved = false;
23950             // first validate right/bottom
23951             if((x + w) > vw+s.left){
23952                 x = vw - w - this.shadowOffset;
23953                 moved = true;
23954             }
23955             if((y + h) > vh+s.top){
23956                 y = vh - h - this.shadowOffset;
23957                 moved = true;
23958             }
23959             // then make sure top/left isn't negative
23960             if(x < s.left){
23961                 x = s.left;
23962                 moved = true;
23963             }
23964             if(y < s.top){
23965                 y = s.top;
23966                 moved = true;
23967             }
23968             if(moved){
23969                 if(this.avoidY){
23970                     var ay = this.avoidY;
23971                     if(y <= ay && (y+h) >= ay){
23972                         y = ay-h-5;   
23973                     }
23974                 }
23975                 xy = [x, y];
23976                 this.storeXY(xy);
23977                 supr.setXY.call(this, xy);
23978                 this.sync();
23979             }
23980         }
23981     },
23982
23983     isVisible : function(){
23984         return this.visible;    
23985     },
23986
23987     // private
23988     showAction : function(){
23989         this.visible = true; // track visibility to prevent getStyle calls
23990         if(this.useDisplay === true){
23991             this.setDisplayed("");
23992         }else if(this.lastXY){
23993             supr.setXY.call(this, this.lastXY);
23994         }else if(this.lastLT){
23995             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23996         }
23997     },
23998
23999     // private
24000     hideAction : function(){
24001         this.visible = false;
24002         if(this.useDisplay === true){
24003             this.setDisplayed(false);
24004         }else{
24005             this.setLeftTop(-10000,-10000);
24006         }
24007     },
24008
24009     // overridden Element method
24010     setVisible : function(v, a, d, c, e){
24011         if(v){
24012             this.showAction();
24013         }
24014         if(a && v){
24015             var cb = function(){
24016                 this.sync(true);
24017                 if(c){
24018                     c();
24019                 }
24020             }.createDelegate(this);
24021             supr.setVisible.call(this, true, true, d, cb, e);
24022         }else{
24023             if(!v){
24024                 this.hideUnders(true);
24025             }
24026             var cb = c;
24027             if(a){
24028                 cb = function(){
24029                     this.hideAction();
24030                     if(c){
24031                         c();
24032                     }
24033                 }.createDelegate(this);
24034             }
24035             supr.setVisible.call(this, v, a, d, cb, e);
24036             if(v){
24037                 this.sync(true);
24038             }else if(!a){
24039                 this.hideAction();
24040             }
24041         }
24042     },
24043
24044     storeXY : function(xy){
24045         delete this.lastLT;
24046         this.lastXY = xy;
24047     },
24048
24049     storeLeftTop : function(left, top){
24050         delete this.lastXY;
24051         this.lastLT = [left, top];
24052     },
24053
24054     // private
24055     beforeFx : function(){
24056         this.beforeAction();
24057         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24058     },
24059
24060     // private
24061     afterFx : function(){
24062         Roo.Layer.superclass.afterFx.apply(this, arguments);
24063         this.sync(this.isVisible());
24064     },
24065
24066     // private
24067     beforeAction : function(){
24068         if(!this.updating && this.shadow){
24069             this.shadow.hide();
24070         }
24071     },
24072
24073     // overridden Element method
24074     setLeft : function(left){
24075         this.storeLeftTop(left, this.getTop(true));
24076         supr.setLeft.apply(this, arguments);
24077         this.sync();
24078     },
24079
24080     setTop : function(top){
24081         this.storeLeftTop(this.getLeft(true), top);
24082         supr.setTop.apply(this, arguments);
24083         this.sync();
24084     },
24085
24086     setLeftTop : function(left, top){
24087         this.storeLeftTop(left, top);
24088         supr.setLeftTop.apply(this, arguments);
24089         this.sync();
24090     },
24091
24092     setXY : function(xy, a, d, c, e){
24093         this.fixDisplay();
24094         this.beforeAction();
24095         this.storeXY(xy);
24096         var cb = this.createCB(c);
24097         supr.setXY.call(this, xy, a, d, cb, e);
24098         if(!a){
24099             cb();
24100         }
24101     },
24102
24103     // private
24104     createCB : function(c){
24105         var el = this;
24106         return function(){
24107             el.constrainXY();
24108             el.sync(true);
24109             if(c){
24110                 c();
24111             }
24112         };
24113     },
24114
24115     // overridden Element method
24116     setX : function(x, a, d, c, e){
24117         this.setXY([x, this.getY()], a, d, c, e);
24118     },
24119
24120     // overridden Element method
24121     setY : function(y, a, d, c, e){
24122         this.setXY([this.getX(), y], a, d, c, e);
24123     },
24124
24125     // overridden Element method
24126     setSize : function(w, h, a, d, c, e){
24127         this.beforeAction();
24128         var cb = this.createCB(c);
24129         supr.setSize.call(this, w, h, a, d, cb, e);
24130         if(!a){
24131             cb();
24132         }
24133     },
24134
24135     // overridden Element method
24136     setWidth : function(w, a, d, c, e){
24137         this.beforeAction();
24138         var cb = this.createCB(c);
24139         supr.setWidth.call(this, w, a, d, cb, e);
24140         if(!a){
24141             cb();
24142         }
24143     },
24144
24145     // overridden Element method
24146     setHeight : function(h, a, d, c, e){
24147         this.beforeAction();
24148         var cb = this.createCB(c);
24149         supr.setHeight.call(this, h, a, d, cb, e);
24150         if(!a){
24151             cb();
24152         }
24153     },
24154
24155     // overridden Element method
24156     setBounds : function(x, y, w, h, a, d, c, e){
24157         this.beforeAction();
24158         var cb = this.createCB(c);
24159         if(!a){
24160             this.storeXY([x, y]);
24161             supr.setXY.call(this, [x, y]);
24162             supr.setSize.call(this, w, h, a, d, cb, e);
24163             cb();
24164         }else{
24165             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24166         }
24167         return this;
24168     },
24169     
24170     /**
24171      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24172      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24173      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24174      * @param {Number} zindex The new z-index to set
24175      * @return {this} The Layer
24176      */
24177     setZIndex : function(zindex){
24178         this.zindex = zindex;
24179         this.setStyle("z-index", zindex + 2);
24180         if(this.shadow){
24181             this.shadow.setZIndex(zindex + 1);
24182         }
24183         if(this.shim){
24184             this.shim.setStyle("z-index", zindex);
24185         }
24186     }
24187 });
24188 })();/*
24189  * Based on:
24190  * Ext JS Library 1.1.1
24191  * Copyright(c) 2006-2007, Ext JS, LLC.
24192  *
24193  * Originally Released Under LGPL - original licence link has changed is not relivant.
24194  *
24195  * Fork - LGPL
24196  * <script type="text/javascript">
24197  */
24198
24199
24200 /**
24201  * @class Roo.Shadow
24202  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24203  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24204  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24205  * @constructor
24206  * Create a new Shadow
24207  * @param {Object} config The config object
24208  */
24209 Roo.Shadow = function(config){
24210     Roo.apply(this, config);
24211     if(typeof this.mode != "string"){
24212         this.mode = this.defaultMode;
24213     }
24214     var o = this.offset, a = {h: 0};
24215     var rad = Math.floor(this.offset/2);
24216     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24217         case "drop":
24218             a.w = 0;
24219             a.l = a.t = o;
24220             a.t -= 1;
24221             if(Roo.isIE){
24222                 a.l -= this.offset + rad;
24223                 a.t -= this.offset + rad;
24224                 a.w -= rad;
24225                 a.h -= rad;
24226                 a.t += 1;
24227             }
24228         break;
24229         case "sides":
24230             a.w = (o*2);
24231             a.l = -o;
24232             a.t = o-1;
24233             if(Roo.isIE){
24234                 a.l -= (this.offset - rad);
24235                 a.t -= this.offset + rad;
24236                 a.l += 1;
24237                 a.w -= (this.offset - rad)*2;
24238                 a.w -= rad + 1;
24239                 a.h -= 1;
24240             }
24241         break;
24242         case "frame":
24243             a.w = a.h = (o*2);
24244             a.l = a.t = -o;
24245             a.t += 1;
24246             a.h -= 2;
24247             if(Roo.isIE){
24248                 a.l -= (this.offset - rad);
24249                 a.t -= (this.offset - rad);
24250                 a.l += 1;
24251                 a.w -= (this.offset + rad + 1);
24252                 a.h -= (this.offset + rad);
24253                 a.h += 1;
24254             }
24255         break;
24256     };
24257
24258     this.adjusts = a;
24259 };
24260
24261 Roo.Shadow.prototype = {
24262     /**
24263      * @cfg {String} mode
24264      * The shadow display mode.  Supports the following options:<br />
24265      * sides: Shadow displays on both sides and bottom only<br />
24266      * frame: Shadow displays equally on all four sides<br />
24267      * drop: Traditional bottom-right drop shadow (default)
24268      */
24269     /**
24270      * @cfg {String} offset
24271      * The number of pixels to offset the shadow from the element (defaults to 4)
24272      */
24273     offset: 4,
24274
24275     // private
24276     defaultMode: "drop",
24277
24278     /**
24279      * Displays the shadow under the target element
24280      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24281      */
24282     show : function(target){
24283         target = Roo.get(target);
24284         if(!this.el){
24285             this.el = Roo.Shadow.Pool.pull();
24286             if(this.el.dom.nextSibling != target.dom){
24287                 this.el.insertBefore(target);
24288             }
24289         }
24290         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24291         if(Roo.isIE){
24292             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24293         }
24294         this.realign(
24295             target.getLeft(true),
24296             target.getTop(true),
24297             target.getWidth(),
24298             target.getHeight()
24299         );
24300         this.el.dom.style.display = "block";
24301     },
24302
24303     /**
24304      * Returns true if the shadow is visible, else false
24305      */
24306     isVisible : function(){
24307         return this.el ? true : false;  
24308     },
24309
24310     /**
24311      * Direct alignment when values are already available. Show must be called at least once before
24312      * calling this method to ensure it is initialized.
24313      * @param {Number} left The target element left position
24314      * @param {Number} top The target element top position
24315      * @param {Number} width The target element width
24316      * @param {Number} height The target element height
24317      */
24318     realign : function(l, t, w, h){
24319         if(!this.el){
24320             return;
24321         }
24322         var a = this.adjusts, d = this.el.dom, s = d.style;
24323         var iea = 0;
24324         s.left = (l+a.l)+"px";
24325         s.top = (t+a.t)+"px";
24326         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24327  
24328         if(s.width != sws || s.height != shs){
24329             s.width = sws;
24330             s.height = shs;
24331             if(!Roo.isIE){
24332                 var cn = d.childNodes;
24333                 var sww = Math.max(0, (sw-12))+"px";
24334                 cn[0].childNodes[1].style.width = sww;
24335                 cn[1].childNodes[1].style.width = sww;
24336                 cn[2].childNodes[1].style.width = sww;
24337                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24338             }
24339         }
24340     },
24341
24342     /**
24343      * Hides this shadow
24344      */
24345     hide : function(){
24346         if(this.el){
24347             this.el.dom.style.display = "none";
24348             Roo.Shadow.Pool.push(this.el);
24349             delete this.el;
24350         }
24351     },
24352
24353     /**
24354      * Adjust the z-index of this shadow
24355      * @param {Number} zindex The new z-index
24356      */
24357     setZIndex : function(z){
24358         this.zIndex = z;
24359         if(this.el){
24360             this.el.setStyle("z-index", z);
24361         }
24362     }
24363 };
24364
24365 // Private utility class that manages the internal Shadow cache
24366 Roo.Shadow.Pool = function(){
24367     var p = [];
24368     var markup = Roo.isIE ?
24369                  '<div class="x-ie-shadow"></div>' :
24370                  '<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>';
24371     return {
24372         pull : function(){
24373             var sh = p.shift();
24374             if(!sh){
24375                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24376                 sh.autoBoxAdjust = false;
24377             }
24378             return sh;
24379         },
24380
24381         push : function(sh){
24382             p.push(sh);
24383         }
24384     };
24385 }();/*
24386  * Based on:
24387  * Ext JS Library 1.1.1
24388  * Copyright(c) 2006-2007, Ext JS, LLC.
24389  *
24390  * Originally Released Under LGPL - original licence link has changed is not relivant.
24391  *
24392  * Fork - LGPL
24393  * <script type="text/javascript">
24394  */
24395
24396
24397 /**
24398  * @class Roo.SplitBar
24399  * @extends Roo.util.Observable
24400  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24401  * <br><br>
24402  * Usage:
24403  * <pre><code>
24404 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24405                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24406 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24407 split.minSize = 100;
24408 split.maxSize = 600;
24409 split.animate = true;
24410 split.on('moved', splitterMoved);
24411 </code></pre>
24412  * @constructor
24413  * Create a new SplitBar
24414  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24415  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24416  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24417  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24418                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24419                         position of the SplitBar).
24420  */
24421 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24422     
24423     /** @private */
24424     this.el = Roo.get(dragElement, true);
24425     this.el.dom.unselectable = "on";
24426     /** @private */
24427     this.resizingEl = Roo.get(resizingElement, true);
24428
24429     /**
24430      * @private
24431      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24432      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24433      * @type Number
24434      */
24435     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24436     
24437     /**
24438      * The minimum size of the resizing element. (Defaults to 0)
24439      * @type Number
24440      */
24441     this.minSize = 0;
24442     
24443     /**
24444      * The maximum size of the resizing element. (Defaults to 2000)
24445      * @type Number
24446      */
24447     this.maxSize = 2000;
24448     
24449     /**
24450      * Whether to animate the transition to the new size
24451      * @type Boolean
24452      */
24453     this.animate = false;
24454     
24455     /**
24456      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24457      * @type Boolean
24458      */
24459     this.useShim = false;
24460     
24461     /** @private */
24462     this.shim = null;
24463     
24464     if(!existingProxy){
24465         /** @private */
24466         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24467     }else{
24468         this.proxy = Roo.get(existingProxy).dom;
24469     }
24470     /** @private */
24471     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24472     
24473     /** @private */
24474     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24475     
24476     /** @private */
24477     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24478     
24479     /** @private */
24480     this.dragSpecs = {};
24481     
24482     /**
24483      * @private The adapter to use to positon and resize elements
24484      */
24485     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24486     this.adapter.init(this);
24487     
24488     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24489         /** @private */
24490         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24491         this.el.addClass("x-splitbar-h");
24492     }else{
24493         /** @private */
24494         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24495         this.el.addClass("x-splitbar-v");
24496     }
24497     
24498     this.addEvents({
24499         /**
24500          * @event resize
24501          * Fires when the splitter is moved (alias for {@link #event-moved})
24502          * @param {Roo.SplitBar} this
24503          * @param {Number} newSize the new width or height
24504          */
24505         "resize" : true,
24506         /**
24507          * @event moved
24508          * Fires when the splitter is moved
24509          * @param {Roo.SplitBar} this
24510          * @param {Number} newSize the new width or height
24511          */
24512         "moved" : true,
24513         /**
24514          * @event beforeresize
24515          * Fires before the splitter is dragged
24516          * @param {Roo.SplitBar} this
24517          */
24518         "beforeresize" : true,
24519
24520         "beforeapply" : true
24521     });
24522
24523     Roo.util.Observable.call(this);
24524 };
24525
24526 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24527     onStartProxyDrag : function(x, y){
24528         this.fireEvent("beforeresize", this);
24529         if(!this.overlay){
24530             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24531             o.unselectable();
24532             o.enableDisplayMode("block");
24533             // all splitbars share the same overlay
24534             Roo.SplitBar.prototype.overlay = o;
24535         }
24536         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24537         this.overlay.show();
24538         Roo.get(this.proxy).setDisplayed("block");
24539         var size = this.adapter.getElementSize(this);
24540         this.activeMinSize = this.getMinimumSize();;
24541         this.activeMaxSize = this.getMaximumSize();;
24542         var c1 = size - this.activeMinSize;
24543         var c2 = Math.max(this.activeMaxSize - size, 0);
24544         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24545             this.dd.resetConstraints();
24546             this.dd.setXConstraint(
24547                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24548                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24549             );
24550             this.dd.setYConstraint(0, 0);
24551         }else{
24552             this.dd.resetConstraints();
24553             this.dd.setXConstraint(0, 0);
24554             this.dd.setYConstraint(
24555                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24556                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24557             );
24558          }
24559         this.dragSpecs.startSize = size;
24560         this.dragSpecs.startPoint = [x, y];
24561         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24562     },
24563     
24564     /** 
24565      * @private Called after the drag operation by the DDProxy
24566      */
24567     onEndProxyDrag : function(e){
24568         Roo.get(this.proxy).setDisplayed(false);
24569         var endPoint = Roo.lib.Event.getXY(e);
24570         if(this.overlay){
24571             this.overlay.hide();
24572         }
24573         var newSize;
24574         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24575             newSize = this.dragSpecs.startSize + 
24576                 (this.placement == Roo.SplitBar.LEFT ?
24577                     endPoint[0] - this.dragSpecs.startPoint[0] :
24578                     this.dragSpecs.startPoint[0] - endPoint[0]
24579                 );
24580         }else{
24581             newSize = this.dragSpecs.startSize + 
24582                 (this.placement == Roo.SplitBar.TOP ?
24583                     endPoint[1] - this.dragSpecs.startPoint[1] :
24584                     this.dragSpecs.startPoint[1] - endPoint[1]
24585                 );
24586         }
24587         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24588         if(newSize != this.dragSpecs.startSize){
24589             if(this.fireEvent('beforeapply', this, newSize) !== false){
24590                 this.adapter.setElementSize(this, newSize);
24591                 this.fireEvent("moved", this, newSize);
24592                 this.fireEvent("resize", this, newSize);
24593             }
24594         }
24595     },
24596     
24597     /**
24598      * Get the adapter this SplitBar uses
24599      * @return The adapter object
24600      */
24601     getAdapter : function(){
24602         return this.adapter;
24603     },
24604     
24605     /**
24606      * Set the adapter this SplitBar uses
24607      * @param {Object} adapter A SplitBar adapter object
24608      */
24609     setAdapter : function(adapter){
24610         this.adapter = adapter;
24611         this.adapter.init(this);
24612     },
24613     
24614     /**
24615      * Gets the minimum size for the resizing element
24616      * @return {Number} The minimum size
24617      */
24618     getMinimumSize : function(){
24619         return this.minSize;
24620     },
24621     
24622     /**
24623      * Sets the minimum size for the resizing element
24624      * @param {Number} minSize The minimum size
24625      */
24626     setMinimumSize : function(minSize){
24627         this.minSize = minSize;
24628     },
24629     
24630     /**
24631      * Gets the maximum size for the resizing element
24632      * @return {Number} The maximum size
24633      */
24634     getMaximumSize : function(){
24635         return this.maxSize;
24636     },
24637     
24638     /**
24639      * Sets the maximum size for the resizing element
24640      * @param {Number} maxSize The maximum size
24641      */
24642     setMaximumSize : function(maxSize){
24643         this.maxSize = maxSize;
24644     },
24645     
24646     /**
24647      * Sets the initialize size for the resizing element
24648      * @param {Number} size The initial size
24649      */
24650     setCurrentSize : function(size){
24651         var oldAnimate = this.animate;
24652         this.animate = false;
24653         this.adapter.setElementSize(this, size);
24654         this.animate = oldAnimate;
24655     },
24656     
24657     /**
24658      * Destroy this splitbar. 
24659      * @param {Boolean} removeEl True to remove the element
24660      */
24661     destroy : function(removeEl){
24662         if(this.shim){
24663             this.shim.remove();
24664         }
24665         this.dd.unreg();
24666         this.proxy.parentNode.removeChild(this.proxy);
24667         if(removeEl){
24668             this.el.remove();
24669         }
24670     }
24671 });
24672
24673 /**
24674  * @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.
24675  */
24676 Roo.SplitBar.createProxy = function(dir){
24677     var proxy = new Roo.Element(document.createElement("div"));
24678     proxy.unselectable();
24679     var cls = 'x-splitbar-proxy';
24680     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24681     document.body.appendChild(proxy.dom);
24682     return proxy.dom;
24683 };
24684
24685 /** 
24686  * @class Roo.SplitBar.BasicLayoutAdapter
24687  * Default Adapter. It assumes the splitter and resizing element are not positioned
24688  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24689  */
24690 Roo.SplitBar.BasicLayoutAdapter = function(){
24691 };
24692
24693 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24694     // do nothing for now
24695     init : function(s){
24696     
24697     },
24698     /**
24699      * Called before drag operations to get the current size of the resizing element. 
24700      * @param {Roo.SplitBar} s The SplitBar using this adapter
24701      */
24702      getElementSize : function(s){
24703         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24704             return s.resizingEl.getWidth();
24705         }else{
24706             return s.resizingEl.getHeight();
24707         }
24708     },
24709     
24710     /**
24711      * Called after drag operations to set the size of the resizing element.
24712      * @param {Roo.SplitBar} s The SplitBar using this adapter
24713      * @param {Number} newSize The new size to set
24714      * @param {Function} onComplete A function to be invoked when resizing is complete
24715      */
24716     setElementSize : function(s, newSize, onComplete){
24717         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24718             if(!s.animate){
24719                 s.resizingEl.setWidth(newSize);
24720                 if(onComplete){
24721                     onComplete(s, newSize);
24722                 }
24723             }else{
24724                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24725             }
24726         }else{
24727             
24728             if(!s.animate){
24729                 s.resizingEl.setHeight(newSize);
24730                 if(onComplete){
24731                     onComplete(s, newSize);
24732                 }
24733             }else{
24734                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24735             }
24736         }
24737     }
24738 };
24739
24740 /** 
24741  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24742  * @extends Roo.SplitBar.BasicLayoutAdapter
24743  * Adapter that  moves the splitter element to align with the resized sizing element. 
24744  * Used with an absolute positioned SplitBar.
24745  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24746  * document.body, make sure you assign an id to the body element.
24747  */
24748 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24749     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24750     this.container = Roo.get(container);
24751 };
24752
24753 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24754     init : function(s){
24755         this.basic.init(s);
24756     },
24757     
24758     getElementSize : function(s){
24759         return this.basic.getElementSize(s);
24760     },
24761     
24762     setElementSize : function(s, newSize, onComplete){
24763         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24764     },
24765     
24766     moveSplitter : function(s){
24767         var yes = Roo.SplitBar;
24768         switch(s.placement){
24769             case yes.LEFT:
24770                 s.el.setX(s.resizingEl.getRight());
24771                 break;
24772             case yes.RIGHT:
24773                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24774                 break;
24775             case yes.TOP:
24776                 s.el.setY(s.resizingEl.getBottom());
24777                 break;
24778             case yes.BOTTOM:
24779                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24780                 break;
24781         }
24782     }
24783 };
24784
24785 /**
24786  * Orientation constant - Create a vertical SplitBar
24787  * @static
24788  * @type Number
24789  */
24790 Roo.SplitBar.VERTICAL = 1;
24791
24792 /**
24793  * Orientation constant - Create a horizontal SplitBar
24794  * @static
24795  * @type Number
24796  */
24797 Roo.SplitBar.HORIZONTAL = 2;
24798
24799 /**
24800  * Placement constant - The resizing element is to the left of the splitter element
24801  * @static
24802  * @type Number
24803  */
24804 Roo.SplitBar.LEFT = 1;
24805
24806 /**
24807  * Placement constant - The resizing element is to the right of the splitter element
24808  * @static
24809  * @type Number
24810  */
24811 Roo.SplitBar.RIGHT = 2;
24812
24813 /**
24814  * Placement constant - The resizing element is positioned above the splitter element
24815  * @static
24816  * @type Number
24817  */
24818 Roo.SplitBar.TOP = 3;
24819
24820 /**
24821  * Placement constant - The resizing element is positioned under splitter element
24822  * @static
24823  * @type Number
24824  */
24825 Roo.SplitBar.BOTTOM = 4;
24826 /*
24827  * Based on:
24828  * Ext JS Library 1.1.1
24829  * Copyright(c) 2006-2007, Ext JS, LLC.
24830  *
24831  * Originally Released Under LGPL - original licence link has changed is not relivant.
24832  *
24833  * Fork - LGPL
24834  * <script type="text/javascript">
24835  */
24836
24837 /**
24838  * @class Roo.View
24839  * @extends Roo.util.Observable
24840  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24841  * This class also supports single and multi selection modes. <br>
24842  * Create a data model bound view:
24843  <pre><code>
24844  var store = new Roo.data.Store(...);
24845
24846  var view = new Roo.View({
24847     el : "my-element",
24848     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24849  
24850     singleSelect: true,
24851     selectedClass: "ydataview-selected",
24852     store: store
24853  });
24854
24855  // listen for node click?
24856  view.on("click", function(vw, index, node, e){
24857  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24858  });
24859
24860  // load XML data
24861  dataModel.load("foobar.xml");
24862  </code></pre>
24863  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24864  * <br><br>
24865  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24866  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24867  * 
24868  * Note: old style constructor is still suported (container, template, config)
24869  * 
24870  * @constructor
24871  * Create a new View
24872  * @param {Object} config The config object
24873  * 
24874  */
24875 Roo.View = function(config, depreciated_tpl, depreciated_config){
24876     
24877     this.parent = false;
24878     
24879     if (typeof(depreciated_tpl) == 'undefined') {
24880         // new way.. - universal constructor.
24881         Roo.apply(this, config);
24882         this.el  = Roo.get(this.el);
24883     } else {
24884         // old format..
24885         this.el  = Roo.get(config);
24886         this.tpl = depreciated_tpl;
24887         Roo.apply(this, depreciated_config);
24888     }
24889     this.wrapEl  = this.el.wrap().wrap();
24890     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24891     
24892     
24893     if(typeof(this.tpl) == "string"){
24894         this.tpl = new Roo.Template(this.tpl);
24895     } else {
24896         // support xtype ctors..
24897         this.tpl = new Roo.factory(this.tpl, Roo);
24898     }
24899     
24900     
24901     this.tpl.compile();
24902     
24903     /** @private */
24904     this.addEvents({
24905         /**
24906          * @event beforeclick
24907          * Fires before a click is processed. Returns false to cancel the default action.
24908          * @param {Roo.View} this
24909          * @param {Number} index The index of the target node
24910          * @param {HTMLElement} node The target node
24911          * @param {Roo.EventObject} e The raw event object
24912          */
24913             "beforeclick" : true,
24914         /**
24915          * @event click
24916          * Fires when a template node is clicked.
24917          * @param {Roo.View} this
24918          * @param {Number} index The index of the target node
24919          * @param {HTMLElement} node The target node
24920          * @param {Roo.EventObject} e The raw event object
24921          */
24922             "click" : true,
24923         /**
24924          * @event dblclick
24925          * Fires when a template node is double clicked.
24926          * @param {Roo.View} this
24927          * @param {Number} index The index of the target node
24928          * @param {HTMLElement} node The target node
24929          * @param {Roo.EventObject} e The raw event object
24930          */
24931             "dblclick" : true,
24932         /**
24933          * @event contextmenu
24934          * Fires when a template node is right clicked.
24935          * @param {Roo.View} this
24936          * @param {Number} index The index of the target node
24937          * @param {HTMLElement} node The target node
24938          * @param {Roo.EventObject} e The raw event object
24939          */
24940             "contextmenu" : true,
24941         /**
24942          * @event selectionchange
24943          * Fires when the selected nodes change.
24944          * @param {Roo.View} this
24945          * @param {Array} selections Array of the selected nodes
24946          */
24947             "selectionchange" : true,
24948     
24949         /**
24950          * @event beforeselect
24951          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24952          * @param {Roo.View} this
24953          * @param {HTMLElement} node The node to be selected
24954          * @param {Array} selections Array of currently selected nodes
24955          */
24956             "beforeselect" : true,
24957         /**
24958          * @event preparedata
24959          * Fires on every row to render, to allow you to change the data.
24960          * @param {Roo.View} this
24961          * @param {Object} data to be rendered (change this)
24962          */
24963           "preparedata" : true
24964           
24965           
24966         });
24967
24968
24969
24970     this.el.on({
24971         "click": this.onClick,
24972         "dblclick": this.onDblClick,
24973         "contextmenu": this.onContextMenu,
24974         scope:this
24975     });
24976
24977     this.selections = [];
24978     this.nodes = [];
24979     this.cmp = new Roo.CompositeElementLite([]);
24980     if(this.store){
24981         this.store = Roo.factory(this.store, Roo.data);
24982         this.setStore(this.store, true);
24983     }
24984     
24985     if ( this.footer && this.footer.xtype) {
24986            
24987          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24988         
24989         this.footer.dataSource = this.store
24990         this.footer.container = fctr;
24991         this.footer = Roo.factory(this.footer, Roo);
24992         fctr.insertFirst(this.el);
24993         
24994         // this is a bit insane - as the paging toolbar seems to detach the el..
24995 //        dom.parentNode.parentNode.parentNode
24996          // they get detached?
24997     }
24998     
24999     
25000     Roo.View.superclass.constructor.call(this);
25001     
25002     
25003 };
25004
25005 Roo.extend(Roo.View, Roo.util.Observable, {
25006     
25007      /**
25008      * @cfg {Roo.data.Store} store Data store to load data from.
25009      */
25010     store : false,
25011     
25012     /**
25013      * @cfg {String|Roo.Element} el The container element.
25014      */
25015     el : '',
25016     
25017     /**
25018      * @cfg {String|Roo.Template} tpl The template used by this View 
25019      */
25020     tpl : false,
25021     /**
25022      * @cfg {String} dataName the named area of the template to use as the data area
25023      *                          Works with domtemplates roo-name="name"
25024      */
25025     dataName: false,
25026     /**
25027      * @cfg {String} selectedClass The css class to add to selected nodes
25028      */
25029     selectedClass : "x-view-selected",
25030      /**
25031      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25032      */
25033     emptyText : "",
25034     
25035     /**
25036      * @cfg {String} text to display on mask (default Loading)
25037      */
25038     mask : false,
25039     /**
25040      * @cfg {Boolean} multiSelect Allow multiple selection
25041      */
25042     multiSelect : false,
25043     /**
25044      * @cfg {Boolean} singleSelect Allow single selection
25045      */
25046     singleSelect:  false,
25047     
25048     /**
25049      * @cfg {Boolean} toggleSelect - selecting 
25050      */
25051     toggleSelect : false,
25052     
25053     /**
25054      * @cfg {Boolean} tickable - selecting 
25055      */
25056     tickable : false,
25057     
25058     /**
25059      * Returns the element this view is bound to.
25060      * @return {Roo.Element}
25061      */
25062     getEl : function(){
25063         return this.wrapEl;
25064     },
25065     
25066     
25067
25068     /**
25069      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25070      */
25071     refresh : function(){
25072         //Roo.log('refresh');
25073         var t = this.tpl;
25074         
25075         // if we are using something like 'domtemplate', then
25076         // the what gets used is:
25077         // t.applySubtemplate(NAME, data, wrapping data..)
25078         // the outer template then get' applied with
25079         //     the store 'extra data'
25080         // and the body get's added to the
25081         //      roo-name="data" node?
25082         //      <span class='roo-tpl-{name}'></span> ?????
25083         
25084         
25085         
25086         this.clearSelections();
25087         this.el.update("");
25088         var html = [];
25089         var records = this.store.getRange();
25090         if(records.length < 1) {
25091             
25092             // is this valid??  = should it render a template??
25093             
25094             this.el.update(this.emptyText);
25095             return;
25096         }
25097         var el = this.el;
25098         if (this.dataName) {
25099             this.el.update(t.apply(this.store.meta)); //????
25100             el = this.el.child('.roo-tpl-' + this.dataName);
25101         }
25102         
25103         for(var i = 0, len = records.length; i < len; i++){
25104             var data = this.prepareData(records[i].data, i, records[i]);
25105             this.fireEvent("preparedata", this, data, i, records[i]);
25106             
25107             var d = Roo.apply({}, data);
25108             
25109             if(this.tickable){
25110                 Roo.apply(d, {'roo-id' : Roo.id()});
25111                 
25112                 var _this = this;
25113             
25114                 Roo.each(this.parent.item, function(item){
25115                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25116                         return;
25117                     }
25118                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25119                 });
25120             }
25121             
25122             html[html.length] = Roo.util.Format.trim(
25123                 this.dataName ?
25124                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25125                     t.apply(d)
25126             );
25127         }
25128         
25129         
25130         
25131         el.update(html.join(""));
25132         this.nodes = el.dom.childNodes;
25133         this.updateIndexes(0);
25134     },
25135     
25136
25137     /**
25138      * Function to override to reformat the data that is sent to
25139      * the template for each node.
25140      * DEPRICATED - use the preparedata event handler.
25141      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25142      * a JSON object for an UpdateManager bound view).
25143      */
25144     prepareData : function(data, index, record)
25145     {
25146         this.fireEvent("preparedata", this, data, index, record);
25147         return data;
25148     },
25149
25150     onUpdate : function(ds, record){
25151         // Roo.log('on update');   
25152         this.clearSelections();
25153         var index = this.store.indexOf(record);
25154         var n = this.nodes[index];
25155         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25156         n.parentNode.removeChild(n);
25157         this.updateIndexes(index, index);
25158     },
25159
25160     
25161     
25162 // --------- FIXME     
25163     onAdd : function(ds, records, index)
25164     {
25165         //Roo.log(['on Add', ds, records, index] );        
25166         this.clearSelections();
25167         if(this.nodes.length == 0){
25168             this.refresh();
25169             return;
25170         }
25171         var n = this.nodes[index];
25172         for(var i = 0, len = records.length; i < len; i++){
25173             var d = this.prepareData(records[i].data, i, records[i]);
25174             if(n){
25175                 this.tpl.insertBefore(n, d);
25176             }else{
25177                 
25178                 this.tpl.append(this.el, d);
25179             }
25180         }
25181         this.updateIndexes(index);
25182     },
25183
25184     onRemove : function(ds, record, index){
25185        // Roo.log('onRemove');
25186         this.clearSelections();
25187         var el = this.dataName  ?
25188             this.el.child('.roo-tpl-' + this.dataName) :
25189             this.el; 
25190         
25191         el.dom.removeChild(this.nodes[index]);
25192         this.updateIndexes(index);
25193     },
25194
25195     /**
25196      * Refresh an individual node.
25197      * @param {Number} index
25198      */
25199     refreshNode : function(index){
25200         this.onUpdate(this.store, this.store.getAt(index));
25201     },
25202
25203     updateIndexes : function(startIndex, endIndex){
25204         var ns = this.nodes;
25205         startIndex = startIndex || 0;
25206         endIndex = endIndex || ns.length - 1;
25207         for(var i = startIndex; i <= endIndex; i++){
25208             ns[i].nodeIndex = i;
25209         }
25210     },
25211
25212     /**
25213      * Changes the data store this view uses and refresh the view.
25214      * @param {Store} store
25215      */
25216     setStore : function(store, initial){
25217         if(!initial && this.store){
25218             this.store.un("datachanged", this.refresh);
25219             this.store.un("add", this.onAdd);
25220             this.store.un("remove", this.onRemove);
25221             this.store.un("update", this.onUpdate);
25222             this.store.un("clear", this.refresh);
25223             this.store.un("beforeload", this.onBeforeLoad);
25224             this.store.un("load", this.onLoad);
25225             this.store.un("loadexception", this.onLoad);
25226         }
25227         if(store){
25228           
25229             store.on("datachanged", this.refresh, this);
25230             store.on("add", this.onAdd, this);
25231             store.on("remove", this.onRemove, this);
25232             store.on("update", this.onUpdate, this);
25233             store.on("clear", this.refresh, this);
25234             store.on("beforeload", this.onBeforeLoad, this);
25235             store.on("load", this.onLoad, this);
25236             store.on("loadexception", this.onLoad, this);
25237         }
25238         
25239         if(store){
25240             this.refresh();
25241         }
25242     },
25243     /**
25244      * onbeforeLoad - masks the loading area.
25245      *
25246      */
25247     onBeforeLoad : function(store,opts)
25248     {
25249          //Roo.log('onBeforeLoad');   
25250         if (!opts.add) {
25251             this.el.update("");
25252         }
25253         this.el.mask(this.mask ? this.mask : "Loading" ); 
25254     },
25255     onLoad : function ()
25256     {
25257         this.el.unmask();
25258     },
25259     
25260
25261     /**
25262      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25263      * @param {HTMLElement} node
25264      * @return {HTMLElement} The template node
25265      */
25266     findItemFromChild : function(node){
25267         var el = this.dataName  ?
25268             this.el.child('.roo-tpl-' + this.dataName,true) :
25269             this.el.dom; 
25270         
25271         if(!node || node.parentNode == el){
25272                     return node;
25273             }
25274             var p = node.parentNode;
25275             while(p && p != el){
25276             if(p.parentNode == el){
25277                 return p;
25278             }
25279             p = p.parentNode;
25280         }
25281             return null;
25282     },
25283
25284     /** @ignore */
25285     onClick : function(e){
25286         var item = this.findItemFromChild(e.getTarget());
25287         if(item){
25288             var index = this.indexOf(item);
25289             if(this.onItemClick(item, index, e) !== false){
25290                 this.fireEvent("click", this, index, item, e);
25291             }
25292         }else{
25293             this.clearSelections();
25294         }
25295     },
25296
25297     /** @ignore */
25298     onContextMenu : function(e){
25299         var item = this.findItemFromChild(e.getTarget());
25300         if(item){
25301             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25302         }
25303     },
25304
25305     /** @ignore */
25306     onDblClick : function(e){
25307         var item = this.findItemFromChild(e.getTarget());
25308         if(item){
25309             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25310         }
25311     },
25312
25313     onItemClick : function(item, index, e)
25314     {
25315         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25316             return false;
25317         }
25318         if (this.toggleSelect) {
25319             var m = this.isSelected(item) ? 'unselect' : 'select';
25320             //Roo.log(m);
25321             var _t = this;
25322             _t[m](item, true, false);
25323             return true;
25324         }
25325         if(this.multiSelect || this.singleSelect){
25326             if(this.multiSelect && e.shiftKey && this.lastSelection){
25327                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25328             }else{
25329                 this.select(item, this.multiSelect && e.ctrlKey);
25330                 this.lastSelection = item;
25331             }
25332             
25333             if(!this.tickable){
25334                 e.preventDefault();
25335             }
25336             
25337         }
25338         return true;
25339     },
25340
25341     /**
25342      * Get the number of selected nodes.
25343      * @return {Number}
25344      */
25345     getSelectionCount : function(){
25346         return this.selections.length;
25347     },
25348
25349     /**
25350      * Get the currently selected nodes.
25351      * @return {Array} An array of HTMLElements
25352      */
25353     getSelectedNodes : function(){
25354         return this.selections;
25355     },
25356
25357     /**
25358      * Get the indexes of the selected nodes.
25359      * @return {Array}
25360      */
25361     getSelectedIndexes : function(){
25362         var indexes = [], s = this.selections;
25363         for(var i = 0, len = s.length; i < len; i++){
25364             indexes.push(s[i].nodeIndex);
25365         }
25366         return indexes;
25367     },
25368
25369     /**
25370      * Clear all selections
25371      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25372      */
25373     clearSelections : function(suppressEvent){
25374         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25375             this.cmp.elements = this.selections;
25376             this.cmp.removeClass(this.selectedClass);
25377             this.selections = [];
25378             if(!suppressEvent){
25379                 this.fireEvent("selectionchange", this, this.selections);
25380             }
25381         }
25382     },
25383
25384     /**
25385      * Returns true if the passed node is selected
25386      * @param {HTMLElement/Number} node The node or node index
25387      * @return {Boolean}
25388      */
25389     isSelected : function(node){
25390         var s = this.selections;
25391         if(s.length < 1){
25392             return false;
25393         }
25394         node = this.getNode(node);
25395         return s.indexOf(node) !== -1;
25396     },
25397
25398     /**
25399      * Selects nodes.
25400      * @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
25401      * @param {Boolean} keepExisting (optional) true to keep existing selections
25402      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25403      */
25404     select : function(nodeInfo, keepExisting, suppressEvent){
25405         if(nodeInfo instanceof Array){
25406             if(!keepExisting){
25407                 this.clearSelections(true);
25408             }
25409             for(var i = 0, len = nodeInfo.length; i < len; i++){
25410                 this.select(nodeInfo[i], true, true);
25411             }
25412             return;
25413         } 
25414         var node = this.getNode(nodeInfo);
25415         if(!node || this.isSelected(node)){
25416             return; // already selected.
25417         }
25418         if(!keepExisting){
25419             this.clearSelections(true);
25420         }
25421         
25422         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25423             Roo.fly(node).addClass(this.selectedClass);
25424             this.selections.push(node);
25425             if(!suppressEvent){
25426                 this.fireEvent("selectionchange", this, this.selections);
25427             }
25428         }
25429         
25430         
25431     },
25432       /**
25433      * Unselects nodes.
25434      * @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
25435      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25436      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25437      */
25438     unselect : function(nodeInfo, keepExisting, suppressEvent)
25439     {
25440         if(nodeInfo instanceof Array){
25441             Roo.each(this.selections, function(s) {
25442                 this.unselect(s, nodeInfo);
25443             }, this);
25444             return;
25445         }
25446         var node = this.getNode(nodeInfo);
25447         if(!node || !this.isSelected(node)){
25448             //Roo.log("not selected");
25449             return; // not selected.
25450         }
25451         // fireevent???
25452         var ns = [];
25453         Roo.each(this.selections, function(s) {
25454             if (s == node ) {
25455                 Roo.fly(node).removeClass(this.selectedClass);
25456
25457                 return;
25458             }
25459             ns.push(s);
25460         },this);
25461         
25462         this.selections= ns;
25463         this.fireEvent("selectionchange", this, this.selections);
25464     },
25465
25466     /**
25467      * Gets a template node.
25468      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25469      * @return {HTMLElement} The node or null if it wasn't found
25470      */
25471     getNode : function(nodeInfo){
25472         if(typeof nodeInfo == "string"){
25473             return document.getElementById(nodeInfo);
25474         }else if(typeof nodeInfo == "number"){
25475             return this.nodes[nodeInfo];
25476         }
25477         return nodeInfo;
25478     },
25479
25480     /**
25481      * Gets a range template nodes.
25482      * @param {Number} startIndex
25483      * @param {Number} endIndex
25484      * @return {Array} An array of nodes
25485      */
25486     getNodes : function(start, end){
25487         var ns = this.nodes;
25488         start = start || 0;
25489         end = typeof end == "undefined" ? ns.length - 1 : end;
25490         var nodes = [];
25491         if(start <= end){
25492             for(var i = start; i <= end; i++){
25493                 nodes.push(ns[i]);
25494             }
25495         } else{
25496             for(var i = start; i >= end; i--){
25497                 nodes.push(ns[i]);
25498             }
25499         }
25500         return nodes;
25501     },
25502
25503     /**
25504      * Finds the index of the passed node
25505      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25506      * @return {Number} The index of the node or -1
25507      */
25508     indexOf : function(node){
25509         node = this.getNode(node);
25510         if(typeof node.nodeIndex == "number"){
25511             return node.nodeIndex;
25512         }
25513         var ns = this.nodes;
25514         for(var i = 0, len = ns.length; i < len; i++){
25515             if(ns[i] == node){
25516                 return i;
25517             }
25518         }
25519         return -1;
25520     }
25521 });
25522 /*
25523  * Based on:
25524  * Ext JS Library 1.1.1
25525  * Copyright(c) 2006-2007, Ext JS, LLC.
25526  *
25527  * Originally Released Under LGPL - original licence link has changed is not relivant.
25528  *
25529  * Fork - LGPL
25530  * <script type="text/javascript">
25531  */
25532
25533 /**
25534  * @class Roo.JsonView
25535  * @extends Roo.View
25536  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25537 <pre><code>
25538 var view = new Roo.JsonView({
25539     container: "my-element",
25540     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25541     multiSelect: true, 
25542     jsonRoot: "data" 
25543 });
25544
25545 // listen for node click?
25546 view.on("click", function(vw, index, node, e){
25547     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25548 });
25549
25550 // direct load of JSON data
25551 view.load("foobar.php");
25552
25553 // Example from my blog list
25554 var tpl = new Roo.Template(
25555     '&lt;div class="entry"&gt;' +
25556     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25557     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25558     "&lt;/div&gt;&lt;hr /&gt;"
25559 );
25560
25561 var moreView = new Roo.JsonView({
25562     container :  "entry-list", 
25563     template : tpl,
25564     jsonRoot: "posts"
25565 });
25566 moreView.on("beforerender", this.sortEntries, this);
25567 moreView.load({
25568     url: "/blog/get-posts.php",
25569     params: "allposts=true",
25570     text: "Loading Blog Entries..."
25571 });
25572 </code></pre>
25573
25574 * Note: old code is supported with arguments : (container, template, config)
25575
25576
25577  * @constructor
25578  * Create a new JsonView
25579  * 
25580  * @param {Object} config The config object
25581  * 
25582  */
25583 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25584     
25585     
25586     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25587
25588     var um = this.el.getUpdateManager();
25589     um.setRenderer(this);
25590     um.on("update", this.onLoad, this);
25591     um.on("failure", this.onLoadException, this);
25592
25593     /**
25594      * @event beforerender
25595      * Fires before rendering of the downloaded JSON data.
25596      * @param {Roo.JsonView} this
25597      * @param {Object} data The JSON data loaded
25598      */
25599     /**
25600      * @event load
25601      * Fires when data is loaded.
25602      * @param {Roo.JsonView} this
25603      * @param {Object} data The JSON data loaded
25604      * @param {Object} response The raw Connect response object
25605      */
25606     /**
25607      * @event loadexception
25608      * Fires when loading fails.
25609      * @param {Roo.JsonView} this
25610      * @param {Object} response The raw Connect response object
25611      */
25612     this.addEvents({
25613         'beforerender' : true,
25614         'load' : true,
25615         'loadexception' : true
25616     });
25617 };
25618 Roo.extend(Roo.JsonView, Roo.View, {
25619     /**
25620      * @type {String} The root property in the loaded JSON object that contains the data
25621      */
25622     jsonRoot : "",
25623
25624     /**
25625      * Refreshes the view.
25626      */
25627     refresh : function(){
25628         this.clearSelections();
25629         this.el.update("");
25630         var html = [];
25631         var o = this.jsonData;
25632         if(o && o.length > 0){
25633             for(var i = 0, len = o.length; i < len; i++){
25634                 var data = this.prepareData(o[i], i, o);
25635                 html[html.length] = this.tpl.apply(data);
25636             }
25637         }else{
25638             html.push(this.emptyText);
25639         }
25640         this.el.update(html.join(""));
25641         this.nodes = this.el.dom.childNodes;
25642         this.updateIndexes(0);
25643     },
25644
25645     /**
25646      * 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.
25647      * @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:
25648      <pre><code>
25649      view.load({
25650          url: "your-url.php",
25651          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25652          callback: yourFunction,
25653          scope: yourObject, //(optional scope)
25654          discardUrl: false,
25655          nocache: false,
25656          text: "Loading...",
25657          timeout: 30,
25658          scripts: false
25659      });
25660      </code></pre>
25661      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25662      * 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.
25663      * @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}
25664      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25665      * @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.
25666      */
25667     load : function(){
25668         var um = this.el.getUpdateManager();
25669         um.update.apply(um, arguments);
25670     },
25671
25672     render : function(el, response){
25673         this.clearSelections();
25674         this.el.update("");
25675         var o;
25676         try{
25677             o = Roo.util.JSON.decode(response.responseText);
25678             if(this.jsonRoot){
25679                 
25680                 o = o[this.jsonRoot];
25681             }
25682         } catch(e){
25683         }
25684         /**
25685          * The current JSON data or null
25686          */
25687         this.jsonData = o;
25688         this.beforeRender();
25689         this.refresh();
25690     },
25691
25692 /**
25693  * Get the number of records in the current JSON dataset
25694  * @return {Number}
25695  */
25696     getCount : function(){
25697         return this.jsonData ? this.jsonData.length : 0;
25698     },
25699
25700 /**
25701  * Returns the JSON object for the specified node(s)
25702  * @param {HTMLElement/Array} node The node or an array of nodes
25703  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25704  * you get the JSON object for the node
25705  */
25706     getNodeData : function(node){
25707         if(node instanceof Array){
25708             var data = [];
25709             for(var i = 0, len = node.length; i < len; i++){
25710                 data.push(this.getNodeData(node[i]));
25711             }
25712             return data;
25713         }
25714         return this.jsonData[this.indexOf(node)] || null;
25715     },
25716
25717     beforeRender : function(){
25718         this.snapshot = this.jsonData;
25719         if(this.sortInfo){
25720             this.sort.apply(this, this.sortInfo);
25721         }
25722         this.fireEvent("beforerender", this, this.jsonData);
25723     },
25724
25725     onLoad : function(el, o){
25726         this.fireEvent("load", this, this.jsonData, o);
25727     },
25728
25729     onLoadException : function(el, o){
25730         this.fireEvent("loadexception", this, o);
25731     },
25732
25733 /**
25734  * Filter the data by a specific property.
25735  * @param {String} property A property on your JSON objects
25736  * @param {String/RegExp} value Either string that the property values
25737  * should start with, or a RegExp to test against the property
25738  */
25739     filter : function(property, value){
25740         if(this.jsonData){
25741             var data = [];
25742             var ss = this.snapshot;
25743             if(typeof value == "string"){
25744                 var vlen = value.length;
25745                 if(vlen == 0){
25746                     this.clearFilter();
25747                     return;
25748                 }
25749                 value = value.toLowerCase();
25750                 for(var i = 0, len = ss.length; i < len; i++){
25751                     var o = ss[i];
25752                     if(o[property].substr(0, vlen).toLowerCase() == value){
25753                         data.push(o);
25754                     }
25755                 }
25756             } else if(value.exec){ // regex?
25757                 for(var i = 0, len = ss.length; i < len; i++){
25758                     var o = ss[i];
25759                     if(value.test(o[property])){
25760                         data.push(o);
25761                     }
25762                 }
25763             } else{
25764                 return;
25765             }
25766             this.jsonData = data;
25767             this.refresh();
25768         }
25769     },
25770
25771 /**
25772  * Filter by a function. The passed function will be called with each
25773  * object in the current dataset. If the function returns true the value is kept,
25774  * otherwise it is filtered.
25775  * @param {Function} fn
25776  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25777  */
25778     filterBy : function(fn, scope){
25779         if(this.jsonData){
25780             var data = [];
25781             var ss = this.snapshot;
25782             for(var i = 0, len = ss.length; i < len; i++){
25783                 var o = ss[i];
25784                 if(fn.call(scope || this, o)){
25785                     data.push(o);
25786                 }
25787             }
25788             this.jsonData = data;
25789             this.refresh();
25790         }
25791     },
25792
25793 /**
25794  * Clears the current filter.
25795  */
25796     clearFilter : function(){
25797         if(this.snapshot && this.jsonData != this.snapshot){
25798             this.jsonData = this.snapshot;
25799             this.refresh();
25800         }
25801     },
25802
25803
25804 /**
25805  * Sorts the data for this view and refreshes it.
25806  * @param {String} property A property on your JSON objects to sort on
25807  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25808  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25809  */
25810     sort : function(property, dir, sortType){
25811         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25812         if(this.jsonData){
25813             var p = property;
25814             var dsc = dir && dir.toLowerCase() == "desc";
25815             var f = function(o1, o2){
25816                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25817                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25818                 ;
25819                 if(v1 < v2){
25820                     return dsc ? +1 : -1;
25821                 } else if(v1 > v2){
25822                     return dsc ? -1 : +1;
25823                 } else{
25824                     return 0;
25825                 }
25826             };
25827             this.jsonData.sort(f);
25828             this.refresh();
25829             if(this.jsonData != this.snapshot){
25830                 this.snapshot.sort(f);
25831             }
25832         }
25833     }
25834 });/*
25835  * Based on:
25836  * Ext JS Library 1.1.1
25837  * Copyright(c) 2006-2007, Ext JS, LLC.
25838  *
25839  * Originally Released Under LGPL - original licence link has changed is not relivant.
25840  *
25841  * Fork - LGPL
25842  * <script type="text/javascript">
25843  */
25844  
25845
25846 /**
25847  * @class Roo.ColorPalette
25848  * @extends Roo.Component
25849  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25850  * Here's an example of typical usage:
25851  * <pre><code>
25852 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25853 cp.render('my-div');
25854
25855 cp.on('select', function(palette, selColor){
25856     // do something with selColor
25857 });
25858 </code></pre>
25859  * @constructor
25860  * Create a new ColorPalette
25861  * @param {Object} config The config object
25862  */
25863 Roo.ColorPalette = function(config){
25864     Roo.ColorPalette.superclass.constructor.call(this, config);
25865     this.addEvents({
25866         /**
25867              * @event select
25868              * Fires when a color is selected
25869              * @param {ColorPalette} this
25870              * @param {String} color The 6-digit color hex code (without the # symbol)
25871              */
25872         select: true
25873     });
25874
25875     if(this.handler){
25876         this.on("select", this.handler, this.scope, true);
25877     }
25878 };
25879 Roo.extend(Roo.ColorPalette, Roo.Component, {
25880     /**
25881      * @cfg {String} itemCls
25882      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25883      */
25884     itemCls : "x-color-palette",
25885     /**
25886      * @cfg {String} value
25887      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25888      * the hex codes are case-sensitive.
25889      */
25890     value : null,
25891     clickEvent:'click',
25892     // private
25893     ctype: "Roo.ColorPalette",
25894
25895     /**
25896      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25897      */
25898     allowReselect : false,
25899
25900     /**
25901      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25902      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25903      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25904      * of colors with the width setting until the box is symmetrical.</p>
25905      * <p>You can override individual colors if needed:</p>
25906      * <pre><code>
25907 var cp = new Roo.ColorPalette();
25908 cp.colors[0] = "FF0000";  // change the first box to red
25909 </code></pre>
25910
25911 Or you can provide a custom array of your own for complete control:
25912 <pre><code>
25913 var cp = new Roo.ColorPalette();
25914 cp.colors = ["000000", "993300", "333300"];
25915 </code></pre>
25916      * @type Array
25917      */
25918     colors : [
25919         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25920         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25921         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25922         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25923         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25924     ],
25925
25926     // private
25927     onRender : function(container, position){
25928         var t = new Roo.MasterTemplate(
25929             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25930         );
25931         var c = this.colors;
25932         for(var i = 0, len = c.length; i < len; i++){
25933             t.add([c[i]]);
25934         }
25935         var el = document.createElement("div");
25936         el.className = this.itemCls;
25937         t.overwrite(el);
25938         container.dom.insertBefore(el, position);
25939         this.el = Roo.get(el);
25940         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25941         if(this.clickEvent != 'click'){
25942             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25943         }
25944     },
25945
25946     // private
25947     afterRender : function(){
25948         Roo.ColorPalette.superclass.afterRender.call(this);
25949         if(this.value){
25950             var s = this.value;
25951             this.value = null;
25952             this.select(s);
25953         }
25954     },
25955
25956     // private
25957     handleClick : function(e, t){
25958         e.preventDefault();
25959         if(!this.disabled){
25960             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25961             this.select(c.toUpperCase());
25962         }
25963     },
25964
25965     /**
25966      * Selects the specified color in the palette (fires the select event)
25967      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25968      */
25969     select : function(color){
25970         color = color.replace("#", "");
25971         if(color != this.value || this.allowReselect){
25972             var el = this.el;
25973             if(this.value){
25974                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25975             }
25976             el.child("a.color-"+color).addClass("x-color-palette-sel");
25977             this.value = color;
25978             this.fireEvent("select", this, color);
25979         }
25980     }
25981 });/*
25982  * Based on:
25983  * Ext JS Library 1.1.1
25984  * Copyright(c) 2006-2007, Ext JS, LLC.
25985  *
25986  * Originally Released Under LGPL - original licence link has changed is not relivant.
25987  *
25988  * Fork - LGPL
25989  * <script type="text/javascript">
25990  */
25991  
25992 /**
25993  * @class Roo.DatePicker
25994  * @extends Roo.Component
25995  * Simple date picker class.
25996  * @constructor
25997  * Create a new DatePicker
25998  * @param {Object} config The config object
25999  */
26000 Roo.DatePicker = function(config){
26001     Roo.DatePicker.superclass.constructor.call(this, config);
26002
26003     this.value = config && config.value ?
26004                  config.value.clearTime() : new Date().clearTime();
26005
26006     this.addEvents({
26007         /**
26008              * @event select
26009              * Fires when a date is selected
26010              * @param {DatePicker} this
26011              * @param {Date} date The selected date
26012              */
26013         'select': true,
26014         /**
26015              * @event monthchange
26016              * Fires when the displayed month changes 
26017              * @param {DatePicker} this
26018              * @param {Date} date The selected month
26019              */
26020         'monthchange': true
26021     });
26022
26023     if(this.handler){
26024         this.on("select", this.handler,  this.scope || this);
26025     }
26026     // build the disabledDatesRE
26027     if(!this.disabledDatesRE && this.disabledDates){
26028         var dd = this.disabledDates;
26029         var re = "(?:";
26030         for(var i = 0; i < dd.length; i++){
26031             re += dd[i];
26032             if(i != dd.length-1) re += "|";
26033         }
26034         this.disabledDatesRE = new RegExp(re + ")");
26035     }
26036 };
26037
26038 Roo.extend(Roo.DatePicker, Roo.Component, {
26039     /**
26040      * @cfg {String} todayText
26041      * The text to display on the button that selects the current date (defaults to "Today")
26042      */
26043     todayText : "Today",
26044     /**
26045      * @cfg {String} okText
26046      * The text to display on the ok button
26047      */
26048     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26049     /**
26050      * @cfg {String} cancelText
26051      * The text to display on the cancel button
26052      */
26053     cancelText : "Cancel",
26054     /**
26055      * @cfg {String} todayTip
26056      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26057      */
26058     todayTip : "{0} (Spacebar)",
26059     /**
26060      * @cfg {Date} minDate
26061      * Minimum allowable date (JavaScript date object, defaults to null)
26062      */
26063     minDate : null,
26064     /**
26065      * @cfg {Date} maxDate
26066      * Maximum allowable date (JavaScript date object, defaults to null)
26067      */
26068     maxDate : null,
26069     /**
26070      * @cfg {String} minText
26071      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26072      */
26073     minText : "This date is before the minimum date",
26074     /**
26075      * @cfg {String} maxText
26076      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26077      */
26078     maxText : "This date is after the maximum date",
26079     /**
26080      * @cfg {String} format
26081      * The default date format string which can be overriden for localization support.  The format must be
26082      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26083      */
26084     format : "m/d/y",
26085     /**
26086      * @cfg {Array} disabledDays
26087      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26088      */
26089     disabledDays : null,
26090     /**
26091      * @cfg {String} disabledDaysText
26092      * The tooltip to display when the date falls on a disabled day (defaults to "")
26093      */
26094     disabledDaysText : "",
26095     /**
26096      * @cfg {RegExp} disabledDatesRE
26097      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26098      */
26099     disabledDatesRE : null,
26100     /**
26101      * @cfg {String} disabledDatesText
26102      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26103      */
26104     disabledDatesText : "",
26105     /**
26106      * @cfg {Boolean} constrainToViewport
26107      * True to constrain the date picker to the viewport (defaults to true)
26108      */
26109     constrainToViewport : true,
26110     /**
26111      * @cfg {Array} monthNames
26112      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26113      */
26114     monthNames : Date.monthNames,
26115     /**
26116      * @cfg {Array} dayNames
26117      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26118      */
26119     dayNames : Date.dayNames,
26120     /**
26121      * @cfg {String} nextText
26122      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26123      */
26124     nextText: 'Next Month (Control+Right)',
26125     /**
26126      * @cfg {String} prevText
26127      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26128      */
26129     prevText: 'Previous Month (Control+Left)',
26130     /**
26131      * @cfg {String} monthYearText
26132      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26133      */
26134     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26135     /**
26136      * @cfg {Number} startDay
26137      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26138      */
26139     startDay : 0,
26140     /**
26141      * @cfg {Bool} showClear
26142      * Show a clear button (usefull for date form elements that can be blank.)
26143      */
26144     
26145     showClear: false,
26146     
26147     /**
26148      * Sets the value of the date field
26149      * @param {Date} value The date to set
26150      */
26151     setValue : function(value){
26152         var old = this.value;
26153         
26154         if (typeof(value) == 'string') {
26155          
26156             value = Date.parseDate(value, this.format);
26157         }
26158         if (!value) {
26159             value = new Date();
26160         }
26161         
26162         this.value = value.clearTime(true);
26163         if(this.el){
26164             this.update(this.value);
26165         }
26166     },
26167
26168     /**
26169      * Gets the current selected value of the date field
26170      * @return {Date} The selected date
26171      */
26172     getValue : function(){
26173         return this.value;
26174     },
26175
26176     // private
26177     focus : function(){
26178         if(this.el){
26179             this.update(this.activeDate);
26180         }
26181     },
26182
26183     // privateval
26184     onRender : function(container, position){
26185         
26186         var m = [
26187              '<table cellspacing="0">',
26188                 '<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>',
26189                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26190         var dn = this.dayNames;
26191         for(var i = 0; i < 7; i++){
26192             var d = this.startDay+i;
26193             if(d > 6){
26194                 d = d-7;
26195             }
26196             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26197         }
26198         m[m.length] = "</tr></thead><tbody><tr>";
26199         for(var i = 0; i < 42; i++) {
26200             if(i % 7 == 0 && i != 0){
26201                 m[m.length] = "</tr><tr>";
26202             }
26203             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26204         }
26205         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26206             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26207
26208         var el = document.createElement("div");
26209         el.className = "x-date-picker";
26210         el.innerHTML = m.join("");
26211
26212         container.dom.insertBefore(el, position);
26213
26214         this.el = Roo.get(el);
26215         this.eventEl = Roo.get(el.firstChild);
26216
26217         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26218             handler: this.showPrevMonth,
26219             scope: this,
26220             preventDefault:true,
26221             stopDefault:true
26222         });
26223
26224         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26225             handler: this.showNextMonth,
26226             scope: this,
26227             preventDefault:true,
26228             stopDefault:true
26229         });
26230
26231         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26232
26233         this.monthPicker = this.el.down('div.x-date-mp');
26234         this.monthPicker.enableDisplayMode('block');
26235         
26236         var kn = new Roo.KeyNav(this.eventEl, {
26237             "left" : function(e){
26238                 e.ctrlKey ?
26239                     this.showPrevMonth() :
26240                     this.update(this.activeDate.add("d", -1));
26241             },
26242
26243             "right" : function(e){
26244                 e.ctrlKey ?
26245                     this.showNextMonth() :
26246                     this.update(this.activeDate.add("d", 1));
26247             },
26248
26249             "up" : function(e){
26250                 e.ctrlKey ?
26251                     this.showNextYear() :
26252                     this.update(this.activeDate.add("d", -7));
26253             },
26254
26255             "down" : function(e){
26256                 e.ctrlKey ?
26257                     this.showPrevYear() :
26258                     this.update(this.activeDate.add("d", 7));
26259             },
26260
26261             "pageUp" : function(e){
26262                 this.showNextMonth();
26263             },
26264
26265             "pageDown" : function(e){
26266                 this.showPrevMonth();
26267             },
26268
26269             "enter" : function(e){
26270                 e.stopPropagation();
26271                 return true;
26272             },
26273
26274             scope : this
26275         });
26276
26277         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26278
26279         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26280
26281         this.el.unselectable();
26282         
26283         this.cells = this.el.select("table.x-date-inner tbody td");
26284         this.textNodes = this.el.query("table.x-date-inner tbody span");
26285
26286         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26287             text: "&#160;",
26288             tooltip: this.monthYearText
26289         });
26290
26291         this.mbtn.on('click', this.showMonthPicker, this);
26292         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26293
26294
26295         var today = (new Date()).dateFormat(this.format);
26296         
26297         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26298         if (this.showClear) {
26299             baseTb.add( new Roo.Toolbar.Fill());
26300         }
26301         baseTb.add({
26302             text: String.format(this.todayText, today),
26303             tooltip: String.format(this.todayTip, today),
26304             handler: this.selectToday,
26305             scope: this
26306         });
26307         
26308         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26309             
26310         //});
26311         if (this.showClear) {
26312             
26313             baseTb.add( new Roo.Toolbar.Fill());
26314             baseTb.add({
26315                 text: '&#160;',
26316                 cls: 'x-btn-icon x-btn-clear',
26317                 handler: function() {
26318                     //this.value = '';
26319                     this.fireEvent("select", this, '');
26320                 },
26321                 scope: this
26322             });
26323         }
26324         
26325         
26326         if(Roo.isIE){
26327             this.el.repaint();
26328         }
26329         this.update(this.value);
26330     },
26331
26332     createMonthPicker : function(){
26333         if(!this.monthPicker.dom.firstChild){
26334             var buf = ['<table border="0" cellspacing="0">'];
26335             for(var i = 0; i < 6; i++){
26336                 buf.push(
26337                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26338                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26339                     i == 0 ?
26340                     '<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>' :
26341                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26342                 );
26343             }
26344             buf.push(
26345                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26346                     this.okText,
26347                     '</button><button type="button" class="x-date-mp-cancel">',
26348                     this.cancelText,
26349                     '</button></td></tr>',
26350                 '</table>'
26351             );
26352             this.monthPicker.update(buf.join(''));
26353             this.monthPicker.on('click', this.onMonthClick, this);
26354             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26355
26356             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26357             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26358
26359             this.mpMonths.each(function(m, a, i){
26360                 i += 1;
26361                 if((i%2) == 0){
26362                     m.dom.xmonth = 5 + Math.round(i * .5);
26363                 }else{
26364                     m.dom.xmonth = Math.round((i-1) * .5);
26365                 }
26366             });
26367         }
26368     },
26369
26370     showMonthPicker : function(){
26371         this.createMonthPicker();
26372         var size = this.el.getSize();
26373         this.monthPicker.setSize(size);
26374         this.monthPicker.child('table').setSize(size);
26375
26376         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26377         this.updateMPMonth(this.mpSelMonth);
26378         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26379         this.updateMPYear(this.mpSelYear);
26380
26381         this.monthPicker.slideIn('t', {duration:.2});
26382     },
26383
26384     updateMPYear : function(y){
26385         this.mpyear = y;
26386         var ys = this.mpYears.elements;
26387         for(var i = 1; i <= 10; i++){
26388             var td = ys[i-1], y2;
26389             if((i%2) == 0){
26390                 y2 = y + Math.round(i * .5);
26391                 td.firstChild.innerHTML = y2;
26392                 td.xyear = y2;
26393             }else{
26394                 y2 = y - (5-Math.round(i * .5));
26395                 td.firstChild.innerHTML = y2;
26396                 td.xyear = y2;
26397             }
26398             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26399         }
26400     },
26401
26402     updateMPMonth : function(sm){
26403         this.mpMonths.each(function(m, a, i){
26404             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26405         });
26406     },
26407
26408     selectMPMonth: function(m){
26409         
26410     },
26411
26412     onMonthClick : function(e, t){
26413         e.stopEvent();
26414         var el = new Roo.Element(t), pn;
26415         if(el.is('button.x-date-mp-cancel')){
26416             this.hideMonthPicker();
26417         }
26418         else if(el.is('button.x-date-mp-ok')){
26419             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26420             this.hideMonthPicker();
26421         }
26422         else if(pn = el.up('td.x-date-mp-month', 2)){
26423             this.mpMonths.removeClass('x-date-mp-sel');
26424             pn.addClass('x-date-mp-sel');
26425             this.mpSelMonth = pn.dom.xmonth;
26426         }
26427         else if(pn = el.up('td.x-date-mp-year', 2)){
26428             this.mpYears.removeClass('x-date-mp-sel');
26429             pn.addClass('x-date-mp-sel');
26430             this.mpSelYear = pn.dom.xyear;
26431         }
26432         else if(el.is('a.x-date-mp-prev')){
26433             this.updateMPYear(this.mpyear-10);
26434         }
26435         else if(el.is('a.x-date-mp-next')){
26436             this.updateMPYear(this.mpyear+10);
26437         }
26438     },
26439
26440     onMonthDblClick : function(e, t){
26441         e.stopEvent();
26442         var el = new Roo.Element(t), pn;
26443         if(pn = el.up('td.x-date-mp-month', 2)){
26444             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26445             this.hideMonthPicker();
26446         }
26447         else if(pn = el.up('td.x-date-mp-year', 2)){
26448             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26449             this.hideMonthPicker();
26450         }
26451     },
26452
26453     hideMonthPicker : function(disableAnim){
26454         if(this.monthPicker){
26455             if(disableAnim === true){
26456                 this.monthPicker.hide();
26457             }else{
26458                 this.monthPicker.slideOut('t', {duration:.2});
26459             }
26460         }
26461     },
26462
26463     // private
26464     showPrevMonth : function(e){
26465         this.update(this.activeDate.add("mo", -1));
26466     },
26467
26468     // private
26469     showNextMonth : function(e){
26470         this.update(this.activeDate.add("mo", 1));
26471     },
26472
26473     // private
26474     showPrevYear : function(){
26475         this.update(this.activeDate.add("y", -1));
26476     },
26477
26478     // private
26479     showNextYear : function(){
26480         this.update(this.activeDate.add("y", 1));
26481     },
26482
26483     // private
26484     handleMouseWheel : function(e){
26485         var delta = e.getWheelDelta();
26486         if(delta > 0){
26487             this.showPrevMonth();
26488             e.stopEvent();
26489         } else if(delta < 0){
26490             this.showNextMonth();
26491             e.stopEvent();
26492         }
26493     },
26494
26495     // private
26496     handleDateClick : function(e, t){
26497         e.stopEvent();
26498         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26499             this.setValue(new Date(t.dateValue));
26500             this.fireEvent("select", this, this.value);
26501         }
26502     },
26503
26504     // private
26505     selectToday : function(){
26506         this.setValue(new Date().clearTime());
26507         this.fireEvent("select", this, this.value);
26508     },
26509
26510     // private
26511     update : function(date)
26512     {
26513         var vd = this.activeDate;
26514         this.activeDate = date;
26515         if(vd && this.el){
26516             var t = date.getTime();
26517             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26518                 this.cells.removeClass("x-date-selected");
26519                 this.cells.each(function(c){
26520                    if(c.dom.firstChild.dateValue == t){
26521                        c.addClass("x-date-selected");
26522                        setTimeout(function(){
26523                             try{c.dom.firstChild.focus();}catch(e){}
26524                        }, 50);
26525                        return false;
26526                    }
26527                 });
26528                 return;
26529             }
26530         }
26531         
26532         var days = date.getDaysInMonth();
26533         var firstOfMonth = date.getFirstDateOfMonth();
26534         var startingPos = firstOfMonth.getDay()-this.startDay;
26535
26536         if(startingPos <= this.startDay){
26537             startingPos += 7;
26538         }
26539
26540         var pm = date.add("mo", -1);
26541         var prevStart = pm.getDaysInMonth()-startingPos;
26542
26543         var cells = this.cells.elements;
26544         var textEls = this.textNodes;
26545         days += startingPos;
26546
26547         // convert everything to numbers so it's fast
26548         var day = 86400000;
26549         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26550         var today = new Date().clearTime().getTime();
26551         var sel = date.clearTime().getTime();
26552         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26553         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26554         var ddMatch = this.disabledDatesRE;
26555         var ddText = this.disabledDatesText;
26556         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26557         var ddaysText = this.disabledDaysText;
26558         var format = this.format;
26559
26560         var setCellClass = function(cal, cell){
26561             cell.title = "";
26562             var t = d.getTime();
26563             cell.firstChild.dateValue = t;
26564             if(t == today){
26565                 cell.className += " x-date-today";
26566                 cell.title = cal.todayText;
26567             }
26568             if(t == sel){
26569                 cell.className += " x-date-selected";
26570                 setTimeout(function(){
26571                     try{cell.firstChild.focus();}catch(e){}
26572                 }, 50);
26573             }
26574             // disabling
26575             if(t < min) {
26576                 cell.className = " x-date-disabled";
26577                 cell.title = cal.minText;
26578                 return;
26579             }
26580             if(t > max) {
26581                 cell.className = " x-date-disabled";
26582                 cell.title = cal.maxText;
26583                 return;
26584             }
26585             if(ddays){
26586                 if(ddays.indexOf(d.getDay()) != -1){
26587                     cell.title = ddaysText;
26588                     cell.className = " x-date-disabled";
26589                 }
26590             }
26591             if(ddMatch && format){
26592                 var fvalue = d.dateFormat(format);
26593                 if(ddMatch.test(fvalue)){
26594                     cell.title = ddText.replace("%0", fvalue);
26595                     cell.className = " x-date-disabled";
26596                 }
26597             }
26598         };
26599
26600         var i = 0;
26601         for(; i < startingPos; i++) {
26602             textEls[i].innerHTML = (++prevStart);
26603             d.setDate(d.getDate()+1);
26604             cells[i].className = "x-date-prevday";
26605             setCellClass(this, cells[i]);
26606         }
26607         for(; i < days; i++){
26608             intDay = i - startingPos + 1;
26609             textEls[i].innerHTML = (intDay);
26610             d.setDate(d.getDate()+1);
26611             cells[i].className = "x-date-active";
26612             setCellClass(this, cells[i]);
26613         }
26614         var extraDays = 0;
26615         for(; i < 42; i++) {
26616              textEls[i].innerHTML = (++extraDays);
26617              d.setDate(d.getDate()+1);
26618              cells[i].className = "x-date-nextday";
26619              setCellClass(this, cells[i]);
26620         }
26621
26622         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26623         this.fireEvent('monthchange', this, date);
26624         
26625         if(!this.internalRender){
26626             var main = this.el.dom.firstChild;
26627             var w = main.offsetWidth;
26628             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26629             Roo.fly(main).setWidth(w);
26630             this.internalRender = true;
26631             // opera does not respect the auto grow header center column
26632             // then, after it gets a width opera refuses to recalculate
26633             // without a second pass
26634             if(Roo.isOpera && !this.secondPass){
26635                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26636                 this.secondPass = true;
26637                 this.update.defer(10, this, [date]);
26638             }
26639         }
26640         
26641         
26642     }
26643 });        /*
26644  * Based on:
26645  * Ext JS Library 1.1.1
26646  * Copyright(c) 2006-2007, Ext JS, LLC.
26647  *
26648  * Originally Released Under LGPL - original licence link has changed is not relivant.
26649  *
26650  * Fork - LGPL
26651  * <script type="text/javascript">
26652  */
26653 /**
26654  * @class Roo.TabPanel
26655  * @extends Roo.util.Observable
26656  * A lightweight tab container.
26657  * <br><br>
26658  * Usage:
26659  * <pre><code>
26660 // basic tabs 1, built from existing content
26661 var tabs = new Roo.TabPanel("tabs1");
26662 tabs.addTab("script", "View Script");
26663 tabs.addTab("markup", "View Markup");
26664 tabs.activate("script");
26665
26666 // more advanced tabs, built from javascript
26667 var jtabs = new Roo.TabPanel("jtabs");
26668 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26669
26670 // set up the UpdateManager
26671 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26672 var updater = tab2.getUpdateManager();
26673 updater.setDefaultUrl("ajax1.htm");
26674 tab2.on('activate', updater.refresh, updater, true);
26675
26676 // Use setUrl for Ajax loading
26677 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26678 tab3.setUrl("ajax2.htm", null, true);
26679
26680 // Disabled tab
26681 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26682 tab4.disable();
26683
26684 jtabs.activate("jtabs-1");
26685  * </code></pre>
26686  * @constructor
26687  * Create a new TabPanel.
26688  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26689  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26690  */
26691 Roo.TabPanel = function(container, config){
26692     /**
26693     * The container element for this TabPanel.
26694     * @type Roo.Element
26695     */
26696     this.el = Roo.get(container, true);
26697     if(config){
26698         if(typeof config == "boolean"){
26699             this.tabPosition = config ? "bottom" : "top";
26700         }else{
26701             Roo.apply(this, config);
26702         }
26703     }
26704     if(this.tabPosition == "bottom"){
26705         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26706         this.el.addClass("x-tabs-bottom");
26707     }
26708     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26709     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26710     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26711     if(Roo.isIE){
26712         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26713     }
26714     if(this.tabPosition != "bottom"){
26715         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26716          * @type Roo.Element
26717          */
26718         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26719         this.el.addClass("x-tabs-top");
26720     }
26721     this.items = [];
26722
26723     this.bodyEl.setStyle("position", "relative");
26724
26725     this.active = null;
26726     this.activateDelegate = this.activate.createDelegate(this);
26727
26728     this.addEvents({
26729         /**
26730          * @event tabchange
26731          * Fires when the active tab changes
26732          * @param {Roo.TabPanel} this
26733          * @param {Roo.TabPanelItem} activePanel The new active tab
26734          */
26735         "tabchange": true,
26736         /**
26737          * @event beforetabchange
26738          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26739          * @param {Roo.TabPanel} this
26740          * @param {Object} e Set cancel to true on this object to cancel the tab change
26741          * @param {Roo.TabPanelItem} tab The tab being changed to
26742          */
26743         "beforetabchange" : true
26744     });
26745
26746     Roo.EventManager.onWindowResize(this.onResize, this);
26747     this.cpad = this.el.getPadding("lr");
26748     this.hiddenCount = 0;
26749
26750
26751     // toolbar on the tabbar support...
26752     if (this.toolbar) {
26753         var tcfg = this.toolbar;
26754         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26755         this.toolbar = new Roo.Toolbar(tcfg);
26756         if (Roo.isSafari) {
26757             var tbl = tcfg.container.child('table', true);
26758             tbl.setAttribute('width', '100%');
26759         }
26760         
26761     }
26762    
26763
26764
26765     Roo.TabPanel.superclass.constructor.call(this);
26766 };
26767
26768 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26769     /*
26770      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26771      */
26772     tabPosition : "top",
26773     /*
26774      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26775      */
26776     currentTabWidth : 0,
26777     /*
26778      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26779      */
26780     minTabWidth : 40,
26781     /*
26782      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26783      */
26784     maxTabWidth : 250,
26785     /*
26786      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26787      */
26788     preferredTabWidth : 175,
26789     /*
26790      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26791      */
26792     resizeTabs : false,
26793     /*
26794      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26795      */
26796     monitorResize : true,
26797     /*
26798      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26799      */
26800     toolbar : false,
26801
26802     /**
26803      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26804      * @param {String} id The id of the div to use <b>or create</b>
26805      * @param {String} text The text for the tab
26806      * @param {String} content (optional) Content to put in the TabPanelItem body
26807      * @param {Boolean} closable (optional) True to create a close icon on the tab
26808      * @return {Roo.TabPanelItem} The created TabPanelItem
26809      */
26810     addTab : function(id, text, content, closable){
26811         var item = new Roo.TabPanelItem(this, id, text, closable);
26812         this.addTabItem(item);
26813         if(content){
26814             item.setContent(content);
26815         }
26816         return item;
26817     },
26818
26819     /**
26820      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26821      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26822      * @return {Roo.TabPanelItem}
26823      */
26824     getTab : function(id){
26825         return this.items[id];
26826     },
26827
26828     /**
26829      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26830      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26831      */
26832     hideTab : function(id){
26833         var t = this.items[id];
26834         if(!t.isHidden()){
26835            t.setHidden(true);
26836            this.hiddenCount++;
26837            this.autoSizeTabs();
26838         }
26839     },
26840
26841     /**
26842      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26843      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26844      */
26845     unhideTab : function(id){
26846         var t = this.items[id];
26847         if(t.isHidden()){
26848            t.setHidden(false);
26849            this.hiddenCount--;
26850            this.autoSizeTabs();
26851         }
26852     },
26853
26854     /**
26855      * Adds an existing {@link Roo.TabPanelItem}.
26856      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26857      */
26858     addTabItem : function(item){
26859         this.items[item.id] = item;
26860         this.items.push(item);
26861         if(this.resizeTabs){
26862            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26863            this.autoSizeTabs();
26864         }else{
26865             item.autoSize();
26866         }
26867     },
26868
26869     /**
26870      * Removes a {@link Roo.TabPanelItem}.
26871      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26872      */
26873     removeTab : function(id){
26874         var items = this.items;
26875         var tab = items[id];
26876         if(!tab) { return; }
26877         var index = items.indexOf(tab);
26878         if(this.active == tab && items.length > 1){
26879             var newTab = this.getNextAvailable(index);
26880             if(newTab) {
26881                 newTab.activate();
26882             }
26883         }
26884         this.stripEl.dom.removeChild(tab.pnode.dom);
26885         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26886             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26887         }
26888         items.splice(index, 1);
26889         delete this.items[tab.id];
26890         tab.fireEvent("close", tab);
26891         tab.purgeListeners();
26892         this.autoSizeTabs();
26893     },
26894
26895     getNextAvailable : function(start){
26896         var items = this.items;
26897         var index = start;
26898         // look for a next tab that will slide over to
26899         // replace the one being removed
26900         while(index < items.length){
26901             var item = items[++index];
26902             if(item && !item.isHidden()){
26903                 return item;
26904             }
26905         }
26906         // if one isn't found select the previous tab (on the left)
26907         index = start;
26908         while(index >= 0){
26909             var item = items[--index];
26910             if(item && !item.isHidden()){
26911                 return item;
26912             }
26913         }
26914         return null;
26915     },
26916
26917     /**
26918      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26919      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26920      */
26921     disableTab : function(id){
26922         var tab = this.items[id];
26923         if(tab && this.active != tab){
26924             tab.disable();
26925         }
26926     },
26927
26928     /**
26929      * Enables a {@link Roo.TabPanelItem} that is disabled.
26930      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26931      */
26932     enableTab : function(id){
26933         var tab = this.items[id];
26934         tab.enable();
26935     },
26936
26937     /**
26938      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26939      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26940      * @return {Roo.TabPanelItem} The TabPanelItem.
26941      */
26942     activate : function(id){
26943         var tab = this.items[id];
26944         if(!tab){
26945             return null;
26946         }
26947         if(tab == this.active || tab.disabled){
26948             return tab;
26949         }
26950         var e = {};
26951         this.fireEvent("beforetabchange", this, e, tab);
26952         if(e.cancel !== true && !tab.disabled){
26953             if(this.active){
26954                 this.active.hide();
26955             }
26956             this.active = this.items[id];
26957             this.active.show();
26958             this.fireEvent("tabchange", this, this.active);
26959         }
26960         return tab;
26961     },
26962
26963     /**
26964      * Gets the active {@link Roo.TabPanelItem}.
26965      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26966      */
26967     getActiveTab : function(){
26968         return this.active;
26969     },
26970
26971     /**
26972      * Updates the tab body element to fit the height of the container element
26973      * for overflow scrolling
26974      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26975      */
26976     syncHeight : function(targetHeight){
26977         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26978         var bm = this.bodyEl.getMargins();
26979         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26980         this.bodyEl.setHeight(newHeight);
26981         return newHeight;
26982     },
26983
26984     onResize : function(){
26985         if(this.monitorResize){
26986             this.autoSizeTabs();
26987         }
26988     },
26989
26990     /**
26991      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26992      */
26993     beginUpdate : function(){
26994         this.updating = true;
26995     },
26996
26997     /**
26998      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26999      */
27000     endUpdate : function(){
27001         this.updating = false;
27002         this.autoSizeTabs();
27003     },
27004
27005     /**
27006      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27007      */
27008     autoSizeTabs : function(){
27009         var count = this.items.length;
27010         var vcount = count - this.hiddenCount;
27011         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27012         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27013         var availWidth = Math.floor(w / vcount);
27014         var b = this.stripBody;
27015         if(b.getWidth() > w){
27016             var tabs = this.items;
27017             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27018             if(availWidth < this.minTabWidth){
27019                 /*if(!this.sleft){    // incomplete scrolling code
27020                     this.createScrollButtons();
27021                 }
27022                 this.showScroll();
27023                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27024             }
27025         }else{
27026             if(this.currentTabWidth < this.preferredTabWidth){
27027                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27028             }
27029         }
27030     },
27031
27032     /**
27033      * Returns the number of tabs in this TabPanel.
27034      * @return {Number}
27035      */
27036      getCount : function(){
27037          return this.items.length;
27038      },
27039
27040     /**
27041      * Resizes all the tabs to the passed width
27042      * @param {Number} The new width
27043      */
27044     setTabWidth : function(width){
27045         this.currentTabWidth = width;
27046         for(var i = 0, len = this.items.length; i < len; i++) {
27047                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27048         }
27049     },
27050
27051     /**
27052      * Destroys this TabPanel
27053      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27054      */
27055     destroy : function(removeEl){
27056         Roo.EventManager.removeResizeListener(this.onResize, this);
27057         for(var i = 0, len = this.items.length; i < len; i++){
27058             this.items[i].purgeListeners();
27059         }
27060         if(removeEl === true){
27061             this.el.update("");
27062             this.el.remove();
27063         }
27064     }
27065 });
27066
27067 /**
27068  * @class Roo.TabPanelItem
27069  * @extends Roo.util.Observable
27070  * Represents an individual item (tab plus body) in a TabPanel.
27071  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27072  * @param {String} id The id of this TabPanelItem
27073  * @param {String} text The text for the tab of this TabPanelItem
27074  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27075  */
27076 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27077     /**
27078      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27079      * @type Roo.TabPanel
27080      */
27081     this.tabPanel = tabPanel;
27082     /**
27083      * The id for this TabPanelItem
27084      * @type String
27085      */
27086     this.id = id;
27087     /** @private */
27088     this.disabled = false;
27089     /** @private */
27090     this.text = text;
27091     /** @private */
27092     this.loaded = false;
27093     this.closable = closable;
27094
27095     /**
27096      * The body element for this TabPanelItem.
27097      * @type Roo.Element
27098      */
27099     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27100     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27101     this.bodyEl.setStyle("display", "block");
27102     this.bodyEl.setStyle("zoom", "1");
27103     this.hideAction();
27104
27105     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27106     /** @private */
27107     this.el = Roo.get(els.el, true);
27108     this.inner = Roo.get(els.inner, true);
27109     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27110     this.pnode = Roo.get(els.el.parentNode, true);
27111     this.el.on("mousedown", this.onTabMouseDown, this);
27112     this.el.on("click", this.onTabClick, this);
27113     /** @private */
27114     if(closable){
27115         var c = Roo.get(els.close, true);
27116         c.dom.title = this.closeText;
27117         c.addClassOnOver("close-over");
27118         c.on("click", this.closeClick, this);
27119      }
27120
27121     this.addEvents({
27122          /**
27123          * @event activate
27124          * Fires when this tab becomes the active tab.
27125          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27126          * @param {Roo.TabPanelItem} this
27127          */
27128         "activate": true,
27129         /**
27130          * @event beforeclose
27131          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27132          * @param {Roo.TabPanelItem} this
27133          * @param {Object} e Set cancel to true on this object to cancel the close.
27134          */
27135         "beforeclose": true,
27136         /**
27137          * @event close
27138          * Fires when this tab is closed.
27139          * @param {Roo.TabPanelItem} this
27140          */
27141          "close": true,
27142         /**
27143          * @event deactivate
27144          * Fires when this tab is no longer the active tab.
27145          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27146          * @param {Roo.TabPanelItem} this
27147          */
27148          "deactivate" : true
27149     });
27150     this.hidden = false;
27151
27152     Roo.TabPanelItem.superclass.constructor.call(this);
27153 };
27154
27155 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27156     purgeListeners : function(){
27157        Roo.util.Observable.prototype.purgeListeners.call(this);
27158        this.el.removeAllListeners();
27159     },
27160     /**
27161      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27162      */
27163     show : function(){
27164         this.pnode.addClass("on");
27165         this.showAction();
27166         if(Roo.isOpera){
27167             this.tabPanel.stripWrap.repaint();
27168         }
27169         this.fireEvent("activate", this.tabPanel, this);
27170     },
27171
27172     /**
27173      * Returns true if this tab is the active tab.
27174      * @return {Boolean}
27175      */
27176     isActive : function(){
27177         return this.tabPanel.getActiveTab() == this;
27178     },
27179
27180     /**
27181      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27182      */
27183     hide : function(){
27184         this.pnode.removeClass("on");
27185         this.hideAction();
27186         this.fireEvent("deactivate", this.tabPanel, this);
27187     },
27188
27189     hideAction : function(){
27190         this.bodyEl.hide();
27191         this.bodyEl.setStyle("position", "absolute");
27192         this.bodyEl.setLeft("-20000px");
27193         this.bodyEl.setTop("-20000px");
27194     },
27195
27196     showAction : function(){
27197         this.bodyEl.setStyle("position", "relative");
27198         this.bodyEl.setTop("");
27199         this.bodyEl.setLeft("");
27200         this.bodyEl.show();
27201     },
27202
27203     /**
27204      * Set the tooltip for the tab.
27205      * @param {String} tooltip The tab's tooltip
27206      */
27207     setTooltip : function(text){
27208         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27209             this.textEl.dom.qtip = text;
27210             this.textEl.dom.removeAttribute('title');
27211         }else{
27212             this.textEl.dom.title = text;
27213         }
27214     },
27215
27216     onTabClick : function(e){
27217         e.preventDefault();
27218         this.tabPanel.activate(this.id);
27219     },
27220
27221     onTabMouseDown : function(e){
27222         e.preventDefault();
27223         this.tabPanel.activate(this.id);
27224     },
27225
27226     getWidth : function(){
27227         return this.inner.getWidth();
27228     },
27229
27230     setWidth : function(width){
27231         var iwidth = width - this.pnode.getPadding("lr");
27232         this.inner.setWidth(iwidth);
27233         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27234         this.pnode.setWidth(width);
27235     },
27236
27237     /**
27238      * Show or hide the tab
27239      * @param {Boolean} hidden True to hide or false to show.
27240      */
27241     setHidden : function(hidden){
27242         this.hidden = hidden;
27243         this.pnode.setStyle("display", hidden ? "none" : "");
27244     },
27245
27246     /**
27247      * Returns true if this tab is "hidden"
27248      * @return {Boolean}
27249      */
27250     isHidden : function(){
27251         return this.hidden;
27252     },
27253
27254     /**
27255      * Returns the text for this tab
27256      * @return {String}
27257      */
27258     getText : function(){
27259         return this.text;
27260     },
27261
27262     autoSize : function(){
27263         //this.el.beginMeasure();
27264         this.textEl.setWidth(1);
27265         /*
27266          *  #2804 [new] Tabs in Roojs
27267          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27268          */
27269         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27270         //this.el.endMeasure();
27271     },
27272
27273     /**
27274      * Sets the text for the tab (Note: this also sets the tooltip text)
27275      * @param {String} text The tab's text and tooltip
27276      */
27277     setText : function(text){
27278         this.text = text;
27279         this.textEl.update(text);
27280         this.setTooltip(text);
27281         if(!this.tabPanel.resizeTabs){
27282             this.autoSize();
27283         }
27284     },
27285     /**
27286      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27287      */
27288     activate : function(){
27289         this.tabPanel.activate(this.id);
27290     },
27291
27292     /**
27293      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27294      */
27295     disable : function(){
27296         if(this.tabPanel.active != this){
27297             this.disabled = true;
27298             this.pnode.addClass("disabled");
27299         }
27300     },
27301
27302     /**
27303      * Enables this TabPanelItem if it was previously disabled.
27304      */
27305     enable : function(){
27306         this.disabled = false;
27307         this.pnode.removeClass("disabled");
27308     },
27309
27310     /**
27311      * Sets the content for this TabPanelItem.
27312      * @param {String} content The content
27313      * @param {Boolean} loadScripts true to look for and load scripts
27314      */
27315     setContent : function(content, loadScripts){
27316         this.bodyEl.update(content, loadScripts);
27317     },
27318
27319     /**
27320      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27321      * @return {Roo.UpdateManager} The UpdateManager
27322      */
27323     getUpdateManager : function(){
27324         return this.bodyEl.getUpdateManager();
27325     },
27326
27327     /**
27328      * Set a URL to be used to load the content for this TabPanelItem.
27329      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27330      * @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)
27331      * @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)
27332      * @return {Roo.UpdateManager} The UpdateManager
27333      */
27334     setUrl : function(url, params, loadOnce){
27335         if(this.refreshDelegate){
27336             this.un('activate', this.refreshDelegate);
27337         }
27338         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27339         this.on("activate", this.refreshDelegate);
27340         return this.bodyEl.getUpdateManager();
27341     },
27342
27343     /** @private */
27344     _handleRefresh : function(url, params, loadOnce){
27345         if(!loadOnce || !this.loaded){
27346             var updater = this.bodyEl.getUpdateManager();
27347             updater.update(url, params, this._setLoaded.createDelegate(this));
27348         }
27349     },
27350
27351     /**
27352      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27353      *   Will fail silently if the setUrl method has not been called.
27354      *   This does not activate the panel, just updates its content.
27355      */
27356     refresh : function(){
27357         if(this.refreshDelegate){
27358            this.loaded = false;
27359            this.refreshDelegate();
27360         }
27361     },
27362
27363     /** @private */
27364     _setLoaded : function(){
27365         this.loaded = true;
27366     },
27367
27368     /** @private */
27369     closeClick : function(e){
27370         var o = {};
27371         e.stopEvent();
27372         this.fireEvent("beforeclose", this, o);
27373         if(o.cancel !== true){
27374             this.tabPanel.removeTab(this.id);
27375         }
27376     },
27377     /**
27378      * The text displayed in the tooltip for the close icon.
27379      * @type String
27380      */
27381     closeText : "Close this tab"
27382 });
27383
27384 /** @private */
27385 Roo.TabPanel.prototype.createStrip = function(container){
27386     var strip = document.createElement("div");
27387     strip.className = "x-tabs-wrap";
27388     container.appendChild(strip);
27389     return strip;
27390 };
27391 /** @private */
27392 Roo.TabPanel.prototype.createStripList = function(strip){
27393     // div wrapper for retard IE
27394     // returns the "tr" element.
27395     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27396         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27397         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27398     return strip.firstChild.firstChild.firstChild.firstChild;
27399 };
27400 /** @private */
27401 Roo.TabPanel.prototype.createBody = function(container){
27402     var body = document.createElement("div");
27403     Roo.id(body, "tab-body");
27404     Roo.fly(body).addClass("x-tabs-body");
27405     container.appendChild(body);
27406     return body;
27407 };
27408 /** @private */
27409 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27410     var body = Roo.getDom(id);
27411     if(!body){
27412         body = document.createElement("div");
27413         body.id = id;
27414     }
27415     Roo.fly(body).addClass("x-tabs-item-body");
27416     bodyEl.insertBefore(body, bodyEl.firstChild);
27417     return body;
27418 };
27419 /** @private */
27420 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27421     var td = document.createElement("td");
27422     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27423     //stripEl.appendChild(td);
27424     if(closable){
27425         td.className = "x-tabs-closable";
27426         if(!this.closeTpl){
27427             this.closeTpl = new Roo.Template(
27428                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27429                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27430                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27431             );
27432         }
27433         var el = this.closeTpl.overwrite(td, {"text": text});
27434         var close = el.getElementsByTagName("div")[0];
27435         var inner = el.getElementsByTagName("em")[0];
27436         return {"el": el, "close": close, "inner": inner};
27437     } else {
27438         if(!this.tabTpl){
27439             this.tabTpl = new Roo.Template(
27440                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27441                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27442             );
27443         }
27444         var el = this.tabTpl.overwrite(td, {"text": text});
27445         var inner = el.getElementsByTagName("em")[0];
27446         return {"el": el, "inner": inner};
27447     }
27448 };/*
27449  * Based on:
27450  * Ext JS Library 1.1.1
27451  * Copyright(c) 2006-2007, Ext JS, LLC.
27452  *
27453  * Originally Released Under LGPL - original licence link has changed is not relivant.
27454  *
27455  * Fork - LGPL
27456  * <script type="text/javascript">
27457  */
27458
27459 /**
27460  * @class Roo.Button
27461  * @extends Roo.util.Observable
27462  * Simple Button class
27463  * @cfg {String} text The button text
27464  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27465  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27466  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27467  * @cfg {Object} scope The scope of the handler
27468  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27469  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27470  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27471  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27472  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27473  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27474    applies if enableToggle = true)
27475  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27476  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27477   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27478  * @constructor
27479  * Create a new button
27480  * @param {Object} config The config object
27481  */
27482 Roo.Button = function(renderTo, config)
27483 {
27484     if (!config) {
27485         config = renderTo;
27486         renderTo = config.renderTo || false;
27487     }
27488     
27489     Roo.apply(this, config);
27490     this.addEvents({
27491         /**
27492              * @event click
27493              * Fires when this button is clicked
27494              * @param {Button} this
27495              * @param {EventObject} e The click event
27496              */
27497             "click" : true,
27498         /**
27499              * @event toggle
27500              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27501              * @param {Button} this
27502              * @param {Boolean} pressed
27503              */
27504             "toggle" : true,
27505         /**
27506              * @event mouseover
27507              * Fires when the mouse hovers over the button
27508              * @param {Button} this
27509              * @param {Event} e The event object
27510              */
27511         'mouseover' : true,
27512         /**
27513              * @event mouseout
27514              * Fires when the mouse exits the button
27515              * @param {Button} this
27516              * @param {Event} e The event object
27517              */
27518         'mouseout': true,
27519          /**
27520              * @event render
27521              * Fires when the button is rendered
27522              * @param {Button} this
27523              */
27524         'render': true
27525     });
27526     if(this.menu){
27527         this.menu = Roo.menu.MenuMgr.get(this.menu);
27528     }
27529     // register listeners first!!  - so render can be captured..
27530     Roo.util.Observable.call(this);
27531     if(renderTo){
27532         this.render(renderTo);
27533     }
27534     
27535   
27536 };
27537
27538 Roo.extend(Roo.Button, Roo.util.Observable, {
27539     /**
27540      * 
27541      */
27542     
27543     /**
27544      * Read-only. True if this button is hidden
27545      * @type Boolean
27546      */
27547     hidden : false,
27548     /**
27549      * Read-only. True if this button is disabled
27550      * @type Boolean
27551      */
27552     disabled : false,
27553     /**
27554      * Read-only. True if this button is pressed (only if enableToggle = true)
27555      * @type Boolean
27556      */
27557     pressed : false,
27558
27559     /**
27560      * @cfg {Number} tabIndex 
27561      * The DOM tabIndex for this button (defaults to undefined)
27562      */
27563     tabIndex : undefined,
27564
27565     /**
27566      * @cfg {Boolean} enableToggle
27567      * True to enable pressed/not pressed toggling (defaults to false)
27568      */
27569     enableToggle: false,
27570     /**
27571      * @cfg {Mixed} menu
27572      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27573      */
27574     menu : undefined,
27575     /**
27576      * @cfg {String} menuAlign
27577      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27578      */
27579     menuAlign : "tl-bl?",
27580
27581     /**
27582      * @cfg {String} iconCls
27583      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27584      */
27585     iconCls : undefined,
27586     /**
27587      * @cfg {String} type
27588      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27589      */
27590     type : 'button',
27591
27592     // private
27593     menuClassTarget: 'tr',
27594
27595     /**
27596      * @cfg {String} clickEvent
27597      * The type of event to map to the button's event handler (defaults to 'click')
27598      */
27599     clickEvent : 'click',
27600
27601     /**
27602      * @cfg {Boolean} handleMouseEvents
27603      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27604      */
27605     handleMouseEvents : true,
27606
27607     /**
27608      * @cfg {String} tooltipType
27609      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27610      */
27611     tooltipType : 'qtip',
27612
27613     /**
27614      * @cfg {String} cls
27615      * A CSS class to apply to the button's main element.
27616      */
27617     
27618     /**
27619      * @cfg {Roo.Template} template (Optional)
27620      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27621      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27622      * require code modifications if required elements (e.g. a button) aren't present.
27623      */
27624
27625     // private
27626     render : function(renderTo){
27627         var btn;
27628         if(this.hideParent){
27629             this.parentEl = Roo.get(renderTo);
27630         }
27631         if(!this.dhconfig){
27632             if(!this.template){
27633                 if(!Roo.Button.buttonTemplate){
27634                     // hideous table template
27635                     Roo.Button.buttonTemplate = new Roo.Template(
27636                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27637                         '<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>',
27638                         "</tr></tbody></table>");
27639                 }
27640                 this.template = Roo.Button.buttonTemplate;
27641             }
27642             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27643             var btnEl = btn.child("button:first");
27644             btnEl.on('focus', this.onFocus, this);
27645             btnEl.on('blur', this.onBlur, this);
27646             if(this.cls){
27647                 btn.addClass(this.cls);
27648             }
27649             if(this.icon){
27650                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27651             }
27652             if(this.iconCls){
27653                 btnEl.addClass(this.iconCls);
27654                 if(!this.cls){
27655                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27656                 }
27657             }
27658             if(this.tabIndex !== undefined){
27659                 btnEl.dom.tabIndex = this.tabIndex;
27660             }
27661             if(this.tooltip){
27662                 if(typeof this.tooltip == 'object'){
27663                     Roo.QuickTips.tips(Roo.apply({
27664                           target: btnEl.id
27665                     }, this.tooltip));
27666                 } else {
27667                     btnEl.dom[this.tooltipType] = this.tooltip;
27668                 }
27669             }
27670         }else{
27671             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27672         }
27673         this.el = btn;
27674         if(this.id){
27675             this.el.dom.id = this.el.id = this.id;
27676         }
27677         if(this.menu){
27678             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27679             this.menu.on("show", this.onMenuShow, this);
27680             this.menu.on("hide", this.onMenuHide, this);
27681         }
27682         btn.addClass("x-btn");
27683         if(Roo.isIE && !Roo.isIE7){
27684             this.autoWidth.defer(1, this);
27685         }else{
27686             this.autoWidth();
27687         }
27688         if(this.handleMouseEvents){
27689             btn.on("mouseover", this.onMouseOver, this);
27690             btn.on("mouseout", this.onMouseOut, this);
27691             btn.on("mousedown", this.onMouseDown, this);
27692         }
27693         btn.on(this.clickEvent, this.onClick, this);
27694         //btn.on("mouseup", this.onMouseUp, this);
27695         if(this.hidden){
27696             this.hide();
27697         }
27698         if(this.disabled){
27699             this.disable();
27700         }
27701         Roo.ButtonToggleMgr.register(this);
27702         if(this.pressed){
27703             this.el.addClass("x-btn-pressed");
27704         }
27705         if(this.repeat){
27706             var repeater = new Roo.util.ClickRepeater(btn,
27707                 typeof this.repeat == "object" ? this.repeat : {}
27708             );
27709             repeater.on("click", this.onClick,  this);
27710         }
27711         
27712         this.fireEvent('render', this);
27713         
27714     },
27715     /**
27716      * Returns the button's underlying element
27717      * @return {Roo.Element} The element
27718      */
27719     getEl : function(){
27720         return this.el;  
27721     },
27722     
27723     /**
27724      * Destroys this Button and removes any listeners.
27725      */
27726     destroy : function(){
27727         Roo.ButtonToggleMgr.unregister(this);
27728         this.el.removeAllListeners();
27729         this.purgeListeners();
27730         this.el.remove();
27731     },
27732
27733     // private
27734     autoWidth : function(){
27735         if(this.el){
27736             this.el.setWidth("auto");
27737             if(Roo.isIE7 && Roo.isStrict){
27738                 var ib = this.el.child('button');
27739                 if(ib && ib.getWidth() > 20){
27740                     ib.clip();
27741                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27742                 }
27743             }
27744             if(this.minWidth){
27745                 if(this.hidden){
27746                     this.el.beginMeasure();
27747                 }
27748                 if(this.el.getWidth() < this.minWidth){
27749                     this.el.setWidth(this.minWidth);
27750                 }
27751                 if(this.hidden){
27752                     this.el.endMeasure();
27753                 }
27754             }
27755         }
27756     },
27757
27758     /**
27759      * Assigns this button's click handler
27760      * @param {Function} handler The function to call when the button is clicked
27761      * @param {Object} scope (optional) Scope for the function passed in
27762      */
27763     setHandler : function(handler, scope){
27764         this.handler = handler;
27765         this.scope = scope;  
27766     },
27767     
27768     /**
27769      * Sets this button's text
27770      * @param {String} text The button text
27771      */
27772     setText : function(text){
27773         this.text = text;
27774         if(this.el){
27775             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27776         }
27777         this.autoWidth();
27778     },
27779     
27780     /**
27781      * Gets the text for this button
27782      * @return {String} The button text
27783      */
27784     getText : function(){
27785         return this.text;  
27786     },
27787     
27788     /**
27789      * Show this button
27790      */
27791     show: function(){
27792         this.hidden = false;
27793         if(this.el){
27794             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27795         }
27796     },
27797     
27798     /**
27799      * Hide this button
27800      */
27801     hide: function(){
27802         this.hidden = true;
27803         if(this.el){
27804             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27805         }
27806     },
27807     
27808     /**
27809      * Convenience function for boolean show/hide
27810      * @param {Boolean} visible True to show, false to hide
27811      */
27812     setVisible: function(visible){
27813         if(visible) {
27814             this.show();
27815         }else{
27816             this.hide();
27817         }
27818     },
27819     
27820     /**
27821      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27822      * @param {Boolean} state (optional) Force a particular state
27823      */
27824     toggle : function(state){
27825         state = state === undefined ? !this.pressed : state;
27826         if(state != this.pressed){
27827             if(state){
27828                 this.el.addClass("x-btn-pressed");
27829                 this.pressed = true;
27830                 this.fireEvent("toggle", this, true);
27831             }else{
27832                 this.el.removeClass("x-btn-pressed");
27833                 this.pressed = false;
27834                 this.fireEvent("toggle", this, false);
27835             }
27836             if(this.toggleHandler){
27837                 this.toggleHandler.call(this.scope || this, this, state);
27838             }
27839         }
27840     },
27841     
27842     /**
27843      * Focus the button
27844      */
27845     focus : function(){
27846         this.el.child('button:first').focus();
27847     },
27848     
27849     /**
27850      * Disable this button
27851      */
27852     disable : function(){
27853         if(this.el){
27854             this.el.addClass("x-btn-disabled");
27855         }
27856         this.disabled = true;
27857     },
27858     
27859     /**
27860      * Enable this button
27861      */
27862     enable : function(){
27863         if(this.el){
27864             this.el.removeClass("x-btn-disabled");
27865         }
27866         this.disabled = false;
27867     },
27868
27869     /**
27870      * Convenience function for boolean enable/disable
27871      * @param {Boolean} enabled True to enable, false to disable
27872      */
27873     setDisabled : function(v){
27874         this[v !== true ? "enable" : "disable"]();
27875     },
27876
27877     // private
27878     onClick : function(e)
27879     {
27880         if(e){
27881             e.preventDefault();
27882         }
27883         if(e.button != 0){
27884             return;
27885         }
27886         if(!this.disabled){
27887             if(this.enableToggle){
27888                 this.toggle();
27889             }
27890             if(this.menu && !this.menu.isVisible()){
27891                 this.menu.show(this.el, this.menuAlign);
27892             }
27893             this.fireEvent("click", this, e);
27894             if(this.handler){
27895                 this.el.removeClass("x-btn-over");
27896                 this.handler.call(this.scope || this, this, e);
27897             }
27898         }
27899     },
27900     // private
27901     onMouseOver : function(e){
27902         if(!this.disabled){
27903             this.el.addClass("x-btn-over");
27904             this.fireEvent('mouseover', this, e);
27905         }
27906     },
27907     // private
27908     onMouseOut : function(e){
27909         if(!e.within(this.el,  true)){
27910             this.el.removeClass("x-btn-over");
27911             this.fireEvent('mouseout', this, e);
27912         }
27913     },
27914     // private
27915     onFocus : function(e){
27916         if(!this.disabled){
27917             this.el.addClass("x-btn-focus");
27918         }
27919     },
27920     // private
27921     onBlur : function(e){
27922         this.el.removeClass("x-btn-focus");
27923     },
27924     // private
27925     onMouseDown : function(e){
27926         if(!this.disabled && e.button == 0){
27927             this.el.addClass("x-btn-click");
27928             Roo.get(document).on('mouseup', this.onMouseUp, this);
27929         }
27930     },
27931     // private
27932     onMouseUp : function(e){
27933         if(e.button == 0){
27934             this.el.removeClass("x-btn-click");
27935             Roo.get(document).un('mouseup', this.onMouseUp, this);
27936         }
27937     },
27938     // private
27939     onMenuShow : function(e){
27940         this.el.addClass("x-btn-menu-active");
27941     },
27942     // private
27943     onMenuHide : function(e){
27944         this.el.removeClass("x-btn-menu-active");
27945     }   
27946 });
27947
27948 // Private utility class used by Button
27949 Roo.ButtonToggleMgr = function(){
27950    var groups = {};
27951    
27952    function toggleGroup(btn, state){
27953        if(state){
27954            var g = groups[btn.toggleGroup];
27955            for(var i = 0, l = g.length; i < l; i++){
27956                if(g[i] != btn){
27957                    g[i].toggle(false);
27958                }
27959            }
27960        }
27961    }
27962    
27963    return {
27964        register : function(btn){
27965            if(!btn.toggleGroup){
27966                return;
27967            }
27968            var g = groups[btn.toggleGroup];
27969            if(!g){
27970                g = groups[btn.toggleGroup] = [];
27971            }
27972            g.push(btn);
27973            btn.on("toggle", toggleGroup);
27974        },
27975        
27976        unregister : function(btn){
27977            if(!btn.toggleGroup){
27978                return;
27979            }
27980            var g = groups[btn.toggleGroup];
27981            if(g){
27982                g.remove(btn);
27983                btn.un("toggle", toggleGroup);
27984            }
27985        }
27986    };
27987 }();/*
27988  * Based on:
27989  * Ext JS Library 1.1.1
27990  * Copyright(c) 2006-2007, Ext JS, LLC.
27991  *
27992  * Originally Released Under LGPL - original licence link has changed is not relivant.
27993  *
27994  * Fork - LGPL
27995  * <script type="text/javascript">
27996  */
27997  
27998 /**
27999  * @class Roo.SplitButton
28000  * @extends Roo.Button
28001  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28002  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28003  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28004  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28005  * @cfg {String} arrowTooltip The title attribute of the arrow
28006  * @constructor
28007  * Create a new menu button
28008  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28009  * @param {Object} config The config object
28010  */
28011 Roo.SplitButton = function(renderTo, config){
28012     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28013     /**
28014      * @event arrowclick
28015      * Fires when this button's arrow is clicked
28016      * @param {SplitButton} this
28017      * @param {EventObject} e The click event
28018      */
28019     this.addEvents({"arrowclick":true});
28020 };
28021
28022 Roo.extend(Roo.SplitButton, Roo.Button, {
28023     render : function(renderTo){
28024         // this is one sweet looking template!
28025         var tpl = new Roo.Template(
28026             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28027             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28028             '<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>',
28029             "</tbody></table></td><td>",
28030             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28031             '<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>',
28032             "</tbody></table></td></tr></table>"
28033         );
28034         var btn = tpl.append(renderTo, [this.text, this.type], true);
28035         var btnEl = btn.child("button");
28036         if(this.cls){
28037             btn.addClass(this.cls);
28038         }
28039         if(this.icon){
28040             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28041         }
28042         if(this.iconCls){
28043             btnEl.addClass(this.iconCls);
28044             if(!this.cls){
28045                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28046             }
28047         }
28048         this.el = btn;
28049         if(this.handleMouseEvents){
28050             btn.on("mouseover", this.onMouseOver, this);
28051             btn.on("mouseout", this.onMouseOut, this);
28052             btn.on("mousedown", this.onMouseDown, this);
28053             btn.on("mouseup", this.onMouseUp, this);
28054         }
28055         btn.on(this.clickEvent, this.onClick, this);
28056         if(this.tooltip){
28057             if(typeof this.tooltip == 'object'){
28058                 Roo.QuickTips.tips(Roo.apply({
28059                       target: btnEl.id
28060                 }, this.tooltip));
28061             } else {
28062                 btnEl.dom[this.tooltipType] = this.tooltip;
28063             }
28064         }
28065         if(this.arrowTooltip){
28066             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28067         }
28068         if(this.hidden){
28069             this.hide();
28070         }
28071         if(this.disabled){
28072             this.disable();
28073         }
28074         if(this.pressed){
28075             this.el.addClass("x-btn-pressed");
28076         }
28077         if(Roo.isIE && !Roo.isIE7){
28078             this.autoWidth.defer(1, this);
28079         }else{
28080             this.autoWidth();
28081         }
28082         if(this.menu){
28083             this.menu.on("show", this.onMenuShow, this);
28084             this.menu.on("hide", this.onMenuHide, this);
28085         }
28086         this.fireEvent('render', this);
28087     },
28088
28089     // private
28090     autoWidth : function(){
28091         if(this.el){
28092             var tbl = this.el.child("table:first");
28093             var tbl2 = this.el.child("table:last");
28094             this.el.setWidth("auto");
28095             tbl.setWidth("auto");
28096             if(Roo.isIE7 && Roo.isStrict){
28097                 var ib = this.el.child('button:first');
28098                 if(ib && ib.getWidth() > 20){
28099                     ib.clip();
28100                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28101                 }
28102             }
28103             if(this.minWidth){
28104                 if(this.hidden){
28105                     this.el.beginMeasure();
28106                 }
28107                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28108                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28109                 }
28110                 if(this.hidden){
28111                     this.el.endMeasure();
28112                 }
28113             }
28114             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28115         } 
28116     },
28117     /**
28118      * Sets this button's click handler
28119      * @param {Function} handler The function to call when the button is clicked
28120      * @param {Object} scope (optional) Scope for the function passed above
28121      */
28122     setHandler : function(handler, scope){
28123         this.handler = handler;
28124         this.scope = scope;  
28125     },
28126     
28127     /**
28128      * Sets this button's arrow click handler
28129      * @param {Function} handler The function to call when the arrow is clicked
28130      * @param {Object} scope (optional) Scope for the function passed above
28131      */
28132     setArrowHandler : function(handler, scope){
28133         this.arrowHandler = handler;
28134         this.scope = scope;  
28135     },
28136     
28137     /**
28138      * Focus the button
28139      */
28140     focus : function(){
28141         if(this.el){
28142             this.el.child("button:first").focus();
28143         }
28144     },
28145
28146     // private
28147     onClick : function(e){
28148         e.preventDefault();
28149         if(!this.disabled){
28150             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28151                 if(this.menu && !this.menu.isVisible()){
28152                     this.menu.show(this.el, this.menuAlign);
28153                 }
28154                 this.fireEvent("arrowclick", this, e);
28155                 if(this.arrowHandler){
28156                     this.arrowHandler.call(this.scope || this, this, e);
28157                 }
28158             }else{
28159                 this.fireEvent("click", this, e);
28160                 if(this.handler){
28161                     this.handler.call(this.scope || this, this, e);
28162                 }
28163             }
28164         }
28165     },
28166     // private
28167     onMouseDown : function(e){
28168         if(!this.disabled){
28169             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28170         }
28171     },
28172     // private
28173     onMouseUp : function(e){
28174         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28175     }   
28176 });
28177
28178
28179 // backwards compat
28180 Roo.MenuButton = Roo.SplitButton;/*
28181  * Based on:
28182  * Ext JS Library 1.1.1
28183  * Copyright(c) 2006-2007, Ext JS, LLC.
28184  *
28185  * Originally Released Under LGPL - original licence link has changed is not relivant.
28186  *
28187  * Fork - LGPL
28188  * <script type="text/javascript">
28189  */
28190
28191 /**
28192  * @class Roo.Toolbar
28193  * Basic Toolbar class.
28194  * @constructor
28195  * Creates a new Toolbar
28196  * @param {Object} container The config object
28197  */ 
28198 Roo.Toolbar = function(container, buttons, config)
28199 {
28200     /// old consturctor format still supported..
28201     if(container instanceof Array){ // omit the container for later rendering
28202         buttons = container;
28203         config = buttons;
28204         container = null;
28205     }
28206     if (typeof(container) == 'object' && container.xtype) {
28207         config = container;
28208         container = config.container;
28209         buttons = config.buttons || []; // not really - use items!!
28210     }
28211     var xitems = [];
28212     if (config && config.items) {
28213         xitems = config.items;
28214         delete config.items;
28215     }
28216     Roo.apply(this, config);
28217     this.buttons = buttons;
28218     
28219     if(container){
28220         this.render(container);
28221     }
28222     this.xitems = xitems;
28223     Roo.each(xitems, function(b) {
28224         this.add(b);
28225     }, this);
28226     
28227 };
28228
28229 Roo.Toolbar.prototype = {
28230     /**
28231      * @cfg {Array} items
28232      * array of button configs or elements to add (will be converted to a MixedCollection)
28233      */
28234     
28235     /**
28236      * @cfg {String/HTMLElement/Element} container
28237      * The id or element that will contain the toolbar
28238      */
28239     // private
28240     render : function(ct){
28241         this.el = Roo.get(ct);
28242         if(this.cls){
28243             this.el.addClass(this.cls);
28244         }
28245         // using a table allows for vertical alignment
28246         // 100% width is needed by Safari...
28247         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28248         this.tr = this.el.child("tr", true);
28249         var autoId = 0;
28250         this.items = new Roo.util.MixedCollection(false, function(o){
28251             return o.id || ("item" + (++autoId));
28252         });
28253         if(this.buttons){
28254             this.add.apply(this, this.buttons);
28255             delete this.buttons;
28256         }
28257     },
28258
28259     /**
28260      * Adds element(s) to the toolbar -- this function takes a variable number of 
28261      * arguments of mixed type and adds them to the toolbar.
28262      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28263      * <ul>
28264      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28265      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28266      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28267      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28268      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28269      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28270      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28271      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28272      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28273      * </ul>
28274      * @param {Mixed} arg2
28275      * @param {Mixed} etc.
28276      */
28277     add : function(){
28278         var a = arguments, l = a.length;
28279         for(var i = 0; i < l; i++){
28280             this._add(a[i]);
28281         }
28282     },
28283     // private..
28284     _add : function(el) {
28285         
28286         if (el.xtype) {
28287             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28288         }
28289         
28290         if (el.applyTo){ // some kind of form field
28291             return this.addField(el);
28292         } 
28293         if (el.render){ // some kind of Toolbar.Item
28294             return this.addItem(el);
28295         }
28296         if (typeof el == "string"){ // string
28297             if(el == "separator" || el == "-"){
28298                 return this.addSeparator();
28299             }
28300             if (el == " "){
28301                 return this.addSpacer();
28302             }
28303             if(el == "->"){
28304                 return this.addFill();
28305             }
28306             return this.addText(el);
28307             
28308         }
28309         if(el.tagName){ // element
28310             return this.addElement(el);
28311         }
28312         if(typeof el == "object"){ // must be button config?
28313             return this.addButton(el);
28314         }
28315         // and now what?!?!
28316         return false;
28317         
28318     },
28319     
28320     /**
28321      * Add an Xtype element
28322      * @param {Object} xtype Xtype Object
28323      * @return {Object} created Object
28324      */
28325     addxtype : function(e){
28326         return this.add(e);  
28327     },
28328     
28329     /**
28330      * Returns the Element for this toolbar.
28331      * @return {Roo.Element}
28332      */
28333     getEl : function(){
28334         return this.el;  
28335     },
28336     
28337     /**
28338      * Adds a separator
28339      * @return {Roo.Toolbar.Item} The separator item
28340      */
28341     addSeparator : function(){
28342         return this.addItem(new Roo.Toolbar.Separator());
28343     },
28344
28345     /**
28346      * Adds a spacer element
28347      * @return {Roo.Toolbar.Spacer} The spacer item
28348      */
28349     addSpacer : function(){
28350         return this.addItem(new Roo.Toolbar.Spacer());
28351     },
28352
28353     /**
28354      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28355      * @return {Roo.Toolbar.Fill} The fill item
28356      */
28357     addFill : function(){
28358         return this.addItem(new Roo.Toolbar.Fill());
28359     },
28360
28361     /**
28362      * Adds any standard HTML element to the toolbar
28363      * @param {String/HTMLElement/Element} el The element or id of the element to add
28364      * @return {Roo.Toolbar.Item} The element's item
28365      */
28366     addElement : function(el){
28367         return this.addItem(new Roo.Toolbar.Item(el));
28368     },
28369     /**
28370      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28371      * @type Roo.util.MixedCollection  
28372      */
28373     items : false,
28374      
28375     /**
28376      * Adds any Toolbar.Item or subclass
28377      * @param {Roo.Toolbar.Item} item
28378      * @return {Roo.Toolbar.Item} The item
28379      */
28380     addItem : function(item){
28381         var td = this.nextBlock();
28382         item.render(td);
28383         this.items.add(item);
28384         return item;
28385     },
28386     
28387     /**
28388      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28389      * @param {Object/Array} config A button config or array of configs
28390      * @return {Roo.Toolbar.Button/Array}
28391      */
28392     addButton : function(config){
28393         if(config instanceof Array){
28394             var buttons = [];
28395             for(var i = 0, len = config.length; i < len; i++) {
28396                 buttons.push(this.addButton(config[i]));
28397             }
28398             return buttons;
28399         }
28400         var b = config;
28401         if(!(config instanceof Roo.Toolbar.Button)){
28402             b = config.split ?
28403                 new Roo.Toolbar.SplitButton(config) :
28404                 new Roo.Toolbar.Button(config);
28405         }
28406         var td = this.nextBlock();
28407         b.render(td);
28408         this.items.add(b);
28409         return b;
28410     },
28411     
28412     /**
28413      * Adds text to the toolbar
28414      * @param {String} text The text to add
28415      * @return {Roo.Toolbar.Item} The element's item
28416      */
28417     addText : function(text){
28418         return this.addItem(new Roo.Toolbar.TextItem(text));
28419     },
28420     
28421     /**
28422      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28423      * @param {Number} index The index where the item is to be inserted
28424      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28425      * @return {Roo.Toolbar.Button/Item}
28426      */
28427     insertButton : function(index, item){
28428         if(item instanceof Array){
28429             var buttons = [];
28430             for(var i = 0, len = item.length; i < len; i++) {
28431                buttons.push(this.insertButton(index + i, item[i]));
28432             }
28433             return buttons;
28434         }
28435         if (!(item instanceof Roo.Toolbar.Button)){
28436            item = new Roo.Toolbar.Button(item);
28437         }
28438         var td = document.createElement("td");
28439         this.tr.insertBefore(td, this.tr.childNodes[index]);
28440         item.render(td);
28441         this.items.insert(index, item);
28442         return item;
28443     },
28444     
28445     /**
28446      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28447      * @param {Object} config
28448      * @return {Roo.Toolbar.Item} The element's item
28449      */
28450     addDom : function(config, returnEl){
28451         var td = this.nextBlock();
28452         Roo.DomHelper.overwrite(td, config);
28453         var ti = new Roo.Toolbar.Item(td.firstChild);
28454         ti.render(td);
28455         this.items.add(ti);
28456         return ti;
28457     },
28458
28459     /**
28460      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28461      * @type Roo.util.MixedCollection  
28462      */
28463     fields : false,
28464     
28465     /**
28466      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28467      * Note: the field should not have been rendered yet. For a field that has already been
28468      * rendered, use {@link #addElement}.
28469      * @param {Roo.form.Field} field
28470      * @return {Roo.ToolbarItem}
28471      */
28472      
28473       
28474     addField : function(field) {
28475         if (!this.fields) {
28476             var autoId = 0;
28477             this.fields = new Roo.util.MixedCollection(false, function(o){
28478                 return o.id || ("item" + (++autoId));
28479             });
28480
28481         }
28482         
28483         var td = this.nextBlock();
28484         field.render(td);
28485         var ti = new Roo.Toolbar.Item(td.firstChild);
28486         ti.render(td);
28487         this.items.add(ti);
28488         this.fields.add(field);
28489         return ti;
28490     },
28491     /**
28492      * Hide the toolbar
28493      * @method hide
28494      */
28495      
28496       
28497     hide : function()
28498     {
28499         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28500         this.el.child('div').hide();
28501     },
28502     /**
28503      * Show the toolbar
28504      * @method show
28505      */
28506     show : function()
28507     {
28508         this.el.child('div').show();
28509     },
28510       
28511     // private
28512     nextBlock : function(){
28513         var td = document.createElement("td");
28514         this.tr.appendChild(td);
28515         return td;
28516     },
28517
28518     // private
28519     destroy : function(){
28520         if(this.items){ // rendered?
28521             Roo.destroy.apply(Roo, this.items.items);
28522         }
28523         if(this.fields){ // rendered?
28524             Roo.destroy.apply(Roo, this.fields.items);
28525         }
28526         Roo.Element.uncache(this.el, this.tr);
28527     }
28528 };
28529
28530 /**
28531  * @class Roo.Toolbar.Item
28532  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28533  * @constructor
28534  * Creates a new Item
28535  * @param {HTMLElement} el 
28536  */
28537 Roo.Toolbar.Item = function(el){
28538     var cfg = {};
28539     if (typeof (el.xtype) != 'undefined') {
28540         cfg = el;
28541         el = cfg.el;
28542     }
28543     
28544     this.el = Roo.getDom(el);
28545     this.id = Roo.id(this.el);
28546     this.hidden = false;
28547     
28548     this.addEvents({
28549          /**
28550              * @event render
28551              * Fires when the button is rendered
28552              * @param {Button} this
28553              */
28554         'render': true
28555     });
28556     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28557 };
28558 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28559 //Roo.Toolbar.Item.prototype = {
28560     
28561     /**
28562      * Get this item's HTML Element
28563      * @return {HTMLElement}
28564      */
28565     getEl : function(){
28566        return this.el;  
28567     },
28568
28569     // private
28570     render : function(td){
28571         
28572          this.td = td;
28573         td.appendChild(this.el);
28574         
28575         this.fireEvent('render', this);
28576     },
28577     
28578     /**
28579      * Removes and destroys this item.
28580      */
28581     destroy : function(){
28582         this.td.parentNode.removeChild(this.td);
28583     },
28584     
28585     /**
28586      * Shows this item.
28587      */
28588     show: function(){
28589         this.hidden = false;
28590         this.td.style.display = "";
28591     },
28592     
28593     /**
28594      * Hides this item.
28595      */
28596     hide: function(){
28597         this.hidden = true;
28598         this.td.style.display = "none";
28599     },
28600     
28601     /**
28602      * Convenience function for boolean show/hide.
28603      * @param {Boolean} visible true to show/false to hide
28604      */
28605     setVisible: function(visible){
28606         if(visible) {
28607             this.show();
28608         }else{
28609             this.hide();
28610         }
28611     },
28612     
28613     /**
28614      * Try to focus this item.
28615      */
28616     focus : function(){
28617         Roo.fly(this.el).focus();
28618     },
28619     
28620     /**
28621      * Disables this item.
28622      */
28623     disable : function(){
28624         Roo.fly(this.td).addClass("x-item-disabled");
28625         this.disabled = true;
28626         this.el.disabled = true;
28627     },
28628     
28629     /**
28630      * Enables this item.
28631      */
28632     enable : function(){
28633         Roo.fly(this.td).removeClass("x-item-disabled");
28634         this.disabled = false;
28635         this.el.disabled = false;
28636     }
28637 });
28638
28639
28640 /**
28641  * @class Roo.Toolbar.Separator
28642  * @extends Roo.Toolbar.Item
28643  * A simple toolbar separator class
28644  * @constructor
28645  * Creates a new Separator
28646  */
28647 Roo.Toolbar.Separator = function(cfg){
28648     
28649     var s = document.createElement("span");
28650     s.className = "ytb-sep";
28651     if (cfg) {
28652         cfg.el = s;
28653     }
28654     
28655     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28656 };
28657 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28658     enable:Roo.emptyFn,
28659     disable:Roo.emptyFn,
28660     focus:Roo.emptyFn
28661 });
28662
28663 /**
28664  * @class Roo.Toolbar.Spacer
28665  * @extends Roo.Toolbar.Item
28666  * A simple element that adds extra horizontal space to a toolbar.
28667  * @constructor
28668  * Creates a new Spacer
28669  */
28670 Roo.Toolbar.Spacer = function(cfg){
28671     var s = document.createElement("div");
28672     s.className = "ytb-spacer";
28673     if (cfg) {
28674         cfg.el = s;
28675     }
28676     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28677 };
28678 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28679     enable:Roo.emptyFn,
28680     disable:Roo.emptyFn,
28681     focus:Roo.emptyFn
28682 });
28683
28684 /**
28685  * @class Roo.Toolbar.Fill
28686  * @extends Roo.Toolbar.Spacer
28687  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28688  * @constructor
28689  * Creates a new Spacer
28690  */
28691 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28692     // private
28693     render : function(td){
28694         td.style.width = '100%';
28695         Roo.Toolbar.Fill.superclass.render.call(this, td);
28696     }
28697 });
28698
28699 /**
28700  * @class Roo.Toolbar.TextItem
28701  * @extends Roo.Toolbar.Item
28702  * A simple class that renders text directly into a toolbar.
28703  * @constructor
28704  * Creates a new TextItem
28705  * @param {String} text
28706  */
28707 Roo.Toolbar.TextItem = function(cfg){
28708     var  text = cfg || "";
28709     if (typeof(cfg) == 'object') {
28710         text = cfg.text || "";
28711     }  else {
28712         cfg = null;
28713     }
28714     var s = document.createElement("span");
28715     s.className = "ytb-text";
28716     s.innerHTML = text;
28717     if (cfg) {
28718         cfg.el  = s;
28719     }
28720     
28721     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28722 };
28723 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28724     
28725      
28726     enable:Roo.emptyFn,
28727     disable:Roo.emptyFn,
28728     focus:Roo.emptyFn
28729 });
28730
28731 /**
28732  * @class Roo.Toolbar.Button
28733  * @extends Roo.Button
28734  * A button that renders into a toolbar.
28735  * @constructor
28736  * Creates a new Button
28737  * @param {Object} config A standard {@link Roo.Button} config object
28738  */
28739 Roo.Toolbar.Button = function(config){
28740     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28741 };
28742 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28743     render : function(td){
28744         this.td = td;
28745         Roo.Toolbar.Button.superclass.render.call(this, td);
28746     },
28747     
28748     /**
28749      * Removes and destroys this button
28750      */
28751     destroy : function(){
28752         Roo.Toolbar.Button.superclass.destroy.call(this);
28753         this.td.parentNode.removeChild(this.td);
28754     },
28755     
28756     /**
28757      * Shows this button
28758      */
28759     show: function(){
28760         this.hidden = false;
28761         this.td.style.display = "";
28762     },
28763     
28764     /**
28765      * Hides this button
28766      */
28767     hide: function(){
28768         this.hidden = true;
28769         this.td.style.display = "none";
28770     },
28771
28772     /**
28773      * Disables this item
28774      */
28775     disable : function(){
28776         Roo.fly(this.td).addClass("x-item-disabled");
28777         this.disabled = true;
28778     },
28779
28780     /**
28781      * Enables this item
28782      */
28783     enable : function(){
28784         Roo.fly(this.td).removeClass("x-item-disabled");
28785         this.disabled = false;
28786     }
28787 });
28788 // backwards compat
28789 Roo.ToolbarButton = Roo.Toolbar.Button;
28790
28791 /**
28792  * @class Roo.Toolbar.SplitButton
28793  * @extends Roo.SplitButton
28794  * A menu button that renders into a toolbar.
28795  * @constructor
28796  * Creates a new SplitButton
28797  * @param {Object} config A standard {@link Roo.SplitButton} config object
28798  */
28799 Roo.Toolbar.SplitButton = function(config){
28800     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28801 };
28802 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28803     render : function(td){
28804         this.td = td;
28805         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28806     },
28807     
28808     /**
28809      * Removes and destroys this button
28810      */
28811     destroy : function(){
28812         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28813         this.td.parentNode.removeChild(this.td);
28814     },
28815     
28816     /**
28817      * Shows this button
28818      */
28819     show: function(){
28820         this.hidden = false;
28821         this.td.style.display = "";
28822     },
28823     
28824     /**
28825      * Hides this button
28826      */
28827     hide: function(){
28828         this.hidden = true;
28829         this.td.style.display = "none";
28830     }
28831 });
28832
28833 // backwards compat
28834 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28835  * Based on:
28836  * Ext JS Library 1.1.1
28837  * Copyright(c) 2006-2007, Ext JS, LLC.
28838  *
28839  * Originally Released Under LGPL - original licence link has changed is not relivant.
28840  *
28841  * Fork - LGPL
28842  * <script type="text/javascript">
28843  */
28844  
28845 /**
28846  * @class Roo.PagingToolbar
28847  * @extends Roo.Toolbar
28848  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28849  * @constructor
28850  * Create a new PagingToolbar
28851  * @param {Object} config The config object
28852  */
28853 Roo.PagingToolbar = function(el, ds, config)
28854 {
28855     // old args format still supported... - xtype is prefered..
28856     if (typeof(el) == 'object' && el.xtype) {
28857         // created from xtype...
28858         config = el;
28859         ds = el.dataSource;
28860         el = config.container;
28861     }
28862     var items = [];
28863     if (config.items) {
28864         items = config.items;
28865         config.items = [];
28866     }
28867     
28868     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28869     this.ds = ds;
28870     this.cursor = 0;
28871     this.renderButtons(this.el);
28872     this.bind(ds);
28873     
28874     // supprot items array.
28875    
28876     Roo.each(items, function(e) {
28877         this.add(Roo.factory(e));
28878     },this);
28879     
28880 };
28881
28882 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28883     /**
28884      * @cfg {Roo.data.Store} dataSource
28885      * The underlying data store providing the paged data
28886      */
28887     /**
28888      * @cfg {String/HTMLElement/Element} container
28889      * container The id or element that will contain the toolbar
28890      */
28891     /**
28892      * @cfg {Boolean} displayInfo
28893      * True to display the displayMsg (defaults to false)
28894      */
28895     /**
28896      * @cfg {Number} pageSize
28897      * The number of records to display per page (defaults to 20)
28898      */
28899     pageSize: 20,
28900     /**
28901      * @cfg {String} displayMsg
28902      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28903      */
28904     displayMsg : 'Displaying {0} - {1} of {2}',
28905     /**
28906      * @cfg {String} emptyMsg
28907      * The message to display when no records are found (defaults to "No data to display")
28908      */
28909     emptyMsg : 'No data to display',
28910     /**
28911      * Customizable piece of the default paging text (defaults to "Page")
28912      * @type String
28913      */
28914     beforePageText : "Page",
28915     /**
28916      * Customizable piece of the default paging text (defaults to "of %0")
28917      * @type String
28918      */
28919     afterPageText : "of {0}",
28920     /**
28921      * Customizable piece of the default paging text (defaults to "First Page")
28922      * @type String
28923      */
28924     firstText : "First Page",
28925     /**
28926      * Customizable piece of the default paging text (defaults to "Previous Page")
28927      * @type String
28928      */
28929     prevText : "Previous Page",
28930     /**
28931      * Customizable piece of the default paging text (defaults to "Next Page")
28932      * @type String
28933      */
28934     nextText : "Next Page",
28935     /**
28936      * Customizable piece of the default paging text (defaults to "Last Page")
28937      * @type String
28938      */
28939     lastText : "Last Page",
28940     /**
28941      * Customizable piece of the default paging text (defaults to "Refresh")
28942      * @type String
28943      */
28944     refreshText : "Refresh",
28945
28946     // private
28947     renderButtons : function(el){
28948         Roo.PagingToolbar.superclass.render.call(this, el);
28949         this.first = this.addButton({
28950             tooltip: this.firstText,
28951             cls: "x-btn-icon x-grid-page-first",
28952             disabled: true,
28953             handler: this.onClick.createDelegate(this, ["first"])
28954         });
28955         this.prev = this.addButton({
28956             tooltip: this.prevText,
28957             cls: "x-btn-icon x-grid-page-prev",
28958             disabled: true,
28959             handler: this.onClick.createDelegate(this, ["prev"])
28960         });
28961         //this.addSeparator();
28962         this.add(this.beforePageText);
28963         this.field = Roo.get(this.addDom({
28964            tag: "input",
28965            type: "text",
28966            size: "3",
28967            value: "1",
28968            cls: "x-grid-page-number"
28969         }).el);
28970         this.field.on("keydown", this.onPagingKeydown, this);
28971         this.field.on("focus", function(){this.dom.select();});
28972         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28973         this.field.setHeight(18);
28974         //this.addSeparator();
28975         this.next = this.addButton({
28976             tooltip: this.nextText,
28977             cls: "x-btn-icon x-grid-page-next",
28978             disabled: true,
28979             handler: this.onClick.createDelegate(this, ["next"])
28980         });
28981         this.last = this.addButton({
28982             tooltip: this.lastText,
28983             cls: "x-btn-icon x-grid-page-last",
28984             disabled: true,
28985             handler: this.onClick.createDelegate(this, ["last"])
28986         });
28987         //this.addSeparator();
28988         this.loading = this.addButton({
28989             tooltip: this.refreshText,
28990             cls: "x-btn-icon x-grid-loading",
28991             handler: this.onClick.createDelegate(this, ["refresh"])
28992         });
28993
28994         if(this.displayInfo){
28995             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28996         }
28997     },
28998
28999     // private
29000     updateInfo : function(){
29001         if(this.displayEl){
29002             var count = this.ds.getCount();
29003             var msg = count == 0 ?
29004                 this.emptyMsg :
29005                 String.format(
29006                     this.displayMsg,
29007                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29008                 );
29009             this.displayEl.update(msg);
29010         }
29011     },
29012
29013     // private
29014     onLoad : function(ds, r, o){
29015        this.cursor = o.params ? o.params.start : 0;
29016        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29017
29018        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29019        this.field.dom.value = ap;
29020        this.first.setDisabled(ap == 1);
29021        this.prev.setDisabled(ap == 1);
29022        this.next.setDisabled(ap == ps);
29023        this.last.setDisabled(ap == ps);
29024        this.loading.enable();
29025        this.updateInfo();
29026     },
29027
29028     // private
29029     getPageData : function(){
29030         var total = this.ds.getTotalCount();
29031         return {
29032             total : total,
29033             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29034             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29035         };
29036     },
29037
29038     // private
29039     onLoadError : function(){
29040         this.loading.enable();
29041     },
29042
29043     // private
29044     onPagingKeydown : function(e){
29045         var k = e.getKey();
29046         var d = this.getPageData();
29047         if(k == e.RETURN){
29048             var v = this.field.dom.value, pageNum;
29049             if(!v || isNaN(pageNum = parseInt(v, 10))){
29050                 this.field.dom.value = d.activePage;
29051                 return;
29052             }
29053             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29054             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29055             e.stopEvent();
29056         }
29057         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))
29058         {
29059           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29060           this.field.dom.value = pageNum;
29061           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29062           e.stopEvent();
29063         }
29064         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29065         {
29066           var v = this.field.dom.value, pageNum; 
29067           var increment = (e.shiftKey) ? 10 : 1;
29068           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29069             increment *= -1;
29070           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29071             this.field.dom.value = d.activePage;
29072             return;
29073           }
29074           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29075           {
29076             this.field.dom.value = parseInt(v, 10) + increment;
29077             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29078             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29079           }
29080           e.stopEvent();
29081         }
29082     },
29083
29084     // private
29085     beforeLoad : function(){
29086         if(this.loading){
29087             this.loading.disable();
29088         }
29089     },
29090
29091     // private
29092     onClick : function(which){
29093         var ds = this.ds;
29094         switch(which){
29095             case "first":
29096                 ds.load({params:{start: 0, limit: this.pageSize}});
29097             break;
29098             case "prev":
29099                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29100             break;
29101             case "next":
29102                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29103             break;
29104             case "last":
29105                 var total = ds.getTotalCount();
29106                 var extra = total % this.pageSize;
29107                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29108                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29109             break;
29110             case "refresh":
29111                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29112             break;
29113         }
29114     },
29115
29116     /**
29117      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29118      * @param {Roo.data.Store} store The data store to unbind
29119      */
29120     unbind : function(ds){
29121         ds.un("beforeload", this.beforeLoad, this);
29122         ds.un("load", this.onLoad, this);
29123         ds.un("loadexception", this.onLoadError, this);
29124         ds.un("remove", this.updateInfo, this);
29125         ds.un("add", this.updateInfo, this);
29126         this.ds = undefined;
29127     },
29128
29129     /**
29130      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29131      * @param {Roo.data.Store} store The data store to bind
29132      */
29133     bind : function(ds){
29134         ds.on("beforeload", this.beforeLoad, this);
29135         ds.on("load", this.onLoad, this);
29136         ds.on("loadexception", this.onLoadError, this);
29137         ds.on("remove", this.updateInfo, this);
29138         ds.on("add", this.updateInfo, this);
29139         this.ds = ds;
29140     }
29141 });/*
29142  * Based on:
29143  * Ext JS Library 1.1.1
29144  * Copyright(c) 2006-2007, Ext JS, LLC.
29145  *
29146  * Originally Released Under LGPL - original licence link has changed is not relivant.
29147  *
29148  * Fork - LGPL
29149  * <script type="text/javascript">
29150  */
29151
29152 /**
29153  * @class Roo.Resizable
29154  * @extends Roo.util.Observable
29155  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29156  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29157  * 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
29158  * the element will be wrapped for you automatically.</p>
29159  * <p>Here is the list of valid resize handles:</p>
29160  * <pre>
29161 Value   Description
29162 ------  -------------------
29163  'n'     north
29164  's'     south
29165  'e'     east
29166  'w'     west
29167  'nw'    northwest
29168  'sw'    southwest
29169  'se'    southeast
29170  'ne'    northeast
29171  'hd'    horizontal drag
29172  'all'   all
29173 </pre>
29174  * <p>Here's an example showing the creation of a typical Resizable:</p>
29175  * <pre><code>
29176 var resizer = new Roo.Resizable("element-id", {
29177     handles: 'all',
29178     minWidth: 200,
29179     minHeight: 100,
29180     maxWidth: 500,
29181     maxHeight: 400,
29182     pinned: true
29183 });
29184 resizer.on("resize", myHandler);
29185 </code></pre>
29186  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29187  * resizer.east.setDisplayed(false);</p>
29188  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29189  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29190  * resize operation's new size (defaults to [0, 0])
29191  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29192  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29193  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29194  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29195  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29196  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29197  * @cfg {Number} width The width of the element in pixels (defaults to null)
29198  * @cfg {Number} height The height of the element in pixels (defaults to null)
29199  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29200  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29201  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29202  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29203  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29204  * in favor of the handles config option (defaults to false)
29205  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29206  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29207  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29208  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29209  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29210  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29211  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29212  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29213  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29214  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29215  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29216  * @constructor
29217  * Create a new resizable component
29218  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29219  * @param {Object} config configuration options
29220   */
29221 Roo.Resizable = function(el, config)
29222 {
29223     this.el = Roo.get(el);
29224
29225     if(config && config.wrap){
29226         config.resizeChild = this.el;
29227         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29228         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29229         this.el.setStyle("overflow", "hidden");
29230         this.el.setPositioning(config.resizeChild.getPositioning());
29231         config.resizeChild.clearPositioning();
29232         if(!config.width || !config.height){
29233             var csize = config.resizeChild.getSize();
29234             this.el.setSize(csize.width, csize.height);
29235         }
29236         if(config.pinned && !config.adjustments){
29237             config.adjustments = "auto";
29238         }
29239     }
29240
29241     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29242     this.proxy.unselectable();
29243     this.proxy.enableDisplayMode('block');
29244
29245     Roo.apply(this, config);
29246
29247     if(this.pinned){
29248         this.disableTrackOver = true;
29249         this.el.addClass("x-resizable-pinned");
29250     }
29251     // if the element isn't positioned, make it relative
29252     var position = this.el.getStyle("position");
29253     if(position != "absolute" && position != "fixed"){
29254         this.el.setStyle("position", "relative");
29255     }
29256     if(!this.handles){ // no handles passed, must be legacy style
29257         this.handles = 's,e,se';
29258         if(this.multiDirectional){
29259             this.handles += ',n,w';
29260         }
29261     }
29262     if(this.handles == "all"){
29263         this.handles = "n s e w ne nw se sw";
29264     }
29265     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29266     var ps = Roo.Resizable.positions;
29267     for(var i = 0, len = hs.length; i < len; i++){
29268         if(hs[i] && ps[hs[i]]){
29269             var pos = ps[hs[i]];
29270             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29271         }
29272     }
29273     // legacy
29274     this.corner = this.southeast;
29275     
29276     // updateBox = the box can move..
29277     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29278         this.updateBox = true;
29279     }
29280
29281     this.activeHandle = null;
29282
29283     if(this.resizeChild){
29284         if(typeof this.resizeChild == "boolean"){
29285             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29286         }else{
29287             this.resizeChild = Roo.get(this.resizeChild, true);
29288         }
29289     }
29290     
29291     if(this.adjustments == "auto"){
29292         var rc = this.resizeChild;
29293         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29294         if(rc && (hw || hn)){
29295             rc.position("relative");
29296             rc.setLeft(hw ? hw.el.getWidth() : 0);
29297             rc.setTop(hn ? hn.el.getHeight() : 0);
29298         }
29299         this.adjustments = [
29300             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29301             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29302         ];
29303     }
29304
29305     if(this.draggable){
29306         this.dd = this.dynamic ?
29307             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29308         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29309     }
29310
29311     // public events
29312     this.addEvents({
29313         /**
29314          * @event beforeresize
29315          * Fired before resize is allowed. Set enabled to false to cancel resize.
29316          * @param {Roo.Resizable} this
29317          * @param {Roo.EventObject} e The mousedown event
29318          */
29319         "beforeresize" : true,
29320         /**
29321          * @event resizing
29322          * Fired a resizing.
29323          * @param {Roo.Resizable} this
29324          * @param {Number} x The new x position
29325          * @param {Number} y The new y position
29326          * @param {Number} w The new w width
29327          * @param {Number} h The new h hight
29328          * @param {Roo.EventObject} e The mouseup event
29329          */
29330         "resizing" : true,
29331         /**
29332          * @event resize
29333          * Fired after a resize.
29334          * @param {Roo.Resizable} this
29335          * @param {Number} width The new width
29336          * @param {Number} height The new height
29337          * @param {Roo.EventObject} e The mouseup event
29338          */
29339         "resize" : true
29340     });
29341
29342     if(this.width !== null && this.height !== null){
29343         this.resizeTo(this.width, this.height);
29344     }else{
29345         this.updateChildSize();
29346     }
29347     if(Roo.isIE){
29348         this.el.dom.style.zoom = 1;
29349     }
29350     Roo.Resizable.superclass.constructor.call(this);
29351 };
29352
29353 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29354         resizeChild : false,
29355         adjustments : [0, 0],
29356         minWidth : 5,
29357         minHeight : 5,
29358         maxWidth : 10000,
29359         maxHeight : 10000,
29360         enabled : true,
29361         animate : false,
29362         duration : .35,
29363         dynamic : false,
29364         handles : false,
29365         multiDirectional : false,
29366         disableTrackOver : false,
29367         easing : 'easeOutStrong',
29368         widthIncrement : 0,
29369         heightIncrement : 0,
29370         pinned : false,
29371         width : null,
29372         height : null,
29373         preserveRatio : false,
29374         transparent: false,
29375         minX: 0,
29376         minY: 0,
29377         draggable: false,
29378
29379         /**
29380          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29381          */
29382         constrainTo: undefined,
29383         /**
29384          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29385          */
29386         resizeRegion: undefined,
29387
29388
29389     /**
29390      * Perform a manual resize
29391      * @param {Number} width
29392      * @param {Number} height
29393      */
29394     resizeTo : function(width, height){
29395         this.el.setSize(width, height);
29396         this.updateChildSize();
29397         this.fireEvent("resize", this, width, height, null);
29398     },
29399
29400     // private
29401     startSizing : function(e, handle){
29402         this.fireEvent("beforeresize", this, e);
29403         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29404
29405             if(!this.overlay){
29406                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29407                 this.overlay.unselectable();
29408                 this.overlay.enableDisplayMode("block");
29409                 this.overlay.on("mousemove", this.onMouseMove, this);
29410                 this.overlay.on("mouseup", this.onMouseUp, this);
29411             }
29412             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29413
29414             this.resizing = true;
29415             this.startBox = this.el.getBox();
29416             this.startPoint = e.getXY();
29417             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29418                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29419
29420             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29421             this.overlay.show();
29422
29423             if(this.constrainTo) {
29424                 var ct = Roo.get(this.constrainTo);
29425                 this.resizeRegion = ct.getRegion().adjust(
29426                     ct.getFrameWidth('t'),
29427                     ct.getFrameWidth('l'),
29428                     -ct.getFrameWidth('b'),
29429                     -ct.getFrameWidth('r')
29430                 );
29431             }
29432
29433             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29434             this.proxy.show();
29435             this.proxy.setBox(this.startBox);
29436             if(!this.dynamic){
29437                 this.proxy.setStyle('visibility', 'visible');
29438             }
29439         }
29440     },
29441
29442     // private
29443     onMouseDown : function(handle, e){
29444         if(this.enabled){
29445             e.stopEvent();
29446             this.activeHandle = handle;
29447             this.startSizing(e, handle);
29448         }
29449     },
29450
29451     // private
29452     onMouseUp : function(e){
29453         var size = this.resizeElement();
29454         this.resizing = false;
29455         this.handleOut();
29456         this.overlay.hide();
29457         this.proxy.hide();
29458         this.fireEvent("resize", this, size.width, size.height, e);
29459     },
29460
29461     // private
29462     updateChildSize : function(){
29463         
29464         if(this.resizeChild){
29465             var el = this.el;
29466             var child = this.resizeChild;
29467             var adj = this.adjustments;
29468             if(el.dom.offsetWidth){
29469                 var b = el.getSize(true);
29470                 child.setSize(b.width+adj[0], b.height+adj[1]);
29471             }
29472             // Second call here for IE
29473             // The first call enables instant resizing and
29474             // the second call corrects scroll bars if they
29475             // exist
29476             if(Roo.isIE){
29477                 setTimeout(function(){
29478                     if(el.dom.offsetWidth){
29479                         var b = el.getSize(true);
29480                         child.setSize(b.width+adj[0], b.height+adj[1]);
29481                     }
29482                 }, 10);
29483             }
29484         }
29485     },
29486
29487     // private
29488     snap : function(value, inc, min){
29489         if(!inc || !value) return value;
29490         var newValue = value;
29491         var m = value % inc;
29492         if(m > 0){
29493             if(m > (inc/2)){
29494                 newValue = value + (inc-m);
29495             }else{
29496                 newValue = value - m;
29497             }
29498         }
29499         return Math.max(min, newValue);
29500     },
29501
29502     // private
29503     resizeElement : function(){
29504         var box = this.proxy.getBox();
29505         if(this.updateBox){
29506             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29507         }else{
29508             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29509         }
29510         this.updateChildSize();
29511         if(!this.dynamic){
29512             this.proxy.hide();
29513         }
29514         return box;
29515     },
29516
29517     // private
29518     constrain : function(v, diff, m, mx){
29519         if(v - diff < m){
29520             diff = v - m;
29521         }else if(v - diff > mx){
29522             diff = mx - v;
29523         }
29524         return diff;
29525     },
29526
29527     // private
29528     onMouseMove : function(e){
29529         
29530         if(this.enabled){
29531             try{// try catch so if something goes wrong the user doesn't get hung
29532
29533             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29534                 return;
29535             }
29536
29537             //var curXY = this.startPoint;
29538             var curSize = this.curSize || this.startBox;
29539             var x = this.startBox.x, y = this.startBox.y;
29540             var ox = x, oy = y;
29541             var w = curSize.width, h = curSize.height;
29542             var ow = w, oh = h;
29543             var mw = this.minWidth, mh = this.minHeight;
29544             var mxw = this.maxWidth, mxh = this.maxHeight;
29545             var wi = this.widthIncrement;
29546             var hi = this.heightIncrement;
29547
29548             var eventXY = e.getXY();
29549             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29550             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29551
29552             var pos = this.activeHandle.position;
29553
29554             switch(pos){
29555                 case "east":
29556                     w += diffX;
29557                     w = Math.min(Math.max(mw, w), mxw);
29558                     break;
29559              
29560                 case "south":
29561                     h += diffY;
29562                     h = Math.min(Math.max(mh, h), mxh);
29563                     break;
29564                 case "southeast":
29565                     w += diffX;
29566                     h += diffY;
29567                     w = Math.min(Math.max(mw, w), mxw);
29568                     h = Math.min(Math.max(mh, h), mxh);
29569                     break;
29570                 case "north":
29571                     diffY = this.constrain(h, diffY, mh, mxh);
29572                     y += diffY;
29573                     h -= diffY;
29574                     break;
29575                 case "hdrag":
29576                     
29577                     if (wi) {
29578                         var adiffX = Math.abs(diffX);
29579                         var sub = (adiffX % wi); // how much 
29580                         if (sub > (wi/2)) { // far enough to snap
29581                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29582                         } else {
29583                             // remove difference.. 
29584                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29585                         }
29586                     }
29587                     x += diffX;
29588                     x = Math.max(this.minX, x);
29589                     break;
29590                 case "west":
29591                     diffX = this.constrain(w, diffX, mw, mxw);
29592                     x += diffX;
29593                     w -= diffX;
29594                     break;
29595                 case "northeast":
29596                     w += diffX;
29597                     w = Math.min(Math.max(mw, w), mxw);
29598                     diffY = this.constrain(h, diffY, mh, mxh);
29599                     y += diffY;
29600                     h -= diffY;
29601                     break;
29602                 case "northwest":
29603                     diffX = this.constrain(w, diffX, mw, mxw);
29604                     diffY = this.constrain(h, diffY, mh, mxh);
29605                     y += diffY;
29606                     h -= diffY;
29607                     x += diffX;
29608                     w -= diffX;
29609                     break;
29610                case "southwest":
29611                     diffX = this.constrain(w, diffX, mw, mxw);
29612                     h += diffY;
29613                     h = Math.min(Math.max(mh, h), mxh);
29614                     x += diffX;
29615                     w -= diffX;
29616                     break;
29617             }
29618
29619             var sw = this.snap(w, wi, mw);
29620             var sh = this.snap(h, hi, mh);
29621             if(sw != w || sh != h){
29622                 switch(pos){
29623                     case "northeast":
29624                         y -= sh - h;
29625                     break;
29626                     case "north":
29627                         y -= sh - h;
29628                         break;
29629                     case "southwest":
29630                         x -= sw - w;
29631                     break;
29632                     case "west":
29633                         x -= sw - w;
29634                         break;
29635                     case "northwest":
29636                         x -= sw - w;
29637                         y -= sh - h;
29638                     break;
29639                 }
29640                 w = sw;
29641                 h = sh;
29642             }
29643
29644             if(this.preserveRatio){
29645                 switch(pos){
29646                     case "southeast":
29647                     case "east":
29648                         h = oh * (w/ow);
29649                         h = Math.min(Math.max(mh, h), mxh);
29650                         w = ow * (h/oh);
29651                        break;
29652                     case "south":
29653                         w = ow * (h/oh);
29654                         w = Math.min(Math.max(mw, w), mxw);
29655                         h = oh * (w/ow);
29656                         break;
29657                     case "northeast":
29658                         w = ow * (h/oh);
29659                         w = Math.min(Math.max(mw, w), mxw);
29660                         h = oh * (w/ow);
29661                     break;
29662                     case "north":
29663                         var tw = w;
29664                         w = ow * (h/oh);
29665                         w = Math.min(Math.max(mw, w), mxw);
29666                         h = oh * (w/ow);
29667                         x += (tw - w) / 2;
29668                         break;
29669                     case "southwest":
29670                         h = oh * (w/ow);
29671                         h = Math.min(Math.max(mh, h), mxh);
29672                         var tw = w;
29673                         w = ow * (h/oh);
29674                         x += tw - w;
29675                         break;
29676                     case "west":
29677                         var th = h;
29678                         h = oh * (w/ow);
29679                         h = Math.min(Math.max(mh, h), mxh);
29680                         y += (th - h) / 2;
29681                         var tw = w;
29682                         w = ow * (h/oh);
29683                         x += tw - w;
29684                        break;
29685                     case "northwest":
29686                         var tw = w;
29687                         var th = h;
29688                         h = oh * (w/ow);
29689                         h = Math.min(Math.max(mh, h), mxh);
29690                         w = ow * (h/oh);
29691                         y += th - h;
29692                         x += tw - w;
29693                        break;
29694
29695                 }
29696             }
29697             if (pos == 'hdrag') {
29698                 w = ow;
29699             }
29700             this.proxy.setBounds(x, y, w, h);
29701             if(this.dynamic){
29702                 this.resizeElement();
29703             }
29704             }catch(e){}
29705         }
29706         this.fireEvent("resizing", this, x, y, w, h, e);
29707     },
29708
29709     // private
29710     handleOver : function(){
29711         if(this.enabled){
29712             this.el.addClass("x-resizable-over");
29713         }
29714     },
29715
29716     // private
29717     handleOut : function(){
29718         if(!this.resizing){
29719             this.el.removeClass("x-resizable-over");
29720         }
29721     },
29722
29723     /**
29724      * Returns the element this component is bound to.
29725      * @return {Roo.Element}
29726      */
29727     getEl : function(){
29728         return this.el;
29729     },
29730
29731     /**
29732      * Returns the resizeChild element (or null).
29733      * @return {Roo.Element}
29734      */
29735     getResizeChild : function(){
29736         return this.resizeChild;
29737     },
29738     groupHandler : function()
29739     {
29740         
29741     },
29742     /**
29743      * Destroys this resizable. If the element was wrapped and
29744      * removeEl is not true then the element remains.
29745      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29746      */
29747     destroy : function(removeEl){
29748         this.proxy.remove();
29749         if(this.overlay){
29750             this.overlay.removeAllListeners();
29751             this.overlay.remove();
29752         }
29753         var ps = Roo.Resizable.positions;
29754         for(var k in ps){
29755             if(typeof ps[k] != "function" && this[ps[k]]){
29756                 var h = this[ps[k]];
29757                 h.el.removeAllListeners();
29758                 h.el.remove();
29759             }
29760         }
29761         if(removeEl){
29762             this.el.update("");
29763             this.el.remove();
29764         }
29765     }
29766 });
29767
29768 // private
29769 // hash to map config positions to true positions
29770 Roo.Resizable.positions = {
29771     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29772     hd: "hdrag"
29773 };
29774
29775 // private
29776 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29777     if(!this.tpl){
29778         // only initialize the template if resizable is used
29779         var tpl = Roo.DomHelper.createTemplate(
29780             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29781         );
29782         tpl.compile();
29783         Roo.Resizable.Handle.prototype.tpl = tpl;
29784     }
29785     this.position = pos;
29786     this.rz = rz;
29787     // show north drag fro topdra
29788     var handlepos = pos == 'hdrag' ? 'north' : pos;
29789     
29790     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29791     if (pos == 'hdrag') {
29792         this.el.setStyle('cursor', 'pointer');
29793     }
29794     this.el.unselectable();
29795     if(transparent){
29796         this.el.setOpacity(0);
29797     }
29798     this.el.on("mousedown", this.onMouseDown, this);
29799     if(!disableTrackOver){
29800         this.el.on("mouseover", this.onMouseOver, this);
29801         this.el.on("mouseout", this.onMouseOut, this);
29802     }
29803 };
29804
29805 // private
29806 Roo.Resizable.Handle.prototype = {
29807     afterResize : function(rz){
29808         Roo.log('after?');
29809         // do nothing
29810     },
29811     // private
29812     onMouseDown : function(e){
29813         this.rz.onMouseDown(this, e);
29814     },
29815     // private
29816     onMouseOver : function(e){
29817         this.rz.handleOver(this, e);
29818     },
29819     // private
29820     onMouseOut : function(e){
29821         this.rz.handleOut(this, e);
29822     }
29823 };/*
29824  * Based on:
29825  * Ext JS Library 1.1.1
29826  * Copyright(c) 2006-2007, Ext JS, LLC.
29827  *
29828  * Originally Released Under LGPL - original licence link has changed is not relivant.
29829  *
29830  * Fork - LGPL
29831  * <script type="text/javascript">
29832  */
29833
29834 /**
29835  * @class Roo.Editor
29836  * @extends Roo.Component
29837  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29838  * @constructor
29839  * Create a new Editor
29840  * @param {Roo.form.Field} field The Field object (or descendant)
29841  * @param {Object} config The config object
29842  */
29843 Roo.Editor = function(field, config){
29844     Roo.Editor.superclass.constructor.call(this, config);
29845     this.field = field;
29846     this.addEvents({
29847         /**
29848              * @event beforestartedit
29849              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29850              * false from the handler of this event.
29851              * @param {Editor} this
29852              * @param {Roo.Element} boundEl The underlying element bound to this editor
29853              * @param {Mixed} value The field value being set
29854              */
29855         "beforestartedit" : true,
29856         /**
29857              * @event startedit
29858              * Fires when this editor is displayed
29859              * @param {Roo.Element} boundEl The underlying element bound to this editor
29860              * @param {Mixed} value The starting field value
29861              */
29862         "startedit" : true,
29863         /**
29864              * @event beforecomplete
29865              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29866              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29867              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29868              * event will not fire since no edit actually occurred.
29869              * @param {Editor} this
29870              * @param {Mixed} value The current field value
29871              * @param {Mixed} startValue The original field value
29872              */
29873         "beforecomplete" : true,
29874         /**
29875              * @event complete
29876              * Fires after editing is complete and any changed value has been written to the underlying field.
29877              * @param {Editor} this
29878              * @param {Mixed} value The current field value
29879              * @param {Mixed} startValue The original field value
29880              */
29881         "complete" : true,
29882         /**
29883          * @event specialkey
29884          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29885          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29886          * @param {Roo.form.Field} this
29887          * @param {Roo.EventObject} e The event object
29888          */
29889         "specialkey" : true
29890     });
29891 };
29892
29893 Roo.extend(Roo.Editor, Roo.Component, {
29894     /**
29895      * @cfg {Boolean/String} autosize
29896      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29897      * or "height" to adopt the height only (defaults to false)
29898      */
29899     /**
29900      * @cfg {Boolean} revertInvalid
29901      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29902      * validation fails (defaults to true)
29903      */
29904     /**
29905      * @cfg {Boolean} ignoreNoChange
29906      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29907      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29908      * will never be ignored.
29909      */
29910     /**
29911      * @cfg {Boolean} hideEl
29912      * False to keep the bound element visible while the editor is displayed (defaults to true)
29913      */
29914     /**
29915      * @cfg {Mixed} value
29916      * The data value of the underlying field (defaults to "")
29917      */
29918     value : "",
29919     /**
29920      * @cfg {String} alignment
29921      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29922      */
29923     alignment: "c-c?",
29924     /**
29925      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29926      * for bottom-right shadow (defaults to "frame")
29927      */
29928     shadow : "frame",
29929     /**
29930      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29931      */
29932     constrain : false,
29933     /**
29934      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29935      */
29936     completeOnEnter : false,
29937     /**
29938      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29939      */
29940     cancelOnEsc : false,
29941     /**
29942      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29943      */
29944     updateEl : false,
29945
29946     // private
29947     onRender : function(ct, position){
29948         this.el = new Roo.Layer({
29949             shadow: this.shadow,
29950             cls: "x-editor",
29951             parentEl : ct,
29952             shim : this.shim,
29953             shadowOffset:4,
29954             id: this.id,
29955             constrain: this.constrain
29956         });
29957         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29958         if(this.field.msgTarget != 'title'){
29959             this.field.msgTarget = 'qtip';
29960         }
29961         this.field.render(this.el);
29962         if(Roo.isGecko){
29963             this.field.el.dom.setAttribute('autocomplete', 'off');
29964         }
29965         this.field.on("specialkey", this.onSpecialKey, this);
29966         if(this.swallowKeys){
29967             this.field.el.swallowEvent(['keydown','keypress']);
29968         }
29969         this.field.show();
29970         this.field.on("blur", this.onBlur, this);
29971         if(this.field.grow){
29972             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29973         }
29974     },
29975
29976     onSpecialKey : function(field, e)
29977     {
29978         //Roo.log('editor onSpecialKey');
29979         if(this.completeOnEnter && e.getKey() == e.ENTER){
29980             e.stopEvent();
29981             this.completeEdit();
29982             return;
29983         }
29984         // do not fire special key otherwise it might hide close the editor...
29985         if(e.getKey() == e.ENTER){    
29986             return;
29987         }
29988         if(this.cancelOnEsc && e.getKey() == e.ESC){
29989             this.cancelEdit();
29990             return;
29991         } 
29992         this.fireEvent('specialkey', field, e);
29993     
29994     },
29995
29996     /**
29997      * Starts the editing process and shows the editor.
29998      * @param {String/HTMLElement/Element} el The element to edit
29999      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30000       * to the innerHTML of el.
30001      */
30002     startEdit : function(el, value){
30003         if(this.editing){
30004             this.completeEdit();
30005         }
30006         this.boundEl = Roo.get(el);
30007         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30008         if(!this.rendered){
30009             this.render(this.parentEl || document.body);
30010         }
30011         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30012             return;
30013         }
30014         this.startValue = v;
30015         this.field.setValue(v);
30016         if(this.autoSize){
30017             var sz = this.boundEl.getSize();
30018             switch(this.autoSize){
30019                 case "width":
30020                 this.setSize(sz.width,  "");
30021                 break;
30022                 case "height":
30023                 this.setSize("",  sz.height);
30024                 break;
30025                 default:
30026                 this.setSize(sz.width,  sz.height);
30027             }
30028         }
30029         this.el.alignTo(this.boundEl, this.alignment);
30030         this.editing = true;
30031         if(Roo.QuickTips){
30032             Roo.QuickTips.disable();
30033         }
30034         this.show();
30035     },
30036
30037     /**
30038      * Sets the height and width of this editor.
30039      * @param {Number} width The new width
30040      * @param {Number} height The new height
30041      */
30042     setSize : function(w, h){
30043         this.field.setSize(w, h);
30044         if(this.el){
30045             this.el.sync();
30046         }
30047     },
30048
30049     /**
30050      * Realigns the editor to the bound field based on the current alignment config value.
30051      */
30052     realign : function(){
30053         this.el.alignTo(this.boundEl, this.alignment);
30054     },
30055
30056     /**
30057      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30058      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30059      */
30060     completeEdit : function(remainVisible){
30061         if(!this.editing){
30062             return;
30063         }
30064         var v = this.getValue();
30065         if(this.revertInvalid !== false && !this.field.isValid()){
30066             v = this.startValue;
30067             this.cancelEdit(true);
30068         }
30069         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30070             this.editing = false;
30071             this.hide();
30072             return;
30073         }
30074         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30075             this.editing = false;
30076             if(this.updateEl && this.boundEl){
30077                 this.boundEl.update(v);
30078             }
30079             if(remainVisible !== true){
30080                 this.hide();
30081             }
30082             this.fireEvent("complete", this, v, this.startValue);
30083         }
30084     },
30085
30086     // private
30087     onShow : function(){
30088         this.el.show();
30089         if(this.hideEl !== false){
30090             this.boundEl.hide();
30091         }
30092         this.field.show();
30093         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30094             this.fixIEFocus = true;
30095             this.deferredFocus.defer(50, this);
30096         }else{
30097             this.field.focus();
30098         }
30099         this.fireEvent("startedit", this.boundEl, this.startValue);
30100     },
30101
30102     deferredFocus : function(){
30103         if(this.editing){
30104             this.field.focus();
30105         }
30106     },
30107
30108     /**
30109      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30110      * reverted to the original starting value.
30111      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30112      * cancel (defaults to false)
30113      */
30114     cancelEdit : function(remainVisible){
30115         if(this.editing){
30116             this.setValue(this.startValue);
30117             if(remainVisible !== true){
30118                 this.hide();
30119             }
30120         }
30121     },
30122
30123     // private
30124     onBlur : function(){
30125         if(this.allowBlur !== true && this.editing){
30126             this.completeEdit();
30127         }
30128     },
30129
30130     // private
30131     onHide : function(){
30132         if(this.editing){
30133             this.completeEdit();
30134             return;
30135         }
30136         this.field.blur();
30137         if(this.field.collapse){
30138             this.field.collapse();
30139         }
30140         this.el.hide();
30141         if(this.hideEl !== false){
30142             this.boundEl.show();
30143         }
30144         if(Roo.QuickTips){
30145             Roo.QuickTips.enable();
30146         }
30147     },
30148
30149     /**
30150      * Sets the data value of the editor
30151      * @param {Mixed} value Any valid value supported by the underlying field
30152      */
30153     setValue : function(v){
30154         this.field.setValue(v);
30155     },
30156
30157     /**
30158      * Gets the data value of the editor
30159      * @return {Mixed} The data value
30160      */
30161     getValue : function(){
30162         return this.field.getValue();
30163     }
30164 });/*
30165  * Based on:
30166  * Ext JS Library 1.1.1
30167  * Copyright(c) 2006-2007, Ext JS, LLC.
30168  *
30169  * Originally Released Under LGPL - original licence link has changed is not relivant.
30170  *
30171  * Fork - LGPL
30172  * <script type="text/javascript">
30173  */
30174  
30175 /**
30176  * @class Roo.BasicDialog
30177  * @extends Roo.util.Observable
30178  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30179  * <pre><code>
30180 var dlg = new Roo.BasicDialog("my-dlg", {
30181     height: 200,
30182     width: 300,
30183     minHeight: 100,
30184     minWidth: 150,
30185     modal: true,
30186     proxyDrag: true,
30187     shadow: true
30188 });
30189 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30190 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30191 dlg.addButton('Cancel', dlg.hide, dlg);
30192 dlg.show();
30193 </code></pre>
30194   <b>A Dialog should always be a direct child of the body element.</b>
30195  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30196  * @cfg {String} title Default text to display in the title bar (defaults to null)
30197  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30198  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30199  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30200  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30201  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30202  * (defaults to null with no animation)
30203  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30204  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30205  * property for valid values (defaults to 'all')
30206  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30207  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30208  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30209  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30210  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30211  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30212  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30213  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30214  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30215  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30216  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30217  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30218  * draggable = true (defaults to false)
30219  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30220  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30221  * shadow (defaults to false)
30222  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30223  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30224  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30225  * @cfg {Array} buttons Array of buttons
30226  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30227  * @constructor
30228  * Create a new BasicDialog.
30229  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30230  * @param {Object} config Configuration options
30231  */
30232 Roo.BasicDialog = function(el, config){
30233     this.el = Roo.get(el);
30234     var dh = Roo.DomHelper;
30235     if(!this.el && config && config.autoCreate){
30236         if(typeof config.autoCreate == "object"){
30237             if(!config.autoCreate.id){
30238                 config.autoCreate.id = el;
30239             }
30240             this.el = dh.append(document.body,
30241                         config.autoCreate, true);
30242         }else{
30243             this.el = dh.append(document.body,
30244                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30245         }
30246     }
30247     el = this.el;
30248     el.setDisplayed(true);
30249     el.hide = this.hideAction;
30250     this.id = el.id;
30251     el.addClass("x-dlg");
30252
30253     Roo.apply(this, config);
30254
30255     this.proxy = el.createProxy("x-dlg-proxy");
30256     this.proxy.hide = this.hideAction;
30257     this.proxy.setOpacity(.5);
30258     this.proxy.hide();
30259
30260     if(config.width){
30261         el.setWidth(config.width);
30262     }
30263     if(config.height){
30264         el.setHeight(config.height);
30265     }
30266     this.size = el.getSize();
30267     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30268         this.xy = [config.x,config.y];
30269     }else{
30270         this.xy = el.getCenterXY(true);
30271     }
30272     /** The header element @type Roo.Element */
30273     this.header = el.child("> .x-dlg-hd");
30274     /** The body element @type Roo.Element */
30275     this.body = el.child("> .x-dlg-bd");
30276     /** The footer element @type Roo.Element */
30277     this.footer = el.child("> .x-dlg-ft");
30278
30279     if(!this.header){
30280         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30281     }
30282     if(!this.body){
30283         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30284     }
30285
30286     this.header.unselectable();
30287     if(this.title){
30288         this.header.update(this.title);
30289     }
30290     // this element allows the dialog to be focused for keyboard event
30291     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30292     this.focusEl.swallowEvent("click", true);
30293
30294     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30295
30296     // wrap the body and footer for special rendering
30297     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30298     if(this.footer){
30299         this.bwrap.dom.appendChild(this.footer.dom);
30300     }
30301
30302     this.bg = this.el.createChild({
30303         tag: "div", cls:"x-dlg-bg",
30304         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30305     });
30306     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30307
30308
30309     if(this.autoScroll !== false && !this.autoTabs){
30310         this.body.setStyle("overflow", "auto");
30311     }
30312
30313     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30314
30315     if(this.closable !== false){
30316         this.el.addClass("x-dlg-closable");
30317         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30318         this.close.on("click", this.closeClick, this);
30319         this.close.addClassOnOver("x-dlg-close-over");
30320     }
30321     if(this.collapsible !== false){
30322         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30323         this.collapseBtn.on("click", this.collapseClick, this);
30324         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30325         this.header.on("dblclick", this.collapseClick, this);
30326     }
30327     if(this.resizable !== false){
30328         this.el.addClass("x-dlg-resizable");
30329         this.resizer = new Roo.Resizable(el, {
30330             minWidth: this.minWidth || 80,
30331             minHeight:this.minHeight || 80,
30332             handles: this.resizeHandles || "all",
30333             pinned: true
30334         });
30335         this.resizer.on("beforeresize", this.beforeResize, this);
30336         this.resizer.on("resize", this.onResize, this);
30337     }
30338     if(this.draggable !== false){
30339         el.addClass("x-dlg-draggable");
30340         if (!this.proxyDrag) {
30341             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30342         }
30343         else {
30344             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30345         }
30346         dd.setHandleElId(this.header.id);
30347         dd.endDrag = this.endMove.createDelegate(this);
30348         dd.startDrag = this.startMove.createDelegate(this);
30349         dd.onDrag = this.onDrag.createDelegate(this);
30350         dd.scroll = false;
30351         this.dd = dd;
30352     }
30353     if(this.modal){
30354         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30355         this.mask.enableDisplayMode("block");
30356         this.mask.hide();
30357         this.el.addClass("x-dlg-modal");
30358     }
30359     if(this.shadow){
30360         this.shadow = new Roo.Shadow({
30361             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30362             offset : this.shadowOffset
30363         });
30364     }else{
30365         this.shadowOffset = 0;
30366     }
30367     if(Roo.useShims && this.shim !== false){
30368         this.shim = this.el.createShim();
30369         this.shim.hide = this.hideAction;
30370         this.shim.hide();
30371     }else{
30372         this.shim = false;
30373     }
30374     if(this.autoTabs){
30375         this.initTabs();
30376     }
30377     if (this.buttons) { 
30378         var bts= this.buttons;
30379         this.buttons = [];
30380         Roo.each(bts, function(b) {
30381             this.addButton(b);
30382         }, this);
30383     }
30384     
30385     
30386     this.addEvents({
30387         /**
30388          * @event keydown
30389          * Fires when a key is pressed
30390          * @param {Roo.BasicDialog} this
30391          * @param {Roo.EventObject} e
30392          */
30393         "keydown" : true,
30394         /**
30395          * @event move
30396          * Fires when this dialog is moved by the user.
30397          * @param {Roo.BasicDialog} this
30398          * @param {Number} x The new page X
30399          * @param {Number} y The new page Y
30400          */
30401         "move" : true,
30402         /**
30403          * @event resize
30404          * Fires when this dialog is resized by the user.
30405          * @param {Roo.BasicDialog} this
30406          * @param {Number} width The new width
30407          * @param {Number} height The new height
30408          */
30409         "resize" : true,
30410         /**
30411          * @event beforehide
30412          * Fires before this dialog is hidden.
30413          * @param {Roo.BasicDialog} this
30414          */
30415         "beforehide" : true,
30416         /**
30417          * @event hide
30418          * Fires when this dialog is hidden.
30419          * @param {Roo.BasicDialog} this
30420          */
30421         "hide" : true,
30422         /**
30423          * @event beforeshow
30424          * Fires before this dialog is shown.
30425          * @param {Roo.BasicDialog} this
30426          */
30427         "beforeshow" : true,
30428         /**
30429          * @event show
30430          * Fires when this dialog is shown.
30431          * @param {Roo.BasicDialog} this
30432          */
30433         "show" : true
30434     });
30435     el.on("keydown", this.onKeyDown, this);
30436     el.on("mousedown", this.toFront, this);
30437     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30438     this.el.hide();
30439     Roo.DialogManager.register(this);
30440     Roo.BasicDialog.superclass.constructor.call(this);
30441 };
30442
30443 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30444     shadowOffset: Roo.isIE ? 6 : 5,
30445     minHeight: 80,
30446     minWidth: 200,
30447     minButtonWidth: 75,
30448     defaultButton: null,
30449     buttonAlign: "right",
30450     tabTag: 'div',
30451     firstShow: true,
30452
30453     /**
30454      * Sets the dialog title text
30455      * @param {String} text The title text to display
30456      * @return {Roo.BasicDialog} this
30457      */
30458     setTitle : function(text){
30459         this.header.update(text);
30460         return this;
30461     },
30462
30463     // private
30464     closeClick : function(){
30465         this.hide();
30466     },
30467
30468     // private
30469     collapseClick : function(){
30470         this[this.collapsed ? "expand" : "collapse"]();
30471     },
30472
30473     /**
30474      * Collapses the dialog to its minimized state (only the title bar is visible).
30475      * Equivalent to the user clicking the collapse dialog button.
30476      */
30477     collapse : function(){
30478         if(!this.collapsed){
30479             this.collapsed = true;
30480             this.el.addClass("x-dlg-collapsed");
30481             this.restoreHeight = this.el.getHeight();
30482             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30483         }
30484     },
30485
30486     /**
30487      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30488      * clicking the expand dialog button.
30489      */
30490     expand : function(){
30491         if(this.collapsed){
30492             this.collapsed = false;
30493             this.el.removeClass("x-dlg-collapsed");
30494             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30495         }
30496     },
30497
30498     /**
30499      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30500      * @return {Roo.TabPanel} The tabs component
30501      */
30502     initTabs : function(){
30503         var tabs = this.getTabs();
30504         while(tabs.getTab(0)){
30505             tabs.removeTab(0);
30506         }
30507         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30508             var dom = el.dom;
30509             tabs.addTab(Roo.id(dom), dom.title);
30510             dom.title = "";
30511         });
30512         tabs.activate(0);
30513         return tabs;
30514     },
30515
30516     // private
30517     beforeResize : function(){
30518         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30519     },
30520
30521     // private
30522     onResize : function(){
30523         this.refreshSize();
30524         this.syncBodyHeight();
30525         this.adjustAssets();
30526         this.focus();
30527         this.fireEvent("resize", this, this.size.width, this.size.height);
30528     },
30529
30530     // private
30531     onKeyDown : function(e){
30532         if(this.isVisible()){
30533             this.fireEvent("keydown", this, e);
30534         }
30535     },
30536
30537     /**
30538      * Resizes the dialog.
30539      * @param {Number} width
30540      * @param {Number} height
30541      * @return {Roo.BasicDialog} this
30542      */
30543     resizeTo : function(width, height){
30544         this.el.setSize(width, height);
30545         this.size = {width: width, height: height};
30546         this.syncBodyHeight();
30547         if(this.fixedcenter){
30548             this.center();
30549         }
30550         if(this.isVisible()){
30551             this.constrainXY();
30552             this.adjustAssets();
30553         }
30554         this.fireEvent("resize", this, width, height);
30555         return this;
30556     },
30557
30558
30559     /**
30560      * Resizes the dialog to fit the specified content size.
30561      * @param {Number} width
30562      * @param {Number} height
30563      * @return {Roo.BasicDialog} this
30564      */
30565     setContentSize : function(w, h){
30566         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30567         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30568         //if(!this.el.isBorderBox()){
30569             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30570             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30571         //}
30572         if(this.tabs){
30573             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30574             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30575         }
30576         this.resizeTo(w, h);
30577         return this;
30578     },
30579
30580     /**
30581      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30582      * executed in response to a particular key being pressed while the dialog is active.
30583      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30584      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30585      * @param {Function} fn The function to call
30586      * @param {Object} scope (optional) The scope of the function
30587      * @return {Roo.BasicDialog} this
30588      */
30589     addKeyListener : function(key, fn, scope){
30590         var keyCode, shift, ctrl, alt;
30591         if(typeof key == "object" && !(key instanceof Array)){
30592             keyCode = key["key"];
30593             shift = key["shift"];
30594             ctrl = key["ctrl"];
30595             alt = key["alt"];
30596         }else{
30597             keyCode = key;
30598         }
30599         var handler = function(dlg, e){
30600             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30601                 var k = e.getKey();
30602                 if(keyCode instanceof Array){
30603                     for(var i = 0, len = keyCode.length; i < len; i++){
30604                         if(keyCode[i] == k){
30605                           fn.call(scope || window, dlg, k, e);
30606                           return;
30607                         }
30608                     }
30609                 }else{
30610                     if(k == keyCode){
30611                         fn.call(scope || window, dlg, k, e);
30612                     }
30613                 }
30614             }
30615         };
30616         this.on("keydown", handler);
30617         return this;
30618     },
30619
30620     /**
30621      * Returns the TabPanel component (creates it if it doesn't exist).
30622      * Note: If you wish to simply check for the existence of tabs without creating them,
30623      * check for a null 'tabs' property.
30624      * @return {Roo.TabPanel} The tabs component
30625      */
30626     getTabs : function(){
30627         if(!this.tabs){
30628             this.el.addClass("x-dlg-auto-tabs");
30629             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30630             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30631         }
30632         return this.tabs;
30633     },
30634
30635     /**
30636      * Adds a button to the footer section of the dialog.
30637      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30638      * object or a valid Roo.DomHelper element config
30639      * @param {Function} handler The function called when the button is clicked
30640      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30641      * @return {Roo.Button} The new button
30642      */
30643     addButton : function(config, handler, scope){
30644         var dh = Roo.DomHelper;
30645         if(!this.footer){
30646             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30647         }
30648         if(!this.btnContainer){
30649             var tb = this.footer.createChild({
30650
30651                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30652                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30653             }, null, true);
30654             this.btnContainer = tb.firstChild.firstChild.firstChild;
30655         }
30656         var bconfig = {
30657             handler: handler,
30658             scope: scope,
30659             minWidth: this.minButtonWidth,
30660             hideParent:true
30661         };
30662         if(typeof config == "string"){
30663             bconfig.text = config;
30664         }else{
30665             if(config.tag){
30666                 bconfig.dhconfig = config;
30667             }else{
30668                 Roo.apply(bconfig, config);
30669             }
30670         }
30671         var fc = false;
30672         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30673             bconfig.position = Math.max(0, bconfig.position);
30674             fc = this.btnContainer.childNodes[bconfig.position];
30675         }
30676          
30677         var btn = new Roo.Button(
30678             fc ? 
30679                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30680                 : this.btnContainer.appendChild(document.createElement("td")),
30681             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30682             bconfig
30683         );
30684         this.syncBodyHeight();
30685         if(!this.buttons){
30686             /**
30687              * Array of all the buttons that have been added to this dialog via addButton
30688              * @type Array
30689              */
30690             this.buttons = [];
30691         }
30692         this.buttons.push(btn);
30693         return btn;
30694     },
30695
30696     /**
30697      * Sets the default button to be focused when the dialog is displayed.
30698      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30699      * @return {Roo.BasicDialog} this
30700      */
30701     setDefaultButton : function(btn){
30702         this.defaultButton = btn;
30703         return this;
30704     },
30705
30706     // private
30707     getHeaderFooterHeight : function(safe){
30708         var height = 0;
30709         if(this.header){
30710            height += this.header.getHeight();
30711         }
30712         if(this.footer){
30713            var fm = this.footer.getMargins();
30714             height += (this.footer.getHeight()+fm.top+fm.bottom);
30715         }
30716         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30717         height += this.centerBg.getPadding("tb");
30718         return height;
30719     },
30720
30721     // private
30722     syncBodyHeight : function()
30723     {
30724         var bd = this.body, // the text
30725             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30726             bw = this.bwrap;
30727         var height = this.size.height - this.getHeaderFooterHeight(false);
30728         bd.setHeight(height-bd.getMargins("tb"));
30729         var hh = this.header.getHeight();
30730         var h = this.size.height-hh;
30731         cb.setHeight(h);
30732         
30733         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30734         bw.setHeight(h-cb.getPadding("tb"));
30735         
30736         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30737         bd.setWidth(bw.getWidth(true));
30738         if(this.tabs){
30739             this.tabs.syncHeight();
30740             if(Roo.isIE){
30741                 this.tabs.el.repaint();
30742             }
30743         }
30744     },
30745
30746     /**
30747      * Restores the previous state of the dialog if Roo.state is configured.
30748      * @return {Roo.BasicDialog} this
30749      */
30750     restoreState : function(){
30751         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30752         if(box && box.width){
30753             this.xy = [box.x, box.y];
30754             this.resizeTo(box.width, box.height);
30755         }
30756         return this;
30757     },
30758
30759     // private
30760     beforeShow : function(){
30761         this.expand();
30762         if(this.fixedcenter){
30763             this.xy = this.el.getCenterXY(true);
30764         }
30765         if(this.modal){
30766             Roo.get(document.body).addClass("x-body-masked");
30767             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30768             this.mask.show();
30769         }
30770         this.constrainXY();
30771     },
30772
30773     // private
30774     animShow : function(){
30775         var b = Roo.get(this.animateTarget).getBox();
30776         this.proxy.setSize(b.width, b.height);
30777         this.proxy.setLocation(b.x, b.y);
30778         this.proxy.show();
30779         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30780                     true, .35, this.showEl.createDelegate(this));
30781     },
30782
30783     /**
30784      * Shows the dialog.
30785      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30786      * @return {Roo.BasicDialog} this
30787      */
30788     show : function(animateTarget){
30789         if (this.fireEvent("beforeshow", this) === false){
30790             return;
30791         }
30792         if(this.syncHeightBeforeShow){
30793             this.syncBodyHeight();
30794         }else if(this.firstShow){
30795             this.firstShow = false;
30796             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30797         }
30798         this.animateTarget = animateTarget || this.animateTarget;
30799         if(!this.el.isVisible()){
30800             this.beforeShow();
30801             if(this.animateTarget && Roo.get(this.animateTarget)){
30802                 this.animShow();
30803             }else{
30804                 this.showEl();
30805             }
30806         }
30807         return this;
30808     },
30809
30810     // private
30811     showEl : function(){
30812         this.proxy.hide();
30813         this.el.setXY(this.xy);
30814         this.el.show();
30815         this.adjustAssets(true);
30816         this.toFront();
30817         this.focus();
30818         // IE peekaboo bug - fix found by Dave Fenwick
30819         if(Roo.isIE){
30820             this.el.repaint();
30821         }
30822         this.fireEvent("show", this);
30823     },
30824
30825     /**
30826      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30827      * dialog itself will receive focus.
30828      */
30829     focus : function(){
30830         if(this.defaultButton){
30831             this.defaultButton.focus();
30832         }else{
30833             this.focusEl.focus();
30834         }
30835     },
30836
30837     // private
30838     constrainXY : function(){
30839         if(this.constraintoviewport !== false){
30840             if(!this.viewSize){
30841                 if(this.container){
30842                     var s = this.container.getSize();
30843                     this.viewSize = [s.width, s.height];
30844                 }else{
30845                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30846                 }
30847             }
30848             var s = Roo.get(this.container||document).getScroll();
30849
30850             var x = this.xy[0], y = this.xy[1];
30851             var w = this.size.width, h = this.size.height;
30852             var vw = this.viewSize[0], vh = this.viewSize[1];
30853             // only move it if it needs it
30854             var moved = false;
30855             // first validate right/bottom
30856             if(x + w > vw+s.left){
30857                 x = vw - w;
30858                 moved = true;
30859             }
30860             if(y + h > vh+s.top){
30861                 y = vh - h;
30862                 moved = true;
30863             }
30864             // then make sure top/left isn't negative
30865             if(x < s.left){
30866                 x = s.left;
30867                 moved = true;
30868             }
30869             if(y < s.top){
30870                 y = s.top;
30871                 moved = true;
30872             }
30873             if(moved){
30874                 // cache xy
30875                 this.xy = [x, y];
30876                 if(this.isVisible()){
30877                     this.el.setLocation(x, y);
30878                     this.adjustAssets();
30879                 }
30880             }
30881         }
30882     },
30883
30884     // private
30885     onDrag : function(){
30886         if(!this.proxyDrag){
30887             this.xy = this.el.getXY();
30888             this.adjustAssets();
30889         }
30890     },
30891
30892     // private
30893     adjustAssets : function(doShow){
30894         var x = this.xy[0], y = this.xy[1];
30895         var w = this.size.width, h = this.size.height;
30896         if(doShow === true){
30897             if(this.shadow){
30898                 this.shadow.show(this.el);
30899             }
30900             if(this.shim){
30901                 this.shim.show();
30902             }
30903         }
30904         if(this.shadow && this.shadow.isVisible()){
30905             this.shadow.show(this.el);
30906         }
30907         if(this.shim && this.shim.isVisible()){
30908             this.shim.setBounds(x, y, w, h);
30909         }
30910     },
30911
30912     // private
30913     adjustViewport : function(w, h){
30914         if(!w || !h){
30915             w = Roo.lib.Dom.getViewWidth();
30916             h = Roo.lib.Dom.getViewHeight();
30917         }
30918         // cache the size
30919         this.viewSize = [w, h];
30920         if(this.modal && this.mask.isVisible()){
30921             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30922             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30923         }
30924         if(this.isVisible()){
30925             this.constrainXY();
30926         }
30927     },
30928
30929     /**
30930      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30931      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30932      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30933      */
30934     destroy : function(removeEl){
30935         if(this.isVisible()){
30936             this.animateTarget = null;
30937             this.hide();
30938         }
30939         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30940         if(this.tabs){
30941             this.tabs.destroy(removeEl);
30942         }
30943         Roo.destroy(
30944              this.shim,
30945              this.proxy,
30946              this.resizer,
30947              this.close,
30948              this.mask
30949         );
30950         if(this.dd){
30951             this.dd.unreg();
30952         }
30953         if(this.buttons){
30954            for(var i = 0, len = this.buttons.length; i < len; i++){
30955                this.buttons[i].destroy();
30956            }
30957         }
30958         this.el.removeAllListeners();
30959         if(removeEl === true){
30960             this.el.update("");
30961             this.el.remove();
30962         }
30963         Roo.DialogManager.unregister(this);
30964     },
30965
30966     // private
30967     startMove : function(){
30968         if(this.proxyDrag){
30969             this.proxy.show();
30970         }
30971         if(this.constraintoviewport !== false){
30972             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30973         }
30974     },
30975
30976     // private
30977     endMove : function(){
30978         if(!this.proxyDrag){
30979             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30980         }else{
30981             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30982             this.proxy.hide();
30983         }
30984         this.refreshSize();
30985         this.adjustAssets();
30986         this.focus();
30987         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30988     },
30989
30990     /**
30991      * Brings this dialog to the front of any other visible dialogs
30992      * @return {Roo.BasicDialog} this
30993      */
30994     toFront : function(){
30995         Roo.DialogManager.bringToFront(this);
30996         return this;
30997     },
30998
30999     /**
31000      * Sends this dialog to the back (under) of any other visible dialogs
31001      * @return {Roo.BasicDialog} this
31002      */
31003     toBack : function(){
31004         Roo.DialogManager.sendToBack(this);
31005         return this;
31006     },
31007
31008     /**
31009      * Centers this dialog in the viewport
31010      * @return {Roo.BasicDialog} this
31011      */
31012     center : function(){
31013         var xy = this.el.getCenterXY(true);
31014         this.moveTo(xy[0], xy[1]);
31015         return this;
31016     },
31017
31018     /**
31019      * Moves the dialog's top-left corner to the specified point
31020      * @param {Number} x
31021      * @param {Number} y
31022      * @return {Roo.BasicDialog} this
31023      */
31024     moveTo : function(x, y){
31025         this.xy = [x,y];
31026         if(this.isVisible()){
31027             this.el.setXY(this.xy);
31028             this.adjustAssets();
31029         }
31030         return this;
31031     },
31032
31033     /**
31034      * Aligns the dialog to the specified element
31035      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31036      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31037      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31038      * @return {Roo.BasicDialog} this
31039      */
31040     alignTo : function(element, position, offsets){
31041         this.xy = this.el.getAlignToXY(element, position, offsets);
31042         if(this.isVisible()){
31043             this.el.setXY(this.xy);
31044             this.adjustAssets();
31045         }
31046         return this;
31047     },
31048
31049     /**
31050      * Anchors an element to another element and realigns it when the window is resized.
31051      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31052      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31053      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31054      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31055      * is a number, it is used as the buffer delay (defaults to 50ms).
31056      * @return {Roo.BasicDialog} this
31057      */
31058     anchorTo : function(el, alignment, offsets, monitorScroll){
31059         var action = function(){
31060             this.alignTo(el, alignment, offsets);
31061         };
31062         Roo.EventManager.onWindowResize(action, this);
31063         var tm = typeof monitorScroll;
31064         if(tm != 'undefined'){
31065             Roo.EventManager.on(window, 'scroll', action, this,
31066                 {buffer: tm == 'number' ? monitorScroll : 50});
31067         }
31068         action.call(this);
31069         return this;
31070     },
31071
31072     /**
31073      * Returns true if the dialog is visible
31074      * @return {Boolean}
31075      */
31076     isVisible : function(){
31077         return this.el.isVisible();
31078     },
31079
31080     // private
31081     animHide : function(callback){
31082         var b = Roo.get(this.animateTarget).getBox();
31083         this.proxy.show();
31084         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31085         this.el.hide();
31086         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31087                     this.hideEl.createDelegate(this, [callback]));
31088     },
31089
31090     /**
31091      * Hides the dialog.
31092      * @param {Function} callback (optional) Function to call when the dialog is hidden
31093      * @return {Roo.BasicDialog} this
31094      */
31095     hide : function(callback){
31096         if (this.fireEvent("beforehide", this) === false){
31097             return;
31098         }
31099         if(this.shadow){
31100             this.shadow.hide();
31101         }
31102         if(this.shim) {
31103           this.shim.hide();
31104         }
31105         // sometimes animateTarget seems to get set.. causing problems...
31106         // this just double checks..
31107         if(this.animateTarget && Roo.get(this.animateTarget)) {
31108            this.animHide(callback);
31109         }else{
31110             this.el.hide();
31111             this.hideEl(callback);
31112         }
31113         return this;
31114     },
31115
31116     // private
31117     hideEl : function(callback){
31118         this.proxy.hide();
31119         if(this.modal){
31120             this.mask.hide();
31121             Roo.get(document.body).removeClass("x-body-masked");
31122         }
31123         this.fireEvent("hide", this);
31124         if(typeof callback == "function"){
31125             callback();
31126         }
31127     },
31128
31129     // private
31130     hideAction : function(){
31131         this.setLeft("-10000px");
31132         this.setTop("-10000px");
31133         this.setStyle("visibility", "hidden");
31134     },
31135
31136     // private
31137     refreshSize : function(){
31138         this.size = this.el.getSize();
31139         this.xy = this.el.getXY();
31140         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31141     },
31142
31143     // private
31144     // z-index is managed by the DialogManager and may be overwritten at any time
31145     setZIndex : function(index){
31146         if(this.modal){
31147             this.mask.setStyle("z-index", index);
31148         }
31149         if(this.shim){
31150             this.shim.setStyle("z-index", ++index);
31151         }
31152         if(this.shadow){
31153             this.shadow.setZIndex(++index);
31154         }
31155         this.el.setStyle("z-index", ++index);
31156         if(this.proxy){
31157             this.proxy.setStyle("z-index", ++index);
31158         }
31159         if(this.resizer){
31160             this.resizer.proxy.setStyle("z-index", ++index);
31161         }
31162
31163         this.lastZIndex = index;
31164     },
31165
31166     /**
31167      * Returns the element for this dialog
31168      * @return {Roo.Element} The underlying dialog Element
31169      */
31170     getEl : function(){
31171         return this.el;
31172     }
31173 });
31174
31175 /**
31176  * @class Roo.DialogManager
31177  * Provides global access to BasicDialogs that have been created and
31178  * support for z-indexing (layering) multiple open dialogs.
31179  */
31180 Roo.DialogManager = function(){
31181     var list = {};
31182     var accessList = [];
31183     var front = null;
31184
31185     // private
31186     var sortDialogs = function(d1, d2){
31187         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31188     };
31189
31190     // private
31191     var orderDialogs = function(){
31192         accessList.sort(sortDialogs);
31193         var seed = Roo.DialogManager.zseed;
31194         for(var i = 0, len = accessList.length; i < len; i++){
31195             var dlg = accessList[i];
31196             if(dlg){
31197                 dlg.setZIndex(seed + (i*10));
31198             }
31199         }
31200     };
31201
31202     return {
31203         /**
31204          * The starting z-index for BasicDialogs (defaults to 9000)
31205          * @type Number The z-index value
31206          */
31207         zseed : 9000,
31208
31209         // private
31210         register : function(dlg){
31211             list[dlg.id] = dlg;
31212             accessList.push(dlg);
31213         },
31214
31215         // private
31216         unregister : function(dlg){
31217             delete list[dlg.id];
31218             var i=0;
31219             var len=0;
31220             if(!accessList.indexOf){
31221                 for(  i = 0, len = accessList.length; i < len; i++){
31222                     if(accessList[i] == dlg){
31223                         accessList.splice(i, 1);
31224                         return;
31225                     }
31226                 }
31227             }else{
31228                  i = accessList.indexOf(dlg);
31229                 if(i != -1){
31230                     accessList.splice(i, 1);
31231                 }
31232             }
31233         },
31234
31235         /**
31236          * Gets a registered dialog by id
31237          * @param {String/Object} id The id of the dialog or a dialog
31238          * @return {Roo.BasicDialog} this
31239          */
31240         get : function(id){
31241             return typeof id == "object" ? id : list[id];
31242         },
31243
31244         /**
31245          * Brings the specified dialog to the front
31246          * @param {String/Object} dlg The id of the dialog or a dialog
31247          * @return {Roo.BasicDialog} this
31248          */
31249         bringToFront : function(dlg){
31250             dlg = this.get(dlg);
31251             if(dlg != front){
31252                 front = dlg;
31253                 dlg._lastAccess = new Date().getTime();
31254                 orderDialogs();
31255             }
31256             return dlg;
31257         },
31258
31259         /**
31260          * Sends the specified dialog to the back
31261          * @param {String/Object} dlg The id of the dialog or a dialog
31262          * @return {Roo.BasicDialog} this
31263          */
31264         sendToBack : function(dlg){
31265             dlg = this.get(dlg);
31266             dlg._lastAccess = -(new Date().getTime());
31267             orderDialogs();
31268             return dlg;
31269         },
31270
31271         /**
31272          * Hides all dialogs
31273          */
31274         hideAll : function(){
31275             for(var id in list){
31276                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31277                     list[id].hide();
31278                 }
31279             }
31280         }
31281     };
31282 }();
31283
31284 /**
31285  * @class Roo.LayoutDialog
31286  * @extends Roo.BasicDialog
31287  * Dialog which provides adjustments for working with a layout in a Dialog.
31288  * Add your necessary layout config options to the dialog's config.<br>
31289  * Example usage (including a nested layout):
31290  * <pre><code>
31291 if(!dialog){
31292     dialog = new Roo.LayoutDialog("download-dlg", {
31293         modal: true,
31294         width:600,
31295         height:450,
31296         shadow:true,
31297         minWidth:500,
31298         minHeight:350,
31299         autoTabs:true,
31300         proxyDrag:true,
31301         // layout config merges with the dialog config
31302         center:{
31303             tabPosition: "top",
31304             alwaysShowTabs: true
31305         }
31306     });
31307     dialog.addKeyListener(27, dialog.hide, dialog);
31308     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31309     dialog.addButton("Build It!", this.getDownload, this);
31310
31311     // we can even add nested layouts
31312     var innerLayout = new Roo.BorderLayout("dl-inner", {
31313         east: {
31314             initialSize: 200,
31315             autoScroll:true,
31316             split:true
31317         },
31318         center: {
31319             autoScroll:true
31320         }
31321     });
31322     innerLayout.beginUpdate();
31323     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31324     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31325     innerLayout.endUpdate(true);
31326
31327     var layout = dialog.getLayout();
31328     layout.beginUpdate();
31329     layout.add("center", new Roo.ContentPanel("standard-panel",
31330                         {title: "Download the Source", fitToFrame:true}));
31331     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31332                {title: "Build your own roo.js"}));
31333     layout.getRegion("center").showPanel(sp);
31334     layout.endUpdate();
31335 }
31336 </code></pre>
31337     * @constructor
31338     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31339     * @param {Object} config configuration options
31340   */
31341 Roo.LayoutDialog = function(el, cfg){
31342     
31343     var config=  cfg;
31344     if (typeof(cfg) == 'undefined') {
31345         config = Roo.apply({}, el);
31346         // not sure why we use documentElement here.. - it should always be body.
31347         // IE7 borks horribly if we use documentElement.
31348         // webkit also does not like documentElement - it creates a body element...
31349         el = Roo.get( document.body || document.documentElement ).createChild();
31350         //config.autoCreate = true;
31351     }
31352     
31353     
31354     config.autoTabs = false;
31355     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31356     this.body.setStyle({overflow:"hidden", position:"relative"});
31357     this.layout = new Roo.BorderLayout(this.body.dom, config);
31358     this.layout.monitorWindowResize = false;
31359     this.el.addClass("x-dlg-auto-layout");
31360     // fix case when center region overwrites center function
31361     this.center = Roo.BasicDialog.prototype.center;
31362     this.on("show", this.layout.layout, this.layout, true);
31363     if (config.items) {
31364         var xitems = config.items;
31365         delete config.items;
31366         Roo.each(xitems, this.addxtype, this);
31367     }
31368     
31369     
31370 };
31371 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31372     /**
31373      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31374      * @deprecated
31375      */
31376     endUpdate : function(){
31377         this.layout.endUpdate();
31378     },
31379
31380     /**
31381      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31382      *  @deprecated
31383      */
31384     beginUpdate : function(){
31385         this.layout.beginUpdate();
31386     },
31387
31388     /**
31389      * Get the BorderLayout for this dialog
31390      * @return {Roo.BorderLayout}
31391      */
31392     getLayout : function(){
31393         return this.layout;
31394     },
31395
31396     showEl : function(){
31397         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31398         if(Roo.isIE7){
31399             this.layout.layout();
31400         }
31401     },
31402
31403     // private
31404     // Use the syncHeightBeforeShow config option to control this automatically
31405     syncBodyHeight : function(){
31406         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31407         if(this.layout){this.layout.layout();}
31408     },
31409     
31410       /**
31411      * Add an xtype element (actually adds to the layout.)
31412      * @return {Object} xdata xtype object data.
31413      */
31414     
31415     addxtype : function(c) {
31416         return this.layout.addxtype(c);
31417     }
31418 });/*
31419  * Based on:
31420  * Ext JS Library 1.1.1
31421  * Copyright(c) 2006-2007, Ext JS, LLC.
31422  *
31423  * Originally Released Under LGPL - original licence link has changed is not relivant.
31424  *
31425  * Fork - LGPL
31426  * <script type="text/javascript">
31427  */
31428  
31429 /**
31430  * @class Roo.MessageBox
31431  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31432  * Example usage:
31433  *<pre><code>
31434 // Basic alert:
31435 Roo.Msg.alert('Status', 'Changes saved successfully.');
31436
31437 // Prompt for user data:
31438 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31439     if (btn == 'ok'){
31440         // process text value...
31441     }
31442 });
31443
31444 // Show a dialog using config options:
31445 Roo.Msg.show({
31446    title:'Save Changes?',
31447    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31448    buttons: Roo.Msg.YESNOCANCEL,
31449    fn: processResult,
31450    animEl: 'elId'
31451 });
31452 </code></pre>
31453  * @singleton
31454  */
31455 Roo.MessageBox = function(){
31456     var dlg, opt, mask, waitTimer;
31457     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31458     var buttons, activeTextEl, bwidth;
31459
31460     // private
31461     var handleButton = function(button){
31462         dlg.hide();
31463         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31464     };
31465
31466     // private
31467     var handleHide = function(){
31468         if(opt && opt.cls){
31469             dlg.el.removeClass(opt.cls);
31470         }
31471         if(waitTimer){
31472             Roo.TaskMgr.stop(waitTimer);
31473             waitTimer = null;
31474         }
31475     };
31476
31477     // private
31478     var updateButtons = function(b){
31479         var width = 0;
31480         if(!b){
31481             buttons["ok"].hide();
31482             buttons["cancel"].hide();
31483             buttons["yes"].hide();
31484             buttons["no"].hide();
31485             dlg.footer.dom.style.display = 'none';
31486             return width;
31487         }
31488         dlg.footer.dom.style.display = '';
31489         for(var k in buttons){
31490             if(typeof buttons[k] != "function"){
31491                 if(b[k]){
31492                     buttons[k].show();
31493                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31494                     width += buttons[k].el.getWidth()+15;
31495                 }else{
31496                     buttons[k].hide();
31497                 }
31498             }
31499         }
31500         return width;
31501     };
31502
31503     // private
31504     var handleEsc = function(d, k, e){
31505         if(opt && opt.closable !== false){
31506             dlg.hide();
31507         }
31508         if(e){
31509             e.stopEvent();
31510         }
31511     };
31512
31513     return {
31514         /**
31515          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31516          * @return {Roo.BasicDialog} The BasicDialog element
31517          */
31518         getDialog : function(){
31519            if(!dlg){
31520                 dlg = new Roo.BasicDialog("x-msg-box", {
31521                     autoCreate : true,
31522                     shadow: true,
31523                     draggable: true,
31524                     resizable:false,
31525                     constraintoviewport:false,
31526                     fixedcenter:true,
31527                     collapsible : false,
31528                     shim:true,
31529                     modal: true,
31530                     width:400, height:100,
31531                     buttonAlign:"center",
31532                     closeClick : function(){
31533                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31534                             handleButton("no");
31535                         }else{
31536                             handleButton("cancel");
31537                         }
31538                     }
31539                 });
31540                 dlg.on("hide", handleHide);
31541                 mask = dlg.mask;
31542                 dlg.addKeyListener(27, handleEsc);
31543                 buttons = {};
31544                 var bt = this.buttonText;
31545                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31546                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31547                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31548                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31549                 bodyEl = dlg.body.createChild({
31550
31551                     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>'
31552                 });
31553                 msgEl = bodyEl.dom.firstChild;
31554                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31555                 textboxEl.enableDisplayMode();
31556                 textboxEl.addKeyListener([10,13], function(){
31557                     if(dlg.isVisible() && opt && opt.buttons){
31558                         if(opt.buttons.ok){
31559                             handleButton("ok");
31560                         }else if(opt.buttons.yes){
31561                             handleButton("yes");
31562                         }
31563                     }
31564                 });
31565                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31566                 textareaEl.enableDisplayMode();
31567                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31568                 progressEl.enableDisplayMode();
31569                 var pf = progressEl.dom.firstChild;
31570                 if (pf) {
31571                     pp = Roo.get(pf.firstChild);
31572                     pp.setHeight(pf.offsetHeight);
31573                 }
31574                 
31575             }
31576             return dlg;
31577         },
31578
31579         /**
31580          * Updates the message box body text
31581          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31582          * the XHTML-compliant non-breaking space character '&amp;#160;')
31583          * @return {Roo.MessageBox} This message box
31584          */
31585         updateText : function(text){
31586             if(!dlg.isVisible() && !opt.width){
31587                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31588             }
31589             msgEl.innerHTML = text || '&#160;';
31590       
31591             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31592             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31593             var w = Math.max(
31594                     Math.min(opt.width || cw , this.maxWidth), 
31595                     Math.max(opt.minWidth || this.minWidth, bwidth)
31596             );
31597             if(opt.prompt){
31598                 activeTextEl.setWidth(w);
31599             }
31600             if(dlg.isVisible()){
31601                 dlg.fixedcenter = false;
31602             }
31603             // to big, make it scroll. = But as usual stupid IE does not support
31604             // !important..
31605             
31606             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31607                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31608                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31609             } else {
31610                 bodyEl.dom.style.height = '';
31611                 bodyEl.dom.style.overflowY = '';
31612             }
31613             if (cw > w) {
31614                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31615             } else {
31616                 bodyEl.dom.style.overflowX = '';
31617             }
31618             
31619             dlg.setContentSize(w, bodyEl.getHeight());
31620             if(dlg.isVisible()){
31621                 dlg.fixedcenter = true;
31622             }
31623             return this;
31624         },
31625
31626         /**
31627          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31628          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31629          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31630          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31631          * @return {Roo.MessageBox} This message box
31632          */
31633         updateProgress : function(value, text){
31634             if(text){
31635                 this.updateText(text);
31636             }
31637             if (pp) { // weird bug on my firefox - for some reason this is not defined
31638                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31639             }
31640             return this;
31641         },        
31642
31643         /**
31644          * Returns true if the message box is currently displayed
31645          * @return {Boolean} True if the message box is visible, else false
31646          */
31647         isVisible : function(){
31648             return dlg && dlg.isVisible();  
31649         },
31650
31651         /**
31652          * Hides the message box if it is displayed
31653          */
31654         hide : function(){
31655             if(this.isVisible()){
31656                 dlg.hide();
31657             }  
31658         },
31659
31660         /**
31661          * Displays a new message box, or reinitializes an existing message box, based on the config options
31662          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31663          * The following config object properties are supported:
31664          * <pre>
31665 Property    Type             Description
31666 ----------  ---------------  ------------------------------------------------------------------------------------
31667 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31668                                    closes (defaults to undefined)
31669 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31670                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31671 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31672                                    progress and wait dialogs will ignore this property and always hide the
31673                                    close button as they can only be closed programmatically.
31674 cls               String           A custom CSS class to apply to the message box element
31675 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31676                                    displayed (defaults to 75)
31677 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31678                                    function will be btn (the name of the button that was clicked, if applicable,
31679                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31680                                    Progress and wait dialogs will ignore this option since they do not respond to
31681                                    user actions and can only be closed programmatically, so any required function
31682                                    should be called by the same code after it closes the dialog.
31683 icon              String           A CSS class that provides a background image to be used as an icon for
31684                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31685 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31686 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31687 modal             Boolean          False to allow user interaction with the page while the message box is
31688                                    displayed (defaults to true)
31689 msg               String           A string that will replace the existing message box body text (defaults
31690                                    to the XHTML-compliant non-breaking space character '&#160;')
31691 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31692 progress          Boolean          True to display a progress bar (defaults to false)
31693 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31694 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31695 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31696 title             String           The title text
31697 value             String           The string value to set into the active textbox element if displayed
31698 wait              Boolean          True to display a progress bar (defaults to false)
31699 width             Number           The width of the dialog in pixels
31700 </pre>
31701          *
31702          * Example usage:
31703          * <pre><code>
31704 Roo.Msg.show({
31705    title: 'Address',
31706    msg: 'Please enter your address:',
31707    width: 300,
31708    buttons: Roo.MessageBox.OKCANCEL,
31709    multiline: true,
31710    fn: saveAddress,
31711    animEl: 'addAddressBtn'
31712 });
31713 </code></pre>
31714          * @param {Object} config Configuration options
31715          * @return {Roo.MessageBox} This message box
31716          */
31717         show : function(options)
31718         {
31719             
31720             // this causes nightmares if you show one dialog after another
31721             // especially on callbacks..
31722              
31723             if(this.isVisible()){
31724                 
31725                 this.hide();
31726                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31727                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31728                 Roo.log("New Dialog Message:" +  options.msg )
31729                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31730                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31731                 
31732             }
31733             var d = this.getDialog();
31734             opt = options;
31735             d.setTitle(opt.title || "&#160;");
31736             d.close.setDisplayed(opt.closable !== false);
31737             activeTextEl = textboxEl;
31738             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31739             if(opt.prompt){
31740                 if(opt.multiline){
31741                     textboxEl.hide();
31742                     textareaEl.show();
31743                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31744                         opt.multiline : this.defaultTextHeight);
31745                     activeTextEl = textareaEl;
31746                 }else{
31747                     textboxEl.show();
31748                     textareaEl.hide();
31749                 }
31750             }else{
31751                 textboxEl.hide();
31752                 textareaEl.hide();
31753             }
31754             progressEl.setDisplayed(opt.progress === true);
31755             this.updateProgress(0);
31756             activeTextEl.dom.value = opt.value || "";
31757             if(opt.prompt){
31758                 dlg.setDefaultButton(activeTextEl);
31759             }else{
31760                 var bs = opt.buttons;
31761                 var db = null;
31762                 if(bs && bs.ok){
31763                     db = buttons["ok"];
31764                 }else if(bs && bs.yes){
31765                     db = buttons["yes"];
31766                 }
31767                 dlg.setDefaultButton(db);
31768             }
31769             bwidth = updateButtons(opt.buttons);
31770             this.updateText(opt.msg);
31771             if(opt.cls){
31772                 d.el.addClass(opt.cls);
31773             }
31774             d.proxyDrag = opt.proxyDrag === true;
31775             d.modal = opt.modal !== false;
31776             d.mask = opt.modal !== false ? mask : false;
31777             if(!d.isVisible()){
31778                 // force it to the end of the z-index stack so it gets a cursor in FF
31779                 document.body.appendChild(dlg.el.dom);
31780                 d.animateTarget = null;
31781                 d.show(options.animEl);
31782             }
31783             return this;
31784         },
31785
31786         /**
31787          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31788          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31789          * and closing the message box when the process is complete.
31790          * @param {String} title The title bar text
31791          * @param {String} msg The message box body text
31792          * @return {Roo.MessageBox} This message box
31793          */
31794         progress : function(title, msg){
31795             this.show({
31796                 title : title,
31797                 msg : msg,
31798                 buttons: false,
31799                 progress:true,
31800                 closable:false,
31801                 minWidth: this.minProgressWidth,
31802                 modal : true
31803             });
31804             return this;
31805         },
31806
31807         /**
31808          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31809          * If a callback function is passed it will be called after the user clicks the button, and the
31810          * id of the button that was clicked will be passed as the only parameter to the callback
31811          * (could also be the top-right close button).
31812          * @param {String} title The title bar text
31813          * @param {String} msg The message box body text
31814          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31815          * @param {Object} scope (optional) The scope of the callback function
31816          * @return {Roo.MessageBox} This message box
31817          */
31818         alert : function(title, msg, fn, scope){
31819             this.show({
31820                 title : title,
31821                 msg : msg,
31822                 buttons: this.OK,
31823                 fn: fn,
31824                 scope : scope,
31825                 modal : true
31826             });
31827             return this;
31828         },
31829
31830         /**
31831          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31832          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31833          * You are responsible for closing the message box when the process is complete.
31834          * @param {String} msg The message box body text
31835          * @param {String} title (optional) The title bar text
31836          * @return {Roo.MessageBox} This message box
31837          */
31838         wait : function(msg, title){
31839             this.show({
31840                 title : title,
31841                 msg : msg,
31842                 buttons: false,
31843                 closable:false,
31844                 progress:true,
31845                 modal:true,
31846                 width:300,
31847                 wait:true
31848             });
31849             waitTimer = Roo.TaskMgr.start({
31850                 run: function(i){
31851                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31852                 },
31853                 interval: 1000
31854             });
31855             return this;
31856         },
31857
31858         /**
31859          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31860          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31861          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31862          * @param {String} title The title bar text
31863          * @param {String} msg The message box body text
31864          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31865          * @param {Object} scope (optional) The scope of the callback function
31866          * @return {Roo.MessageBox} This message box
31867          */
31868         confirm : function(title, msg, fn, scope){
31869             this.show({
31870                 title : title,
31871                 msg : msg,
31872                 buttons: this.YESNO,
31873                 fn: fn,
31874                 scope : scope,
31875                 modal : true
31876             });
31877             return this;
31878         },
31879
31880         /**
31881          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31882          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31883          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31884          * (could also be the top-right close button) and the text that was entered will be passed as the two
31885          * parameters to the callback.
31886          * @param {String} title The title bar text
31887          * @param {String} msg The message box body text
31888          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31889          * @param {Object} scope (optional) The scope of the callback function
31890          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31891          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31892          * @return {Roo.MessageBox} This message box
31893          */
31894         prompt : function(title, msg, fn, scope, multiline){
31895             this.show({
31896                 title : title,
31897                 msg : msg,
31898                 buttons: this.OKCANCEL,
31899                 fn: fn,
31900                 minWidth:250,
31901                 scope : scope,
31902                 prompt:true,
31903                 multiline: multiline,
31904                 modal : true
31905             });
31906             return this;
31907         },
31908
31909         /**
31910          * Button config that displays a single OK button
31911          * @type Object
31912          */
31913         OK : {ok:true},
31914         /**
31915          * Button config that displays Yes and No buttons
31916          * @type Object
31917          */
31918         YESNO : {yes:true, no:true},
31919         /**
31920          * Button config that displays OK and Cancel buttons
31921          * @type Object
31922          */
31923         OKCANCEL : {ok:true, cancel:true},
31924         /**
31925          * Button config that displays Yes, No and Cancel buttons
31926          * @type Object
31927          */
31928         YESNOCANCEL : {yes:true, no:true, cancel:true},
31929
31930         /**
31931          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31932          * @type Number
31933          */
31934         defaultTextHeight : 75,
31935         /**
31936          * The maximum width in pixels of the message box (defaults to 600)
31937          * @type Number
31938          */
31939         maxWidth : 600,
31940         /**
31941          * The minimum width in pixels of the message box (defaults to 100)
31942          * @type Number
31943          */
31944         minWidth : 100,
31945         /**
31946          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31947          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31948          * @type Number
31949          */
31950         minProgressWidth : 250,
31951         /**
31952          * An object containing the default button text strings that can be overriden for localized language support.
31953          * Supported properties are: ok, cancel, yes and no.
31954          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31955          * @type Object
31956          */
31957         buttonText : {
31958             ok : "OK",
31959             cancel : "Cancel",
31960             yes : "Yes",
31961             no : "No"
31962         }
31963     };
31964 }();
31965
31966 /**
31967  * Shorthand for {@link Roo.MessageBox}
31968  */
31969 Roo.Msg = Roo.MessageBox;/*
31970  * Based on:
31971  * Ext JS Library 1.1.1
31972  * Copyright(c) 2006-2007, Ext JS, LLC.
31973  *
31974  * Originally Released Under LGPL - original licence link has changed is not relivant.
31975  *
31976  * Fork - LGPL
31977  * <script type="text/javascript">
31978  */
31979 /**
31980  * @class Roo.QuickTips
31981  * Provides attractive and customizable tooltips for any element.
31982  * @singleton
31983  */
31984 Roo.QuickTips = function(){
31985     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31986     var ce, bd, xy, dd;
31987     var visible = false, disabled = true, inited = false;
31988     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31989     
31990     var onOver = function(e){
31991         if(disabled){
31992             return;
31993         }
31994         var t = e.getTarget();
31995         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31996             return;
31997         }
31998         if(ce && t == ce.el){
31999             clearTimeout(hideProc);
32000             return;
32001         }
32002         if(t && tagEls[t.id]){
32003             tagEls[t.id].el = t;
32004             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32005             return;
32006         }
32007         var ttp, et = Roo.fly(t);
32008         var ns = cfg.namespace;
32009         if(tm.interceptTitles && t.title){
32010             ttp = t.title;
32011             t.qtip = ttp;
32012             t.removeAttribute("title");
32013             e.preventDefault();
32014         }else{
32015             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32016         }
32017         if(ttp){
32018             showProc = show.defer(tm.showDelay, tm, [{
32019                 el: t, 
32020                 text: ttp, 
32021                 width: et.getAttributeNS(ns, cfg.width),
32022                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32023                 title: et.getAttributeNS(ns, cfg.title),
32024                     cls: et.getAttributeNS(ns, cfg.cls)
32025             }]);
32026         }
32027     };
32028     
32029     var onOut = function(e){
32030         clearTimeout(showProc);
32031         var t = e.getTarget();
32032         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32033             hideProc = setTimeout(hide, tm.hideDelay);
32034         }
32035     };
32036     
32037     var onMove = function(e){
32038         if(disabled){
32039             return;
32040         }
32041         xy = e.getXY();
32042         xy[1] += 18;
32043         if(tm.trackMouse && ce){
32044             el.setXY(xy);
32045         }
32046     };
32047     
32048     var onDown = function(e){
32049         clearTimeout(showProc);
32050         clearTimeout(hideProc);
32051         if(!e.within(el)){
32052             if(tm.hideOnClick){
32053                 hide();
32054                 tm.disable();
32055                 tm.enable.defer(100, tm);
32056             }
32057         }
32058     };
32059     
32060     var getPad = function(){
32061         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32062     };
32063
32064     var show = function(o){
32065         if(disabled){
32066             return;
32067         }
32068         clearTimeout(dismissProc);
32069         ce = o;
32070         if(removeCls){ // in case manually hidden
32071             el.removeClass(removeCls);
32072             removeCls = null;
32073         }
32074         if(ce.cls){
32075             el.addClass(ce.cls);
32076             removeCls = ce.cls;
32077         }
32078         if(ce.title){
32079             tipTitle.update(ce.title);
32080             tipTitle.show();
32081         }else{
32082             tipTitle.update('');
32083             tipTitle.hide();
32084         }
32085         el.dom.style.width  = tm.maxWidth+'px';
32086         //tipBody.dom.style.width = '';
32087         tipBodyText.update(o.text);
32088         var p = getPad(), w = ce.width;
32089         if(!w){
32090             var td = tipBodyText.dom;
32091             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32092             if(aw > tm.maxWidth){
32093                 w = tm.maxWidth;
32094             }else if(aw < tm.minWidth){
32095                 w = tm.minWidth;
32096             }else{
32097                 w = aw;
32098             }
32099         }
32100         //tipBody.setWidth(w);
32101         el.setWidth(parseInt(w, 10) + p);
32102         if(ce.autoHide === false){
32103             close.setDisplayed(true);
32104             if(dd){
32105                 dd.unlock();
32106             }
32107         }else{
32108             close.setDisplayed(false);
32109             if(dd){
32110                 dd.lock();
32111             }
32112         }
32113         if(xy){
32114             el.avoidY = xy[1]-18;
32115             el.setXY(xy);
32116         }
32117         if(tm.animate){
32118             el.setOpacity(.1);
32119             el.setStyle("visibility", "visible");
32120             el.fadeIn({callback: afterShow});
32121         }else{
32122             afterShow();
32123         }
32124     };
32125     
32126     var afterShow = function(){
32127         if(ce){
32128             el.show();
32129             esc.enable();
32130             if(tm.autoDismiss && ce.autoHide !== false){
32131                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32132             }
32133         }
32134     };
32135     
32136     var hide = function(noanim){
32137         clearTimeout(dismissProc);
32138         clearTimeout(hideProc);
32139         ce = null;
32140         if(el.isVisible()){
32141             esc.disable();
32142             if(noanim !== true && tm.animate){
32143                 el.fadeOut({callback: afterHide});
32144             }else{
32145                 afterHide();
32146             } 
32147         }
32148     };
32149     
32150     var afterHide = function(){
32151         el.hide();
32152         if(removeCls){
32153             el.removeClass(removeCls);
32154             removeCls = null;
32155         }
32156     };
32157     
32158     return {
32159         /**
32160         * @cfg {Number} minWidth
32161         * The minimum width of the quick tip (defaults to 40)
32162         */
32163        minWidth : 40,
32164         /**
32165         * @cfg {Number} maxWidth
32166         * The maximum width of the quick tip (defaults to 300)
32167         */
32168        maxWidth : 300,
32169         /**
32170         * @cfg {Boolean} interceptTitles
32171         * True to automatically use the element's DOM title value if available (defaults to false)
32172         */
32173        interceptTitles : false,
32174         /**
32175         * @cfg {Boolean} trackMouse
32176         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32177         */
32178        trackMouse : false,
32179         /**
32180         * @cfg {Boolean} hideOnClick
32181         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32182         */
32183        hideOnClick : true,
32184         /**
32185         * @cfg {Number} showDelay
32186         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32187         */
32188        showDelay : 500,
32189         /**
32190         * @cfg {Number} hideDelay
32191         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32192         */
32193        hideDelay : 200,
32194         /**
32195         * @cfg {Boolean} autoHide
32196         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32197         * Used in conjunction with hideDelay.
32198         */
32199        autoHide : true,
32200         /**
32201         * @cfg {Boolean}
32202         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32203         * (defaults to true).  Used in conjunction with autoDismissDelay.
32204         */
32205        autoDismiss : true,
32206         /**
32207         * @cfg {Number}
32208         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32209         */
32210        autoDismissDelay : 5000,
32211        /**
32212         * @cfg {Boolean} animate
32213         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32214         */
32215        animate : false,
32216
32217        /**
32218         * @cfg {String} title
32219         * Title text to display (defaults to '').  This can be any valid HTML markup.
32220         */
32221         title: '',
32222        /**
32223         * @cfg {String} text
32224         * Body text to display (defaults to '').  This can be any valid HTML markup.
32225         */
32226         text : '',
32227        /**
32228         * @cfg {String} cls
32229         * A CSS class to apply to the base quick tip element (defaults to '').
32230         */
32231         cls : '',
32232        /**
32233         * @cfg {Number} width
32234         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32235         * minWidth or maxWidth.
32236         */
32237         width : null,
32238
32239     /**
32240      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32241      * or display QuickTips in a page.
32242      */
32243        init : function(){
32244           tm = Roo.QuickTips;
32245           cfg = tm.tagConfig;
32246           if(!inited){
32247               if(!Roo.isReady){ // allow calling of init() before onReady
32248                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32249                   return;
32250               }
32251               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32252               el.fxDefaults = {stopFx: true};
32253               // maximum custom styling
32254               //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>');
32255               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>');              
32256               tipTitle = el.child('h3');
32257               tipTitle.enableDisplayMode("block");
32258               tipBody = el.child('div.x-tip-bd');
32259               tipBodyText = el.child('div.x-tip-bd-inner');
32260               //bdLeft = el.child('div.x-tip-bd-left');
32261               //bdRight = el.child('div.x-tip-bd-right');
32262               close = el.child('div.x-tip-close');
32263               close.enableDisplayMode("block");
32264               close.on("click", hide);
32265               var d = Roo.get(document);
32266               d.on("mousedown", onDown);
32267               d.on("mouseover", onOver);
32268               d.on("mouseout", onOut);
32269               d.on("mousemove", onMove);
32270               esc = d.addKeyListener(27, hide);
32271               esc.disable();
32272               if(Roo.dd.DD){
32273                   dd = el.initDD("default", null, {
32274                       onDrag : function(){
32275                           el.sync();  
32276                       }
32277                   });
32278                   dd.setHandleElId(tipTitle.id);
32279                   dd.lock();
32280               }
32281               inited = true;
32282           }
32283           this.enable(); 
32284        },
32285
32286     /**
32287      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32288      * are supported:
32289      * <pre>
32290 Property    Type                   Description
32291 ----------  ---------------------  ------------------------------------------------------------------------
32292 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32293      * </ul>
32294      * @param {Object} config The config object
32295      */
32296        register : function(config){
32297            var cs = config instanceof Array ? config : arguments;
32298            for(var i = 0, len = cs.length; i < len; i++) {
32299                var c = cs[i];
32300                var target = c.target;
32301                if(target){
32302                    if(target instanceof Array){
32303                        for(var j = 0, jlen = target.length; j < jlen; j++){
32304                            tagEls[target[j]] = c;
32305                        }
32306                    }else{
32307                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32308                    }
32309                }
32310            }
32311        },
32312
32313     /**
32314      * Removes this quick tip from its element and destroys it.
32315      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32316      */
32317        unregister : function(el){
32318            delete tagEls[Roo.id(el)];
32319        },
32320
32321     /**
32322      * Enable this quick tip.
32323      */
32324        enable : function(){
32325            if(inited && disabled){
32326                locks.pop();
32327                if(locks.length < 1){
32328                    disabled = false;
32329                }
32330            }
32331        },
32332
32333     /**
32334      * Disable this quick tip.
32335      */
32336        disable : function(){
32337           disabled = true;
32338           clearTimeout(showProc);
32339           clearTimeout(hideProc);
32340           clearTimeout(dismissProc);
32341           if(ce){
32342               hide(true);
32343           }
32344           locks.push(1);
32345        },
32346
32347     /**
32348      * Returns true if the quick tip is enabled, else false.
32349      */
32350        isEnabled : function(){
32351             return !disabled;
32352        },
32353
32354         // private
32355        tagConfig : {
32356            namespace : "ext",
32357            attribute : "qtip",
32358            width : "width",
32359            target : "target",
32360            title : "qtitle",
32361            hide : "hide",
32362            cls : "qclass"
32363        }
32364    };
32365 }();
32366
32367 // backwards compat
32368 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32369  * Based on:
32370  * Ext JS Library 1.1.1
32371  * Copyright(c) 2006-2007, Ext JS, LLC.
32372  *
32373  * Originally Released Under LGPL - original licence link has changed is not relivant.
32374  *
32375  * Fork - LGPL
32376  * <script type="text/javascript">
32377  */
32378  
32379
32380 /**
32381  * @class Roo.tree.TreePanel
32382  * @extends Roo.data.Tree
32383
32384  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32385  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32386  * @cfg {Boolean} enableDD true to enable drag and drop
32387  * @cfg {Boolean} enableDrag true to enable just drag
32388  * @cfg {Boolean} enableDrop true to enable just drop
32389  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32390  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32391  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32392  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32393  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32394  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32395  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32396  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32397  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32398  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32399  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32400  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32401  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32402  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32403  * @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>
32404  * @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>
32405  * 
32406  * @constructor
32407  * @param {String/HTMLElement/Element} el The container element
32408  * @param {Object} config
32409  */
32410 Roo.tree.TreePanel = function(el, config){
32411     var root = false;
32412     var loader = false;
32413     if (config.root) {
32414         root = config.root;
32415         delete config.root;
32416     }
32417     if (config.loader) {
32418         loader = config.loader;
32419         delete config.loader;
32420     }
32421     
32422     Roo.apply(this, config);
32423     Roo.tree.TreePanel.superclass.constructor.call(this);
32424     this.el = Roo.get(el);
32425     this.el.addClass('x-tree');
32426     //console.log(root);
32427     if (root) {
32428         this.setRootNode( Roo.factory(root, Roo.tree));
32429     }
32430     if (loader) {
32431         this.loader = Roo.factory(loader, Roo.tree);
32432     }
32433    /**
32434     * Read-only. The id of the container element becomes this TreePanel's id.
32435     */
32436     this.id = this.el.id;
32437     this.addEvents({
32438         /**
32439         * @event beforeload
32440         * Fires before a node is loaded, return false to cancel
32441         * @param {Node} node The node being loaded
32442         */
32443         "beforeload" : true,
32444         /**
32445         * @event load
32446         * Fires when a node is loaded
32447         * @param {Node} node The node that was loaded
32448         */
32449         "load" : true,
32450         /**
32451         * @event textchange
32452         * Fires when the text for a node is changed
32453         * @param {Node} node The node
32454         * @param {String} text The new text
32455         * @param {String} oldText The old text
32456         */
32457         "textchange" : true,
32458         /**
32459         * @event beforeexpand
32460         * Fires before a node is expanded, return false to cancel.
32461         * @param {Node} node The node
32462         * @param {Boolean} deep
32463         * @param {Boolean} anim
32464         */
32465         "beforeexpand" : true,
32466         /**
32467         * @event beforecollapse
32468         * Fires before a node is collapsed, return false to cancel.
32469         * @param {Node} node The node
32470         * @param {Boolean} deep
32471         * @param {Boolean} anim
32472         */
32473         "beforecollapse" : true,
32474         /**
32475         * @event expand
32476         * Fires when a node is expanded
32477         * @param {Node} node The node
32478         */
32479         "expand" : true,
32480         /**
32481         * @event disabledchange
32482         * Fires when the disabled status of a node changes
32483         * @param {Node} node The node
32484         * @param {Boolean} disabled
32485         */
32486         "disabledchange" : true,
32487         /**
32488         * @event collapse
32489         * Fires when a node is collapsed
32490         * @param {Node} node The node
32491         */
32492         "collapse" : true,
32493         /**
32494         * @event beforeclick
32495         * Fires before click processing on a node. Return false to cancel the default action.
32496         * @param {Node} node The node
32497         * @param {Roo.EventObject} e The event object
32498         */
32499         "beforeclick":true,
32500         /**
32501         * @event checkchange
32502         * Fires when a node with a checkbox's checked property changes
32503         * @param {Node} this This node
32504         * @param {Boolean} checked
32505         */
32506         "checkchange":true,
32507         /**
32508         * @event click
32509         * Fires when a node is clicked
32510         * @param {Node} node The node
32511         * @param {Roo.EventObject} e The event object
32512         */
32513         "click":true,
32514         /**
32515         * @event dblclick
32516         * Fires when a node is double clicked
32517         * @param {Node} node The node
32518         * @param {Roo.EventObject} e The event object
32519         */
32520         "dblclick":true,
32521         /**
32522         * @event contextmenu
32523         * Fires when a node is right clicked
32524         * @param {Node} node The node
32525         * @param {Roo.EventObject} e The event object
32526         */
32527         "contextmenu":true,
32528         /**
32529         * @event beforechildrenrendered
32530         * Fires right before the child nodes for a node are rendered
32531         * @param {Node} node The node
32532         */
32533         "beforechildrenrendered":true,
32534         /**
32535         * @event startdrag
32536         * Fires when a node starts being dragged
32537         * @param {Roo.tree.TreePanel} this
32538         * @param {Roo.tree.TreeNode} node
32539         * @param {event} e The raw browser event
32540         */ 
32541        "startdrag" : true,
32542        /**
32543         * @event enddrag
32544         * Fires when a drag operation is complete
32545         * @param {Roo.tree.TreePanel} this
32546         * @param {Roo.tree.TreeNode} node
32547         * @param {event} e The raw browser event
32548         */
32549        "enddrag" : true,
32550        /**
32551         * @event dragdrop
32552         * Fires when a dragged node is dropped on a valid DD target
32553         * @param {Roo.tree.TreePanel} this
32554         * @param {Roo.tree.TreeNode} node
32555         * @param {DD} dd The dd it was dropped on
32556         * @param {event} e The raw browser event
32557         */
32558        "dragdrop" : true,
32559        /**
32560         * @event beforenodedrop
32561         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32562         * passed to handlers has the following properties:<br />
32563         * <ul style="padding:5px;padding-left:16px;">
32564         * <li>tree - The TreePanel</li>
32565         * <li>target - The node being targeted for the drop</li>
32566         * <li>data - The drag data from the drag source</li>
32567         * <li>point - The point of the drop - append, above or below</li>
32568         * <li>source - The drag source</li>
32569         * <li>rawEvent - Raw mouse event</li>
32570         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32571         * to be inserted by setting them on this object.</li>
32572         * <li>cancel - Set this to true to cancel the drop.</li>
32573         * </ul>
32574         * @param {Object} dropEvent
32575         */
32576        "beforenodedrop" : true,
32577        /**
32578         * @event nodedrop
32579         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32580         * passed to handlers has the following properties:<br />
32581         * <ul style="padding:5px;padding-left:16px;">
32582         * <li>tree - The TreePanel</li>
32583         * <li>target - The node being targeted for the drop</li>
32584         * <li>data - The drag data from the drag source</li>
32585         * <li>point - The point of the drop - append, above or below</li>
32586         * <li>source - The drag source</li>
32587         * <li>rawEvent - Raw mouse event</li>
32588         * <li>dropNode - Dropped node(s).</li>
32589         * </ul>
32590         * @param {Object} dropEvent
32591         */
32592        "nodedrop" : true,
32593         /**
32594         * @event nodedragover
32595         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32596         * passed to handlers has the following properties:<br />
32597         * <ul style="padding:5px;padding-left:16px;">
32598         * <li>tree - The TreePanel</li>
32599         * <li>target - The node being targeted for the drop</li>
32600         * <li>data - The drag data from the drag source</li>
32601         * <li>point - The point of the drop - append, above or below</li>
32602         * <li>source - The drag source</li>
32603         * <li>rawEvent - Raw mouse event</li>
32604         * <li>dropNode - Drop node(s) provided by the source.</li>
32605         * <li>cancel - Set this to true to signal drop not allowed.</li>
32606         * </ul>
32607         * @param {Object} dragOverEvent
32608         */
32609        "nodedragover" : true
32610         
32611     });
32612     if(this.singleExpand){
32613        this.on("beforeexpand", this.restrictExpand, this);
32614     }
32615     if (this.editor) {
32616         this.editor.tree = this;
32617         this.editor = Roo.factory(this.editor, Roo.tree);
32618     }
32619     
32620     if (this.selModel) {
32621         this.selModel = Roo.factory(this.selModel, Roo.tree);
32622     }
32623    
32624 };
32625 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32626     rootVisible : true,
32627     animate: Roo.enableFx,
32628     lines : true,
32629     enableDD : false,
32630     hlDrop : Roo.enableFx,
32631   
32632     renderer: false,
32633     
32634     rendererTip: false,
32635     // private
32636     restrictExpand : function(node){
32637         var p = node.parentNode;
32638         if(p){
32639             if(p.expandedChild && p.expandedChild.parentNode == p){
32640                 p.expandedChild.collapse();
32641             }
32642             p.expandedChild = node;
32643         }
32644     },
32645
32646     // private override
32647     setRootNode : function(node){
32648         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32649         if(!this.rootVisible){
32650             node.ui = new Roo.tree.RootTreeNodeUI(node);
32651         }
32652         return node;
32653     },
32654
32655     /**
32656      * Returns the container element for this TreePanel
32657      */
32658     getEl : function(){
32659         return this.el;
32660     },
32661
32662     /**
32663      * Returns the default TreeLoader for this TreePanel
32664      */
32665     getLoader : function(){
32666         return this.loader;
32667     },
32668
32669     /**
32670      * Expand all nodes
32671      */
32672     expandAll : function(){
32673         this.root.expand(true);
32674     },
32675
32676     /**
32677      * Collapse all nodes
32678      */
32679     collapseAll : function(){
32680         this.root.collapse(true);
32681     },
32682
32683     /**
32684      * Returns the selection model used by this TreePanel
32685      */
32686     getSelectionModel : function(){
32687         if(!this.selModel){
32688             this.selModel = new Roo.tree.DefaultSelectionModel();
32689         }
32690         return this.selModel;
32691     },
32692
32693     /**
32694      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32695      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32696      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32697      * @return {Array}
32698      */
32699     getChecked : function(a, startNode){
32700         startNode = startNode || this.root;
32701         var r = [];
32702         var f = function(){
32703             if(this.attributes.checked){
32704                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32705             }
32706         }
32707         startNode.cascade(f);
32708         return r;
32709     },
32710
32711     /**
32712      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32713      * @param {String} path
32714      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32715      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32716      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32717      */
32718     expandPath : function(path, attr, callback){
32719         attr = attr || "id";
32720         var keys = path.split(this.pathSeparator);
32721         var curNode = this.root;
32722         if(curNode.attributes[attr] != keys[1]){ // invalid root
32723             if(callback){
32724                 callback(false, null);
32725             }
32726             return;
32727         }
32728         var index = 1;
32729         var f = function(){
32730             if(++index == keys.length){
32731                 if(callback){
32732                     callback(true, curNode);
32733                 }
32734                 return;
32735             }
32736             var c = curNode.findChild(attr, keys[index]);
32737             if(!c){
32738                 if(callback){
32739                     callback(false, curNode);
32740                 }
32741                 return;
32742             }
32743             curNode = c;
32744             c.expand(false, false, f);
32745         };
32746         curNode.expand(false, false, f);
32747     },
32748
32749     /**
32750      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32751      * @param {String} path
32752      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32753      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32754      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32755      */
32756     selectPath : function(path, attr, callback){
32757         attr = attr || "id";
32758         var keys = path.split(this.pathSeparator);
32759         var v = keys.pop();
32760         if(keys.length > 0){
32761             var f = function(success, node){
32762                 if(success && node){
32763                     var n = node.findChild(attr, v);
32764                     if(n){
32765                         n.select();
32766                         if(callback){
32767                             callback(true, n);
32768                         }
32769                     }else if(callback){
32770                         callback(false, n);
32771                     }
32772                 }else{
32773                     if(callback){
32774                         callback(false, n);
32775                     }
32776                 }
32777             };
32778             this.expandPath(keys.join(this.pathSeparator), attr, f);
32779         }else{
32780             this.root.select();
32781             if(callback){
32782                 callback(true, this.root);
32783             }
32784         }
32785     },
32786
32787     getTreeEl : function(){
32788         return this.el;
32789     },
32790
32791     /**
32792      * Trigger rendering of this TreePanel
32793      */
32794     render : function(){
32795         if (this.innerCt) {
32796             return this; // stop it rendering more than once!!
32797         }
32798         
32799         this.innerCt = this.el.createChild({tag:"ul",
32800                cls:"x-tree-root-ct " +
32801                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32802
32803         if(this.containerScroll){
32804             Roo.dd.ScrollManager.register(this.el);
32805         }
32806         if((this.enableDD || this.enableDrop) && !this.dropZone){
32807            /**
32808             * The dropZone used by this tree if drop is enabled
32809             * @type Roo.tree.TreeDropZone
32810             */
32811              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32812                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32813            });
32814         }
32815         if((this.enableDD || this.enableDrag) && !this.dragZone){
32816            /**
32817             * The dragZone used by this tree if drag is enabled
32818             * @type Roo.tree.TreeDragZone
32819             */
32820             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32821                ddGroup: this.ddGroup || "TreeDD",
32822                scroll: this.ddScroll
32823            });
32824         }
32825         this.getSelectionModel().init(this);
32826         if (!this.root) {
32827             Roo.log("ROOT not set in tree");
32828             return this;
32829         }
32830         this.root.render();
32831         if(!this.rootVisible){
32832             this.root.renderChildren();
32833         }
32834         return this;
32835     }
32836 });/*
32837  * Based on:
32838  * Ext JS Library 1.1.1
32839  * Copyright(c) 2006-2007, Ext JS, LLC.
32840  *
32841  * Originally Released Under LGPL - original licence link has changed is not relivant.
32842  *
32843  * Fork - LGPL
32844  * <script type="text/javascript">
32845  */
32846  
32847
32848 /**
32849  * @class Roo.tree.DefaultSelectionModel
32850  * @extends Roo.util.Observable
32851  * The default single selection for a TreePanel.
32852  * @param {Object} cfg Configuration
32853  */
32854 Roo.tree.DefaultSelectionModel = function(cfg){
32855    this.selNode = null;
32856    
32857    
32858    
32859    this.addEvents({
32860        /**
32861         * @event selectionchange
32862         * Fires when the selected node changes
32863         * @param {DefaultSelectionModel} this
32864         * @param {TreeNode} node the new selection
32865         */
32866        "selectionchange" : true,
32867
32868        /**
32869         * @event beforeselect
32870         * Fires before the selected node changes, return false to cancel the change
32871         * @param {DefaultSelectionModel} this
32872         * @param {TreeNode} node the new selection
32873         * @param {TreeNode} node the old selection
32874         */
32875        "beforeselect" : true
32876    });
32877    
32878     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32879 };
32880
32881 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32882     init : function(tree){
32883         this.tree = tree;
32884         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32885         tree.on("click", this.onNodeClick, this);
32886     },
32887     
32888     onNodeClick : function(node, e){
32889         if (e.ctrlKey && this.selNode == node)  {
32890             this.unselect(node);
32891             return;
32892         }
32893         this.select(node);
32894     },
32895     
32896     /**
32897      * Select a node.
32898      * @param {TreeNode} node The node to select
32899      * @return {TreeNode} The selected node
32900      */
32901     select : function(node){
32902         var last = this.selNode;
32903         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32904             if(last){
32905                 last.ui.onSelectedChange(false);
32906             }
32907             this.selNode = node;
32908             node.ui.onSelectedChange(true);
32909             this.fireEvent("selectionchange", this, node, last);
32910         }
32911         return node;
32912     },
32913     
32914     /**
32915      * Deselect a node.
32916      * @param {TreeNode} node The node to unselect
32917      */
32918     unselect : function(node){
32919         if(this.selNode == node){
32920             this.clearSelections();
32921         }    
32922     },
32923     
32924     /**
32925      * Clear all selections
32926      */
32927     clearSelections : function(){
32928         var n = this.selNode;
32929         if(n){
32930             n.ui.onSelectedChange(false);
32931             this.selNode = null;
32932             this.fireEvent("selectionchange", this, null);
32933         }
32934         return n;
32935     },
32936     
32937     /**
32938      * Get the selected node
32939      * @return {TreeNode} The selected node
32940      */
32941     getSelectedNode : function(){
32942         return this.selNode;    
32943     },
32944     
32945     /**
32946      * Returns true if the node is selected
32947      * @param {TreeNode} node The node to check
32948      * @return {Boolean}
32949      */
32950     isSelected : function(node){
32951         return this.selNode == node;  
32952     },
32953
32954     /**
32955      * Selects the node above the selected node in the tree, intelligently walking the nodes
32956      * @return TreeNode The new selection
32957      */
32958     selectPrevious : function(){
32959         var s = this.selNode || this.lastSelNode;
32960         if(!s){
32961             return null;
32962         }
32963         var ps = s.previousSibling;
32964         if(ps){
32965             if(!ps.isExpanded() || ps.childNodes.length < 1){
32966                 return this.select(ps);
32967             } else{
32968                 var lc = ps.lastChild;
32969                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32970                     lc = lc.lastChild;
32971                 }
32972                 return this.select(lc);
32973             }
32974         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32975             return this.select(s.parentNode);
32976         }
32977         return null;
32978     },
32979
32980     /**
32981      * Selects the node above the selected node in the tree, intelligently walking the nodes
32982      * @return TreeNode The new selection
32983      */
32984     selectNext : function(){
32985         var s = this.selNode || this.lastSelNode;
32986         if(!s){
32987             return null;
32988         }
32989         if(s.firstChild && s.isExpanded()){
32990              return this.select(s.firstChild);
32991          }else if(s.nextSibling){
32992              return this.select(s.nextSibling);
32993          }else if(s.parentNode){
32994             var newS = null;
32995             s.parentNode.bubble(function(){
32996                 if(this.nextSibling){
32997                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32998                     return false;
32999                 }
33000             });
33001             return newS;
33002          }
33003         return null;
33004     },
33005
33006     onKeyDown : function(e){
33007         var s = this.selNode || this.lastSelNode;
33008         // undesirable, but required
33009         var sm = this;
33010         if(!s){
33011             return;
33012         }
33013         var k = e.getKey();
33014         switch(k){
33015              case e.DOWN:
33016                  e.stopEvent();
33017                  this.selectNext();
33018              break;
33019              case e.UP:
33020                  e.stopEvent();
33021                  this.selectPrevious();
33022              break;
33023              case e.RIGHT:
33024                  e.preventDefault();
33025                  if(s.hasChildNodes()){
33026                      if(!s.isExpanded()){
33027                          s.expand();
33028                      }else if(s.firstChild){
33029                          this.select(s.firstChild, e);
33030                      }
33031                  }
33032              break;
33033              case e.LEFT:
33034                  e.preventDefault();
33035                  if(s.hasChildNodes() && s.isExpanded()){
33036                      s.collapse();
33037                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33038                      this.select(s.parentNode, e);
33039                  }
33040              break;
33041         };
33042     }
33043 });
33044
33045 /**
33046  * @class Roo.tree.MultiSelectionModel
33047  * @extends Roo.util.Observable
33048  * Multi selection for a TreePanel.
33049  * @param {Object} cfg Configuration
33050  */
33051 Roo.tree.MultiSelectionModel = function(){
33052    this.selNodes = [];
33053    this.selMap = {};
33054    this.addEvents({
33055        /**
33056         * @event selectionchange
33057         * Fires when the selected nodes change
33058         * @param {MultiSelectionModel} this
33059         * @param {Array} nodes Array of the selected nodes
33060         */
33061        "selectionchange" : true
33062    });
33063    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33064    
33065 };
33066
33067 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33068     init : function(tree){
33069         this.tree = tree;
33070         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33071         tree.on("click", this.onNodeClick, this);
33072     },
33073     
33074     onNodeClick : function(node, e){
33075         this.select(node, e, e.ctrlKey);
33076     },
33077     
33078     /**
33079      * Select a node.
33080      * @param {TreeNode} node The node to select
33081      * @param {EventObject} e (optional) An event associated with the selection
33082      * @param {Boolean} keepExisting True to retain existing selections
33083      * @return {TreeNode} The selected node
33084      */
33085     select : function(node, e, keepExisting){
33086         if(keepExisting !== true){
33087             this.clearSelections(true);
33088         }
33089         if(this.isSelected(node)){
33090             this.lastSelNode = node;
33091             return node;
33092         }
33093         this.selNodes.push(node);
33094         this.selMap[node.id] = node;
33095         this.lastSelNode = node;
33096         node.ui.onSelectedChange(true);
33097         this.fireEvent("selectionchange", this, this.selNodes);
33098         return node;
33099     },
33100     
33101     /**
33102      * Deselect a node.
33103      * @param {TreeNode} node The node to unselect
33104      */
33105     unselect : function(node){
33106         if(this.selMap[node.id]){
33107             node.ui.onSelectedChange(false);
33108             var sn = this.selNodes;
33109             var index = -1;
33110             if(sn.indexOf){
33111                 index = sn.indexOf(node);
33112             }else{
33113                 for(var i = 0, len = sn.length; i < len; i++){
33114                     if(sn[i] == node){
33115                         index = i;
33116                         break;
33117                     }
33118                 }
33119             }
33120             if(index != -1){
33121                 this.selNodes.splice(index, 1);
33122             }
33123             delete this.selMap[node.id];
33124             this.fireEvent("selectionchange", this, this.selNodes);
33125         }
33126     },
33127     
33128     /**
33129      * Clear all selections
33130      */
33131     clearSelections : function(suppressEvent){
33132         var sn = this.selNodes;
33133         if(sn.length > 0){
33134             for(var i = 0, len = sn.length; i < len; i++){
33135                 sn[i].ui.onSelectedChange(false);
33136             }
33137             this.selNodes = [];
33138             this.selMap = {};
33139             if(suppressEvent !== true){
33140                 this.fireEvent("selectionchange", this, this.selNodes);
33141             }
33142         }
33143     },
33144     
33145     /**
33146      * Returns true if the node is selected
33147      * @param {TreeNode} node The node to check
33148      * @return {Boolean}
33149      */
33150     isSelected : function(node){
33151         return this.selMap[node.id] ? true : false;  
33152     },
33153     
33154     /**
33155      * Returns an array of the selected nodes
33156      * @return {Array}
33157      */
33158     getSelectedNodes : function(){
33159         return this.selNodes;    
33160     },
33161
33162     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33163
33164     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33165
33166     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33167 });/*
33168  * Based on:
33169  * Ext JS Library 1.1.1
33170  * Copyright(c) 2006-2007, Ext JS, LLC.
33171  *
33172  * Originally Released Under LGPL - original licence link has changed is not relivant.
33173  *
33174  * Fork - LGPL
33175  * <script type="text/javascript">
33176  */
33177  
33178 /**
33179  * @class Roo.tree.TreeNode
33180  * @extends Roo.data.Node
33181  * @cfg {String} text The text for this node
33182  * @cfg {Boolean} expanded true to start the node expanded
33183  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33184  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33185  * @cfg {Boolean} disabled true to start the node disabled
33186  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33187  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33188  * @cfg {String} cls A css class to be added to the node
33189  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33190  * @cfg {String} href URL of the link used for the node (defaults to #)
33191  * @cfg {String} hrefTarget target frame for the link
33192  * @cfg {String} qtip An Ext QuickTip for the node
33193  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33194  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33195  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33196  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33197  * (defaults to undefined with no checkbox rendered)
33198  * @constructor
33199  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33200  */
33201 Roo.tree.TreeNode = function(attributes){
33202     attributes = attributes || {};
33203     if(typeof attributes == "string"){
33204         attributes = {text: attributes};
33205     }
33206     this.childrenRendered = false;
33207     this.rendered = false;
33208     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33209     this.expanded = attributes.expanded === true;
33210     this.isTarget = attributes.isTarget !== false;
33211     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33212     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33213
33214     /**
33215      * Read-only. The text for this node. To change it use setText().
33216      * @type String
33217      */
33218     this.text = attributes.text;
33219     /**
33220      * True if this node is disabled.
33221      * @type Boolean
33222      */
33223     this.disabled = attributes.disabled === true;
33224
33225     this.addEvents({
33226         /**
33227         * @event textchange
33228         * Fires when the text for this node is changed
33229         * @param {Node} this This node
33230         * @param {String} text The new text
33231         * @param {String} oldText The old text
33232         */
33233         "textchange" : true,
33234         /**
33235         * @event beforeexpand
33236         * Fires before this node is expanded, return false to cancel.
33237         * @param {Node} this This node
33238         * @param {Boolean} deep
33239         * @param {Boolean} anim
33240         */
33241         "beforeexpand" : true,
33242         /**
33243         * @event beforecollapse
33244         * Fires before this node is collapsed, return false to cancel.
33245         * @param {Node} this This node
33246         * @param {Boolean} deep
33247         * @param {Boolean} anim
33248         */
33249         "beforecollapse" : true,
33250         /**
33251         * @event expand
33252         * Fires when this node is expanded
33253         * @param {Node} this This node
33254         */
33255         "expand" : true,
33256         /**
33257         * @event disabledchange
33258         * Fires when the disabled status of this node changes
33259         * @param {Node} this This node
33260         * @param {Boolean} disabled
33261         */
33262         "disabledchange" : true,
33263         /**
33264         * @event collapse
33265         * Fires when this node is collapsed
33266         * @param {Node} this This node
33267         */
33268         "collapse" : true,
33269         /**
33270         * @event beforeclick
33271         * Fires before click processing. Return false to cancel the default action.
33272         * @param {Node} this This node
33273         * @param {Roo.EventObject} e The event object
33274         */
33275         "beforeclick":true,
33276         /**
33277         * @event checkchange
33278         * Fires when a node with a checkbox's checked property changes
33279         * @param {Node} this This node
33280         * @param {Boolean} checked
33281         */
33282         "checkchange":true,
33283         /**
33284         * @event click
33285         * Fires when this node is clicked
33286         * @param {Node} this This node
33287         * @param {Roo.EventObject} e The event object
33288         */
33289         "click":true,
33290         /**
33291         * @event dblclick
33292         * Fires when this node is double clicked
33293         * @param {Node} this This node
33294         * @param {Roo.EventObject} e The event object
33295         */
33296         "dblclick":true,
33297         /**
33298         * @event contextmenu
33299         * Fires when this node is right clicked
33300         * @param {Node} this This node
33301         * @param {Roo.EventObject} e The event object
33302         */
33303         "contextmenu":true,
33304         /**
33305         * @event beforechildrenrendered
33306         * Fires right before the child nodes for this node are rendered
33307         * @param {Node} this This node
33308         */
33309         "beforechildrenrendered":true
33310     });
33311
33312     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33313
33314     /**
33315      * Read-only. The UI for this node
33316      * @type TreeNodeUI
33317      */
33318     this.ui = new uiClass(this);
33319     
33320     // finally support items[]
33321     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33322         return;
33323     }
33324     
33325     
33326     Roo.each(this.attributes.items, function(c) {
33327         this.appendChild(Roo.factory(c,Roo.Tree));
33328     }, this);
33329     delete this.attributes.items;
33330     
33331     
33332     
33333 };
33334 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33335     preventHScroll: true,
33336     /**
33337      * Returns true if this node is expanded
33338      * @return {Boolean}
33339      */
33340     isExpanded : function(){
33341         return this.expanded;
33342     },
33343
33344     /**
33345      * Returns the UI object for this node
33346      * @return {TreeNodeUI}
33347      */
33348     getUI : function(){
33349         return this.ui;
33350     },
33351
33352     // private override
33353     setFirstChild : function(node){
33354         var of = this.firstChild;
33355         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33356         if(this.childrenRendered && of && node != of){
33357             of.renderIndent(true, true);
33358         }
33359         if(this.rendered){
33360             this.renderIndent(true, true);
33361         }
33362     },
33363
33364     // private override
33365     setLastChild : function(node){
33366         var ol = this.lastChild;
33367         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33368         if(this.childrenRendered && ol && node != ol){
33369             ol.renderIndent(true, true);
33370         }
33371         if(this.rendered){
33372             this.renderIndent(true, true);
33373         }
33374     },
33375
33376     // these methods are overridden to provide lazy rendering support
33377     // private override
33378     appendChild : function()
33379     {
33380         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33381         if(node && this.childrenRendered){
33382             node.render();
33383         }
33384         this.ui.updateExpandIcon();
33385         return node;
33386     },
33387
33388     // private override
33389     removeChild : function(node){
33390         this.ownerTree.getSelectionModel().unselect(node);
33391         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33392         // if it's been rendered remove dom node
33393         if(this.childrenRendered){
33394             node.ui.remove();
33395         }
33396         if(this.childNodes.length < 1){
33397             this.collapse(false, false);
33398         }else{
33399             this.ui.updateExpandIcon();
33400         }
33401         if(!this.firstChild) {
33402             this.childrenRendered = false;
33403         }
33404         return node;
33405     },
33406
33407     // private override
33408     insertBefore : function(node, refNode){
33409         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33410         if(newNode && refNode && this.childrenRendered){
33411             node.render();
33412         }
33413         this.ui.updateExpandIcon();
33414         return newNode;
33415     },
33416
33417     /**
33418      * Sets the text for this node
33419      * @param {String} text
33420      */
33421     setText : function(text){
33422         var oldText = this.text;
33423         this.text = text;
33424         this.attributes.text = text;
33425         if(this.rendered){ // event without subscribing
33426             this.ui.onTextChange(this, text, oldText);
33427         }
33428         this.fireEvent("textchange", this, text, oldText);
33429     },
33430
33431     /**
33432      * Triggers selection of this node
33433      */
33434     select : function(){
33435         this.getOwnerTree().getSelectionModel().select(this);
33436     },
33437
33438     /**
33439      * Triggers deselection of this node
33440      */
33441     unselect : function(){
33442         this.getOwnerTree().getSelectionModel().unselect(this);
33443     },
33444
33445     /**
33446      * Returns true if this node is selected
33447      * @return {Boolean}
33448      */
33449     isSelected : function(){
33450         return this.getOwnerTree().getSelectionModel().isSelected(this);
33451     },
33452
33453     /**
33454      * Expand this node.
33455      * @param {Boolean} deep (optional) True to expand all children as well
33456      * @param {Boolean} anim (optional) false to cancel the default animation
33457      * @param {Function} callback (optional) A callback to be called when
33458      * expanding this node completes (does not wait for deep expand to complete).
33459      * Called with 1 parameter, this node.
33460      */
33461     expand : function(deep, anim, callback){
33462         if(!this.expanded){
33463             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33464                 return;
33465             }
33466             if(!this.childrenRendered){
33467                 this.renderChildren();
33468             }
33469             this.expanded = true;
33470             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33471                 this.ui.animExpand(function(){
33472                     this.fireEvent("expand", this);
33473                     if(typeof callback == "function"){
33474                         callback(this);
33475                     }
33476                     if(deep === true){
33477                         this.expandChildNodes(true);
33478                     }
33479                 }.createDelegate(this));
33480                 return;
33481             }else{
33482                 this.ui.expand();
33483                 this.fireEvent("expand", this);
33484                 if(typeof callback == "function"){
33485                     callback(this);
33486                 }
33487             }
33488         }else{
33489            if(typeof callback == "function"){
33490                callback(this);
33491            }
33492         }
33493         if(deep === true){
33494             this.expandChildNodes(true);
33495         }
33496     },
33497
33498     isHiddenRoot : function(){
33499         return this.isRoot && !this.getOwnerTree().rootVisible;
33500     },
33501
33502     /**
33503      * Collapse this node.
33504      * @param {Boolean} deep (optional) True to collapse all children as well
33505      * @param {Boolean} anim (optional) false to cancel the default animation
33506      */
33507     collapse : function(deep, anim){
33508         if(this.expanded && !this.isHiddenRoot()){
33509             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33510                 return;
33511             }
33512             this.expanded = false;
33513             if((this.getOwnerTree().animate && anim !== false) || anim){
33514                 this.ui.animCollapse(function(){
33515                     this.fireEvent("collapse", this);
33516                     if(deep === true){
33517                         this.collapseChildNodes(true);
33518                     }
33519                 }.createDelegate(this));
33520                 return;
33521             }else{
33522                 this.ui.collapse();
33523                 this.fireEvent("collapse", this);
33524             }
33525         }
33526         if(deep === true){
33527             var cs = this.childNodes;
33528             for(var i = 0, len = cs.length; i < len; i++) {
33529                 cs[i].collapse(true, false);
33530             }
33531         }
33532     },
33533
33534     // private
33535     delayedExpand : function(delay){
33536         if(!this.expandProcId){
33537             this.expandProcId = this.expand.defer(delay, this);
33538         }
33539     },
33540
33541     // private
33542     cancelExpand : function(){
33543         if(this.expandProcId){
33544             clearTimeout(this.expandProcId);
33545         }
33546         this.expandProcId = false;
33547     },
33548
33549     /**
33550      * Toggles expanded/collapsed state of the node
33551      */
33552     toggle : function(){
33553         if(this.expanded){
33554             this.collapse();
33555         }else{
33556             this.expand();
33557         }
33558     },
33559
33560     /**
33561      * Ensures all parent nodes are expanded
33562      */
33563     ensureVisible : function(callback){
33564         var tree = this.getOwnerTree();
33565         tree.expandPath(this.parentNode.getPath(), false, function(){
33566             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33567             Roo.callback(callback);
33568         }.createDelegate(this));
33569     },
33570
33571     /**
33572      * Expand all child nodes
33573      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33574      */
33575     expandChildNodes : function(deep){
33576         var cs = this.childNodes;
33577         for(var i = 0, len = cs.length; i < len; i++) {
33578                 cs[i].expand(deep);
33579         }
33580     },
33581
33582     /**
33583      * Collapse all child nodes
33584      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33585      */
33586     collapseChildNodes : function(deep){
33587         var cs = this.childNodes;
33588         for(var i = 0, len = cs.length; i < len; i++) {
33589                 cs[i].collapse(deep);
33590         }
33591     },
33592
33593     /**
33594      * Disables this node
33595      */
33596     disable : function(){
33597         this.disabled = true;
33598         this.unselect();
33599         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33600             this.ui.onDisableChange(this, true);
33601         }
33602         this.fireEvent("disabledchange", this, true);
33603     },
33604
33605     /**
33606      * Enables this node
33607      */
33608     enable : function(){
33609         this.disabled = false;
33610         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33611             this.ui.onDisableChange(this, false);
33612         }
33613         this.fireEvent("disabledchange", this, false);
33614     },
33615
33616     // private
33617     renderChildren : function(suppressEvent){
33618         if(suppressEvent !== false){
33619             this.fireEvent("beforechildrenrendered", this);
33620         }
33621         var cs = this.childNodes;
33622         for(var i = 0, len = cs.length; i < len; i++){
33623             cs[i].render(true);
33624         }
33625         this.childrenRendered = true;
33626     },
33627
33628     // private
33629     sort : function(fn, scope){
33630         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33631         if(this.childrenRendered){
33632             var cs = this.childNodes;
33633             for(var i = 0, len = cs.length; i < len; i++){
33634                 cs[i].render(true);
33635             }
33636         }
33637     },
33638
33639     // private
33640     render : function(bulkRender){
33641         this.ui.render(bulkRender);
33642         if(!this.rendered){
33643             this.rendered = true;
33644             if(this.expanded){
33645                 this.expanded = false;
33646                 this.expand(false, false);
33647             }
33648         }
33649     },
33650
33651     // private
33652     renderIndent : function(deep, refresh){
33653         if(refresh){
33654             this.ui.childIndent = null;
33655         }
33656         this.ui.renderIndent();
33657         if(deep === true && this.childrenRendered){
33658             var cs = this.childNodes;
33659             for(var i = 0, len = cs.length; i < len; i++){
33660                 cs[i].renderIndent(true, refresh);
33661             }
33662         }
33663     }
33664 });/*
33665  * Based on:
33666  * Ext JS Library 1.1.1
33667  * Copyright(c) 2006-2007, Ext JS, LLC.
33668  *
33669  * Originally Released Under LGPL - original licence link has changed is not relivant.
33670  *
33671  * Fork - LGPL
33672  * <script type="text/javascript">
33673  */
33674  
33675 /**
33676  * @class Roo.tree.AsyncTreeNode
33677  * @extends Roo.tree.TreeNode
33678  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33679  * @constructor
33680  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33681  */
33682  Roo.tree.AsyncTreeNode = function(config){
33683     this.loaded = false;
33684     this.loading = false;
33685     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33686     /**
33687     * @event beforeload
33688     * Fires before this node is loaded, return false to cancel
33689     * @param {Node} this This node
33690     */
33691     this.addEvents({'beforeload':true, 'load': true});
33692     /**
33693     * @event load
33694     * Fires when this node is loaded
33695     * @param {Node} this This node
33696     */
33697     /**
33698      * The loader used by this node (defaults to using the tree's defined loader)
33699      * @type TreeLoader
33700      * @property loader
33701      */
33702 };
33703 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33704     expand : function(deep, anim, callback){
33705         if(this.loading){ // if an async load is already running, waiting til it's done
33706             var timer;
33707             var f = function(){
33708                 if(!this.loading){ // done loading
33709                     clearInterval(timer);
33710                     this.expand(deep, anim, callback);
33711                 }
33712             }.createDelegate(this);
33713             timer = setInterval(f, 200);
33714             return;
33715         }
33716         if(!this.loaded){
33717             if(this.fireEvent("beforeload", this) === false){
33718                 return;
33719             }
33720             this.loading = true;
33721             this.ui.beforeLoad(this);
33722             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33723             if(loader){
33724                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33725                 return;
33726             }
33727         }
33728         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33729     },
33730     
33731     /**
33732      * Returns true if this node is currently loading
33733      * @return {Boolean}
33734      */
33735     isLoading : function(){
33736         return this.loading;  
33737     },
33738     
33739     loadComplete : function(deep, anim, callback){
33740         this.loading = false;
33741         this.loaded = true;
33742         this.ui.afterLoad(this);
33743         this.fireEvent("load", this);
33744         this.expand(deep, anim, callback);
33745     },
33746     
33747     /**
33748      * Returns true if this node has been loaded
33749      * @return {Boolean}
33750      */
33751     isLoaded : function(){
33752         return this.loaded;
33753     },
33754     
33755     hasChildNodes : function(){
33756         if(!this.isLeaf() && !this.loaded){
33757             return true;
33758         }else{
33759             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33760         }
33761     },
33762
33763     /**
33764      * Trigger a reload for this node
33765      * @param {Function} callback
33766      */
33767     reload : function(callback){
33768         this.collapse(false, false);
33769         while(this.firstChild){
33770             this.removeChild(this.firstChild);
33771         }
33772         this.childrenRendered = false;
33773         this.loaded = false;
33774         if(this.isHiddenRoot()){
33775             this.expanded = false;
33776         }
33777         this.expand(false, false, callback);
33778     }
33779 });/*
33780  * Based on:
33781  * Ext JS Library 1.1.1
33782  * Copyright(c) 2006-2007, Ext JS, LLC.
33783  *
33784  * Originally Released Under LGPL - original licence link has changed is not relivant.
33785  *
33786  * Fork - LGPL
33787  * <script type="text/javascript">
33788  */
33789  
33790 /**
33791  * @class Roo.tree.TreeNodeUI
33792  * @constructor
33793  * @param {Object} node The node to render
33794  * The TreeNode UI implementation is separate from the
33795  * tree implementation. Unless you are customizing the tree UI,
33796  * you should never have to use this directly.
33797  */
33798 Roo.tree.TreeNodeUI = function(node){
33799     this.node = node;
33800     this.rendered = false;
33801     this.animating = false;
33802     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33803 };
33804
33805 Roo.tree.TreeNodeUI.prototype = {
33806     removeChild : function(node){
33807         if(this.rendered){
33808             this.ctNode.removeChild(node.ui.getEl());
33809         }
33810     },
33811
33812     beforeLoad : function(){
33813          this.addClass("x-tree-node-loading");
33814     },
33815
33816     afterLoad : function(){
33817          this.removeClass("x-tree-node-loading");
33818     },
33819
33820     onTextChange : function(node, text, oldText){
33821         if(this.rendered){
33822             this.textNode.innerHTML = text;
33823         }
33824     },
33825
33826     onDisableChange : function(node, state){
33827         this.disabled = state;
33828         if(state){
33829             this.addClass("x-tree-node-disabled");
33830         }else{
33831             this.removeClass("x-tree-node-disabled");
33832         }
33833     },
33834
33835     onSelectedChange : function(state){
33836         if(state){
33837             this.focus();
33838             this.addClass("x-tree-selected");
33839         }else{
33840             //this.blur();
33841             this.removeClass("x-tree-selected");
33842         }
33843     },
33844
33845     onMove : function(tree, node, oldParent, newParent, index, refNode){
33846         this.childIndent = null;
33847         if(this.rendered){
33848             var targetNode = newParent.ui.getContainer();
33849             if(!targetNode){//target not rendered
33850                 this.holder = document.createElement("div");
33851                 this.holder.appendChild(this.wrap);
33852                 return;
33853             }
33854             var insertBefore = refNode ? refNode.ui.getEl() : null;
33855             if(insertBefore){
33856                 targetNode.insertBefore(this.wrap, insertBefore);
33857             }else{
33858                 targetNode.appendChild(this.wrap);
33859             }
33860             this.node.renderIndent(true);
33861         }
33862     },
33863
33864     addClass : function(cls){
33865         if(this.elNode){
33866             Roo.fly(this.elNode).addClass(cls);
33867         }
33868     },
33869
33870     removeClass : function(cls){
33871         if(this.elNode){
33872             Roo.fly(this.elNode).removeClass(cls);
33873         }
33874     },
33875
33876     remove : function(){
33877         if(this.rendered){
33878             this.holder = document.createElement("div");
33879             this.holder.appendChild(this.wrap);
33880         }
33881     },
33882
33883     fireEvent : function(){
33884         return this.node.fireEvent.apply(this.node, arguments);
33885     },
33886
33887     initEvents : function(){
33888         this.node.on("move", this.onMove, this);
33889         var E = Roo.EventManager;
33890         var a = this.anchor;
33891
33892         var el = Roo.fly(a, '_treeui');
33893
33894         if(Roo.isOpera){ // opera render bug ignores the CSS
33895             el.setStyle("text-decoration", "none");
33896         }
33897
33898         el.on("click", this.onClick, this);
33899         el.on("dblclick", this.onDblClick, this);
33900
33901         if(this.checkbox){
33902             Roo.EventManager.on(this.checkbox,
33903                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33904         }
33905
33906         el.on("contextmenu", this.onContextMenu, this);
33907
33908         var icon = Roo.fly(this.iconNode);
33909         icon.on("click", this.onClick, this);
33910         icon.on("dblclick", this.onDblClick, this);
33911         icon.on("contextmenu", this.onContextMenu, this);
33912         E.on(this.ecNode, "click", this.ecClick, this, true);
33913
33914         if(this.node.disabled){
33915             this.addClass("x-tree-node-disabled");
33916         }
33917         if(this.node.hidden){
33918             this.addClass("x-tree-node-disabled");
33919         }
33920         var ot = this.node.getOwnerTree();
33921         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33922         if(dd && (!this.node.isRoot || ot.rootVisible)){
33923             Roo.dd.Registry.register(this.elNode, {
33924                 node: this.node,
33925                 handles: this.getDDHandles(),
33926                 isHandle: false
33927             });
33928         }
33929     },
33930
33931     getDDHandles : function(){
33932         return [this.iconNode, this.textNode];
33933     },
33934
33935     hide : function(){
33936         if(this.rendered){
33937             this.wrap.style.display = "none";
33938         }
33939     },
33940
33941     show : function(){
33942         if(this.rendered){
33943             this.wrap.style.display = "";
33944         }
33945     },
33946
33947     onContextMenu : function(e){
33948         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33949             e.preventDefault();
33950             this.focus();
33951             this.fireEvent("contextmenu", this.node, e);
33952         }
33953     },
33954
33955     onClick : function(e){
33956         if(this.dropping){
33957             e.stopEvent();
33958             return;
33959         }
33960         if(this.fireEvent("beforeclick", this.node, e) !== false){
33961             if(!this.disabled && this.node.attributes.href){
33962                 this.fireEvent("click", this.node, e);
33963                 return;
33964             }
33965             e.preventDefault();
33966             if(this.disabled){
33967                 return;
33968             }
33969
33970             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33971                 this.node.toggle();
33972             }
33973
33974             this.fireEvent("click", this.node, e);
33975         }else{
33976             e.stopEvent();
33977         }
33978     },
33979
33980     onDblClick : function(e){
33981         e.preventDefault();
33982         if(this.disabled){
33983             return;
33984         }
33985         if(this.checkbox){
33986             this.toggleCheck();
33987         }
33988         if(!this.animating && this.node.hasChildNodes()){
33989             this.node.toggle();
33990         }
33991         this.fireEvent("dblclick", this.node, e);
33992     },
33993
33994     onCheckChange : function(){
33995         var checked = this.checkbox.checked;
33996         this.node.attributes.checked = checked;
33997         this.fireEvent('checkchange', this.node, checked);
33998     },
33999
34000     ecClick : function(e){
34001         if(!this.animating && this.node.hasChildNodes()){
34002             this.node.toggle();
34003         }
34004     },
34005
34006     startDrop : function(){
34007         this.dropping = true;
34008     },
34009
34010     // delayed drop so the click event doesn't get fired on a drop
34011     endDrop : function(){
34012        setTimeout(function(){
34013            this.dropping = false;
34014        }.createDelegate(this), 50);
34015     },
34016
34017     expand : function(){
34018         this.updateExpandIcon();
34019         this.ctNode.style.display = "";
34020     },
34021
34022     focus : function(){
34023         if(!this.node.preventHScroll){
34024             try{this.anchor.focus();
34025             }catch(e){}
34026         }else if(!Roo.isIE){
34027             try{
34028                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34029                 var l = noscroll.scrollLeft;
34030                 this.anchor.focus();
34031                 noscroll.scrollLeft = l;
34032             }catch(e){}
34033         }
34034     },
34035
34036     toggleCheck : function(value){
34037         var cb = this.checkbox;
34038         if(cb){
34039             cb.checked = (value === undefined ? !cb.checked : value);
34040         }
34041     },
34042
34043     blur : function(){
34044         try{
34045             this.anchor.blur();
34046         }catch(e){}
34047     },
34048
34049     animExpand : function(callback){
34050         var ct = Roo.get(this.ctNode);
34051         ct.stopFx();
34052         if(!this.node.hasChildNodes()){
34053             this.updateExpandIcon();
34054             this.ctNode.style.display = "";
34055             Roo.callback(callback);
34056             return;
34057         }
34058         this.animating = true;
34059         this.updateExpandIcon();
34060
34061         ct.slideIn('t', {
34062            callback : function(){
34063                this.animating = false;
34064                Roo.callback(callback);
34065             },
34066             scope: this,
34067             duration: this.node.ownerTree.duration || .25
34068         });
34069     },
34070
34071     highlight : function(){
34072         var tree = this.node.getOwnerTree();
34073         Roo.fly(this.wrap).highlight(
34074             tree.hlColor || "C3DAF9",
34075             {endColor: tree.hlBaseColor}
34076         );
34077     },
34078
34079     collapse : function(){
34080         this.updateExpandIcon();
34081         this.ctNode.style.display = "none";
34082     },
34083
34084     animCollapse : function(callback){
34085         var ct = Roo.get(this.ctNode);
34086         ct.enableDisplayMode('block');
34087         ct.stopFx();
34088
34089         this.animating = true;
34090         this.updateExpandIcon();
34091
34092         ct.slideOut('t', {
34093             callback : function(){
34094                this.animating = false;
34095                Roo.callback(callback);
34096             },
34097             scope: this,
34098             duration: this.node.ownerTree.duration || .25
34099         });
34100     },
34101
34102     getContainer : function(){
34103         return this.ctNode;
34104     },
34105
34106     getEl : function(){
34107         return this.wrap;
34108     },
34109
34110     appendDDGhost : function(ghostNode){
34111         ghostNode.appendChild(this.elNode.cloneNode(true));
34112     },
34113
34114     getDDRepairXY : function(){
34115         return Roo.lib.Dom.getXY(this.iconNode);
34116     },
34117
34118     onRender : function(){
34119         this.render();
34120     },
34121
34122     render : function(bulkRender){
34123         var n = this.node, a = n.attributes;
34124         var targetNode = n.parentNode ?
34125               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34126
34127         if(!this.rendered){
34128             this.rendered = true;
34129
34130             this.renderElements(n, a, targetNode, bulkRender);
34131
34132             if(a.qtip){
34133                if(this.textNode.setAttributeNS){
34134                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34135                    if(a.qtipTitle){
34136                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34137                    }
34138                }else{
34139                    this.textNode.setAttribute("ext:qtip", a.qtip);
34140                    if(a.qtipTitle){
34141                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34142                    }
34143                }
34144             }else if(a.qtipCfg){
34145                 a.qtipCfg.target = Roo.id(this.textNode);
34146                 Roo.QuickTips.register(a.qtipCfg);
34147             }
34148             this.initEvents();
34149             if(!this.node.expanded){
34150                 this.updateExpandIcon();
34151             }
34152         }else{
34153             if(bulkRender === true) {
34154                 targetNode.appendChild(this.wrap);
34155             }
34156         }
34157     },
34158
34159     renderElements : function(n, a, targetNode, bulkRender)
34160     {
34161         // add some indent caching, this helps performance when rendering a large tree
34162         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34163         var t = n.getOwnerTree();
34164         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34165         if (typeof(n.attributes.html) != 'undefined') {
34166             txt = n.attributes.html;
34167         }
34168         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34169         var cb = typeof a.checked == 'boolean';
34170         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34171         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34172             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34173             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34174             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34175             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34176             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34177              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34178                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34179             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34180             "</li>"];
34181
34182         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34183             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34184                                 n.nextSibling.ui.getEl(), buf.join(""));
34185         }else{
34186             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34187         }
34188
34189         this.elNode = this.wrap.childNodes[0];
34190         this.ctNode = this.wrap.childNodes[1];
34191         var cs = this.elNode.childNodes;
34192         this.indentNode = cs[0];
34193         this.ecNode = cs[1];
34194         this.iconNode = cs[2];
34195         var index = 3;
34196         if(cb){
34197             this.checkbox = cs[3];
34198             index++;
34199         }
34200         this.anchor = cs[index];
34201         this.textNode = cs[index].firstChild;
34202     },
34203
34204     getAnchor : function(){
34205         return this.anchor;
34206     },
34207
34208     getTextEl : function(){
34209         return this.textNode;
34210     },
34211
34212     getIconEl : function(){
34213         return this.iconNode;
34214     },
34215
34216     isChecked : function(){
34217         return this.checkbox ? this.checkbox.checked : false;
34218     },
34219
34220     updateExpandIcon : function(){
34221         if(this.rendered){
34222             var n = this.node, c1, c2;
34223             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34224             var hasChild = n.hasChildNodes();
34225             if(hasChild){
34226                 if(n.expanded){
34227                     cls += "-minus";
34228                     c1 = "x-tree-node-collapsed";
34229                     c2 = "x-tree-node-expanded";
34230                 }else{
34231                     cls += "-plus";
34232                     c1 = "x-tree-node-expanded";
34233                     c2 = "x-tree-node-collapsed";
34234                 }
34235                 if(this.wasLeaf){
34236                     this.removeClass("x-tree-node-leaf");
34237                     this.wasLeaf = false;
34238                 }
34239                 if(this.c1 != c1 || this.c2 != c2){
34240                     Roo.fly(this.elNode).replaceClass(c1, c2);
34241                     this.c1 = c1; this.c2 = c2;
34242                 }
34243             }else{
34244                 // this changes non-leafs into leafs if they have no children.
34245                 // it's not very rational behaviour..
34246                 
34247                 if(!this.wasLeaf && this.node.leaf){
34248                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34249                     delete this.c1;
34250                     delete this.c2;
34251                     this.wasLeaf = true;
34252                 }
34253             }
34254             var ecc = "x-tree-ec-icon "+cls;
34255             if(this.ecc != ecc){
34256                 this.ecNode.className = ecc;
34257                 this.ecc = ecc;
34258             }
34259         }
34260     },
34261
34262     getChildIndent : function(){
34263         if(!this.childIndent){
34264             var buf = [];
34265             var p = this.node;
34266             while(p){
34267                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34268                     if(!p.isLast()) {
34269                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34270                     } else {
34271                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34272                     }
34273                 }
34274                 p = p.parentNode;
34275             }
34276             this.childIndent = buf.join("");
34277         }
34278         return this.childIndent;
34279     },
34280
34281     renderIndent : function(){
34282         if(this.rendered){
34283             var indent = "";
34284             var p = this.node.parentNode;
34285             if(p){
34286                 indent = p.ui.getChildIndent();
34287             }
34288             if(this.indentMarkup != indent){ // don't rerender if not required
34289                 this.indentNode.innerHTML = indent;
34290                 this.indentMarkup = indent;
34291             }
34292             this.updateExpandIcon();
34293         }
34294     }
34295 };
34296
34297 Roo.tree.RootTreeNodeUI = function(){
34298     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34299 };
34300 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34301     render : function(){
34302         if(!this.rendered){
34303             var targetNode = this.node.ownerTree.innerCt.dom;
34304             this.node.expanded = true;
34305             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34306             this.wrap = this.ctNode = targetNode.firstChild;
34307         }
34308     },
34309     collapse : function(){
34310     },
34311     expand : function(){
34312     }
34313 });/*
34314  * Based on:
34315  * Ext JS Library 1.1.1
34316  * Copyright(c) 2006-2007, Ext JS, LLC.
34317  *
34318  * Originally Released Under LGPL - original licence link has changed is not relivant.
34319  *
34320  * Fork - LGPL
34321  * <script type="text/javascript">
34322  */
34323 /**
34324  * @class Roo.tree.TreeLoader
34325  * @extends Roo.util.Observable
34326  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34327  * nodes from a specified URL. The response must be a javascript Array definition
34328  * who's elements are node definition objects. eg:
34329  * <pre><code>
34330 {  success : true,
34331    data :      [
34332    
34333     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34334     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34335     ]
34336 }
34337
34338
34339 </code></pre>
34340  * <br><br>
34341  * The old style respose with just an array is still supported, but not recommended.
34342  * <br><br>
34343  *
34344  * A server request is sent, and child nodes are loaded only when a node is expanded.
34345  * The loading node's id is passed to the server under the parameter name "node" to
34346  * enable the server to produce the correct child nodes.
34347  * <br><br>
34348  * To pass extra parameters, an event handler may be attached to the "beforeload"
34349  * event, and the parameters specified in the TreeLoader's baseParams property:
34350  * <pre><code>
34351     myTreeLoader.on("beforeload", function(treeLoader, node) {
34352         this.baseParams.category = node.attributes.category;
34353     }, this);
34354 </code></pre><
34355  * This would pass an HTTP parameter called "category" to the server containing
34356  * the value of the Node's "category" attribute.
34357  * @constructor
34358  * Creates a new Treeloader.
34359  * @param {Object} config A config object containing config properties.
34360  */
34361 Roo.tree.TreeLoader = function(config){
34362     this.baseParams = {};
34363     this.requestMethod = "POST";
34364     Roo.apply(this, config);
34365
34366     this.addEvents({
34367     
34368         /**
34369          * @event beforeload
34370          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34371          * @param {Object} This TreeLoader object.
34372          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34373          * @param {Object} callback The callback function specified in the {@link #load} call.
34374          */
34375         beforeload : true,
34376         /**
34377          * @event load
34378          * Fires when the node has been successfuly loaded.
34379          * @param {Object} This TreeLoader object.
34380          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34381          * @param {Object} response The response object containing the data from the server.
34382          */
34383         load : true,
34384         /**
34385          * @event loadexception
34386          * Fires if the network request failed.
34387          * @param {Object} This TreeLoader object.
34388          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34389          * @param {Object} response The response object containing the data from the server.
34390          */
34391         loadexception : true,
34392         /**
34393          * @event create
34394          * Fires before a node is created, enabling you to return custom Node types 
34395          * @param {Object} This TreeLoader object.
34396          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34397          */
34398         create : true
34399     });
34400
34401     Roo.tree.TreeLoader.superclass.constructor.call(this);
34402 };
34403
34404 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34405     /**
34406     * @cfg {String} dataUrl The URL from which to request a Json string which
34407     * specifies an array of node definition object representing the child nodes
34408     * to be loaded.
34409     */
34410     /**
34411     * @cfg {String} requestMethod either GET or POST
34412     * defaults to POST (due to BC)
34413     * to be loaded.
34414     */
34415     /**
34416     * @cfg {Object} baseParams (optional) An object containing properties which
34417     * specify HTTP parameters to be passed to each request for child nodes.
34418     */
34419     /**
34420     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34421     * created by this loader. If the attributes sent by the server have an attribute in this object,
34422     * they take priority.
34423     */
34424     /**
34425     * @cfg {Object} uiProviders (optional) An object containing properties which
34426     * 
34427     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34428     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34429     * <i>uiProvider</i> attribute of a returned child node is a string rather
34430     * than a reference to a TreeNodeUI implementation, this that string value
34431     * is used as a property name in the uiProviders object. You can define the provider named
34432     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34433     */
34434     uiProviders : {},
34435
34436     /**
34437     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34438     * child nodes before loading.
34439     */
34440     clearOnLoad : true,
34441
34442     /**
34443     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34444     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34445     * Grid query { data : [ .....] }
34446     */
34447     
34448     root : false,
34449      /**
34450     * @cfg {String} queryParam (optional) 
34451     * Name of the query as it will be passed on the querystring (defaults to 'node')
34452     * eg. the request will be ?node=[id]
34453     */
34454     
34455     
34456     queryParam: false,
34457     
34458     /**
34459      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34460      * This is called automatically when a node is expanded, but may be used to reload
34461      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34462      * @param {Roo.tree.TreeNode} node
34463      * @param {Function} callback
34464      */
34465     load : function(node, callback){
34466         if(this.clearOnLoad){
34467             while(node.firstChild){
34468                 node.removeChild(node.firstChild);
34469             }
34470         }
34471         if(node.attributes.children){ // preloaded json children
34472             var cs = node.attributes.children;
34473             for(var i = 0, len = cs.length; i < len; i++){
34474                 node.appendChild(this.createNode(cs[i]));
34475             }
34476             if(typeof callback == "function"){
34477                 callback();
34478             }
34479         }else if(this.dataUrl){
34480             this.requestData(node, callback);
34481         }
34482     },
34483
34484     getParams: function(node){
34485         var buf = [], bp = this.baseParams;
34486         for(var key in bp){
34487             if(typeof bp[key] != "function"){
34488                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34489             }
34490         }
34491         var n = this.queryParam === false ? 'node' : this.queryParam;
34492         buf.push(n + "=", encodeURIComponent(node.id));
34493         return buf.join("");
34494     },
34495
34496     requestData : function(node, callback){
34497         if(this.fireEvent("beforeload", this, node, callback) !== false){
34498             this.transId = Roo.Ajax.request({
34499                 method:this.requestMethod,
34500                 url: this.dataUrl||this.url,
34501                 success: this.handleResponse,
34502                 failure: this.handleFailure,
34503                 scope: this,
34504                 argument: {callback: callback, node: node},
34505                 params: this.getParams(node)
34506             });
34507         }else{
34508             // if the load is cancelled, make sure we notify
34509             // the node that we are done
34510             if(typeof callback == "function"){
34511                 callback();
34512             }
34513         }
34514     },
34515
34516     isLoading : function(){
34517         return this.transId ? true : false;
34518     },
34519
34520     abort : function(){
34521         if(this.isLoading()){
34522             Roo.Ajax.abort(this.transId);
34523         }
34524     },
34525
34526     // private
34527     createNode : function(attr)
34528     {
34529         // apply baseAttrs, nice idea Corey!
34530         if(this.baseAttrs){
34531             Roo.applyIf(attr, this.baseAttrs);
34532         }
34533         if(this.applyLoader !== false){
34534             attr.loader = this;
34535         }
34536         // uiProvider = depreciated..
34537         
34538         if(typeof(attr.uiProvider) == 'string'){
34539            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34540                 /**  eval:var:attr */ eval(attr.uiProvider);
34541         }
34542         if(typeof(this.uiProviders['default']) != 'undefined') {
34543             attr.uiProvider = this.uiProviders['default'];
34544         }
34545         
34546         this.fireEvent('create', this, attr);
34547         
34548         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34549         return(attr.leaf ?
34550                         new Roo.tree.TreeNode(attr) :
34551                         new Roo.tree.AsyncTreeNode(attr));
34552     },
34553
34554     processResponse : function(response, node, callback)
34555     {
34556         var json = response.responseText;
34557         try {
34558             
34559             var o = Roo.decode(json);
34560             
34561             if (this.root === false && typeof(o.success) != undefined) {
34562                 this.root = 'data'; // the default behaviour for list like data..
34563                 }
34564                 
34565             if (this.root !== false &&  !o.success) {
34566                 // it's a failure condition.
34567                 var a = response.argument;
34568                 this.fireEvent("loadexception", this, a.node, response);
34569                 Roo.log("Load failed - should have a handler really");
34570                 return;
34571             }
34572             
34573             
34574             
34575             if (this.root !== false) {
34576                  o = o[this.root];
34577             }
34578             
34579             for(var i = 0, len = o.length; i < len; i++){
34580                 var n = this.createNode(o[i]);
34581                 if(n){
34582                     node.appendChild(n);
34583                 }
34584             }
34585             if(typeof callback == "function"){
34586                 callback(this, node);
34587             }
34588         }catch(e){
34589             this.handleFailure(response);
34590         }
34591     },
34592
34593     handleResponse : function(response){
34594         this.transId = false;
34595         var a = response.argument;
34596         this.processResponse(response, a.node, a.callback);
34597         this.fireEvent("load", this, a.node, response);
34598     },
34599
34600     handleFailure : function(response)
34601     {
34602         // should handle failure better..
34603         this.transId = false;
34604         var a = response.argument;
34605         this.fireEvent("loadexception", this, a.node, response);
34606         if(typeof a.callback == "function"){
34607             a.callback(this, a.node);
34608         }
34609     }
34610 });/*
34611  * Based on:
34612  * Ext JS Library 1.1.1
34613  * Copyright(c) 2006-2007, Ext JS, LLC.
34614  *
34615  * Originally Released Under LGPL - original licence link has changed is not relivant.
34616  *
34617  * Fork - LGPL
34618  * <script type="text/javascript">
34619  */
34620
34621 /**
34622 * @class Roo.tree.TreeFilter
34623 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34624 * @param {TreePanel} tree
34625 * @param {Object} config (optional)
34626  */
34627 Roo.tree.TreeFilter = function(tree, config){
34628     this.tree = tree;
34629     this.filtered = {};
34630     Roo.apply(this, config);
34631 };
34632
34633 Roo.tree.TreeFilter.prototype = {
34634     clearBlank:false,
34635     reverse:false,
34636     autoClear:false,
34637     remove:false,
34638
34639      /**
34640      * Filter the data by a specific attribute.
34641      * @param {String/RegExp} value Either string that the attribute value
34642      * should start with or a RegExp to test against the attribute
34643      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34644      * @param {TreeNode} startNode (optional) The node to start the filter at.
34645      */
34646     filter : function(value, attr, startNode){
34647         attr = attr || "text";
34648         var f;
34649         if(typeof value == "string"){
34650             var vlen = value.length;
34651             // auto clear empty filter
34652             if(vlen == 0 && this.clearBlank){
34653                 this.clear();
34654                 return;
34655             }
34656             value = value.toLowerCase();
34657             f = function(n){
34658                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34659             };
34660         }else if(value.exec){ // regex?
34661             f = function(n){
34662                 return value.test(n.attributes[attr]);
34663             };
34664         }else{
34665             throw 'Illegal filter type, must be string or regex';
34666         }
34667         this.filterBy(f, null, startNode);
34668         },
34669
34670     /**
34671      * Filter by a function. The passed function will be called with each
34672      * node in the tree (or from the startNode). If the function returns true, the node is kept
34673      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34674      * @param {Function} fn The filter function
34675      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34676      */
34677     filterBy : function(fn, scope, startNode){
34678         startNode = startNode || this.tree.root;
34679         if(this.autoClear){
34680             this.clear();
34681         }
34682         var af = this.filtered, rv = this.reverse;
34683         var f = function(n){
34684             if(n == startNode){
34685                 return true;
34686             }
34687             if(af[n.id]){
34688                 return false;
34689             }
34690             var m = fn.call(scope || n, n);
34691             if(!m || rv){
34692                 af[n.id] = n;
34693                 n.ui.hide();
34694                 return false;
34695             }
34696             return true;
34697         };
34698         startNode.cascade(f);
34699         if(this.remove){
34700            for(var id in af){
34701                if(typeof id != "function"){
34702                    var n = af[id];
34703                    if(n && n.parentNode){
34704                        n.parentNode.removeChild(n);
34705                    }
34706                }
34707            }
34708         }
34709     },
34710
34711     /**
34712      * Clears the current filter. Note: with the "remove" option
34713      * set a filter cannot be cleared.
34714      */
34715     clear : function(){
34716         var t = this.tree;
34717         var af = this.filtered;
34718         for(var id in af){
34719             if(typeof id != "function"){
34720                 var n = af[id];
34721                 if(n){
34722                     n.ui.show();
34723                 }
34724             }
34725         }
34726         this.filtered = {};
34727     }
34728 };
34729 /*
34730  * Based on:
34731  * Ext JS Library 1.1.1
34732  * Copyright(c) 2006-2007, Ext JS, LLC.
34733  *
34734  * Originally Released Under LGPL - original licence link has changed is not relivant.
34735  *
34736  * Fork - LGPL
34737  * <script type="text/javascript">
34738  */
34739  
34740
34741 /**
34742  * @class Roo.tree.TreeSorter
34743  * Provides sorting of nodes in a TreePanel
34744  * 
34745  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34746  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34747  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34748  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34749  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34750  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34751  * @constructor
34752  * @param {TreePanel} tree
34753  * @param {Object} config
34754  */
34755 Roo.tree.TreeSorter = function(tree, config){
34756     Roo.apply(this, config);
34757     tree.on("beforechildrenrendered", this.doSort, this);
34758     tree.on("append", this.updateSort, this);
34759     tree.on("insert", this.updateSort, this);
34760     
34761     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34762     var p = this.property || "text";
34763     var sortType = this.sortType;
34764     var fs = this.folderSort;
34765     var cs = this.caseSensitive === true;
34766     var leafAttr = this.leafAttr || 'leaf';
34767
34768     this.sortFn = function(n1, n2){
34769         if(fs){
34770             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34771                 return 1;
34772             }
34773             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34774                 return -1;
34775             }
34776         }
34777         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34778         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34779         if(v1 < v2){
34780                         return dsc ? +1 : -1;
34781                 }else if(v1 > v2){
34782                         return dsc ? -1 : +1;
34783         }else{
34784                 return 0;
34785         }
34786     };
34787 };
34788
34789 Roo.tree.TreeSorter.prototype = {
34790     doSort : function(node){
34791         node.sort(this.sortFn);
34792     },
34793     
34794     compareNodes : function(n1, n2){
34795         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34796     },
34797     
34798     updateSort : function(tree, node){
34799         if(node.childrenRendered){
34800             this.doSort.defer(1, this, [node]);
34801         }
34802     }
34803 };/*
34804  * Based on:
34805  * Ext JS Library 1.1.1
34806  * Copyright(c) 2006-2007, Ext JS, LLC.
34807  *
34808  * Originally Released Under LGPL - original licence link has changed is not relivant.
34809  *
34810  * Fork - LGPL
34811  * <script type="text/javascript">
34812  */
34813
34814 if(Roo.dd.DropZone){
34815     
34816 Roo.tree.TreeDropZone = function(tree, config){
34817     this.allowParentInsert = false;
34818     this.allowContainerDrop = false;
34819     this.appendOnly = false;
34820     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34821     this.tree = tree;
34822     this.lastInsertClass = "x-tree-no-status";
34823     this.dragOverData = {};
34824 };
34825
34826 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34827     ddGroup : "TreeDD",
34828     scroll:  true,
34829     
34830     expandDelay : 1000,
34831     
34832     expandNode : function(node){
34833         if(node.hasChildNodes() && !node.isExpanded()){
34834             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34835         }
34836     },
34837     
34838     queueExpand : function(node){
34839         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34840     },
34841     
34842     cancelExpand : function(){
34843         if(this.expandProcId){
34844             clearTimeout(this.expandProcId);
34845             this.expandProcId = false;
34846         }
34847     },
34848     
34849     isValidDropPoint : function(n, pt, dd, e, data){
34850         if(!n || !data){ return false; }
34851         var targetNode = n.node;
34852         var dropNode = data.node;
34853         // default drop rules
34854         if(!(targetNode && targetNode.isTarget && pt)){
34855             return false;
34856         }
34857         if(pt == "append" && targetNode.allowChildren === false){
34858             return false;
34859         }
34860         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34861             return false;
34862         }
34863         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34864             return false;
34865         }
34866         // reuse the object
34867         var overEvent = this.dragOverData;
34868         overEvent.tree = this.tree;
34869         overEvent.target = targetNode;
34870         overEvent.data = data;
34871         overEvent.point = pt;
34872         overEvent.source = dd;
34873         overEvent.rawEvent = e;
34874         overEvent.dropNode = dropNode;
34875         overEvent.cancel = false;  
34876         var result = this.tree.fireEvent("nodedragover", overEvent);
34877         return overEvent.cancel === false && result !== false;
34878     },
34879     
34880     getDropPoint : function(e, n, dd)
34881     {
34882         var tn = n.node;
34883         if(tn.isRoot){
34884             return tn.allowChildren !== false ? "append" : false; // always append for root
34885         }
34886         var dragEl = n.ddel;
34887         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34888         var y = Roo.lib.Event.getPageY(e);
34889         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34890         
34891         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34892         var noAppend = tn.allowChildren === false;
34893         if(this.appendOnly || tn.parentNode.allowChildren === false){
34894             return noAppend ? false : "append";
34895         }
34896         var noBelow = false;
34897         if(!this.allowParentInsert){
34898             noBelow = tn.hasChildNodes() && tn.isExpanded();
34899         }
34900         var q = (b - t) / (noAppend ? 2 : 3);
34901         if(y >= t && y < (t + q)){
34902             return "above";
34903         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34904             return "below";
34905         }else{
34906             return "append";
34907         }
34908     },
34909     
34910     onNodeEnter : function(n, dd, e, data)
34911     {
34912         this.cancelExpand();
34913     },
34914     
34915     onNodeOver : function(n, dd, e, data)
34916     {
34917        
34918         var pt = this.getDropPoint(e, n, dd);
34919         var node = n.node;
34920         
34921         // auto node expand check
34922         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34923             this.queueExpand(node);
34924         }else if(pt != "append"){
34925             this.cancelExpand();
34926         }
34927         
34928         // set the insert point style on the target node
34929         var returnCls = this.dropNotAllowed;
34930         if(this.isValidDropPoint(n, pt, dd, e, data)){
34931            if(pt){
34932                var el = n.ddel;
34933                var cls;
34934                if(pt == "above"){
34935                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34936                    cls = "x-tree-drag-insert-above";
34937                }else if(pt == "below"){
34938                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34939                    cls = "x-tree-drag-insert-below";
34940                }else{
34941                    returnCls = "x-tree-drop-ok-append";
34942                    cls = "x-tree-drag-append";
34943                }
34944                if(this.lastInsertClass != cls){
34945                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34946                    this.lastInsertClass = cls;
34947                }
34948            }
34949        }
34950        return returnCls;
34951     },
34952     
34953     onNodeOut : function(n, dd, e, data){
34954         
34955         this.cancelExpand();
34956         this.removeDropIndicators(n);
34957     },
34958     
34959     onNodeDrop : function(n, dd, e, data){
34960         var point = this.getDropPoint(e, n, dd);
34961         var targetNode = n.node;
34962         targetNode.ui.startDrop();
34963         if(!this.isValidDropPoint(n, point, dd, e, data)){
34964             targetNode.ui.endDrop();
34965             return false;
34966         }
34967         // first try to find the drop node
34968         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34969         var dropEvent = {
34970             tree : this.tree,
34971             target: targetNode,
34972             data: data,
34973             point: point,
34974             source: dd,
34975             rawEvent: e,
34976             dropNode: dropNode,
34977             cancel: !dropNode   
34978         };
34979         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34980         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34981             targetNode.ui.endDrop();
34982             return false;
34983         }
34984         // allow target changing
34985         targetNode = dropEvent.target;
34986         if(point == "append" && !targetNode.isExpanded()){
34987             targetNode.expand(false, null, function(){
34988                 this.completeDrop(dropEvent);
34989             }.createDelegate(this));
34990         }else{
34991             this.completeDrop(dropEvent);
34992         }
34993         return true;
34994     },
34995     
34996     completeDrop : function(de){
34997         var ns = de.dropNode, p = de.point, t = de.target;
34998         if(!(ns instanceof Array)){
34999             ns = [ns];
35000         }
35001         var n;
35002         for(var i = 0, len = ns.length; i < len; i++){
35003             n = ns[i];
35004             if(p == "above"){
35005                 t.parentNode.insertBefore(n, t);
35006             }else if(p == "below"){
35007                 t.parentNode.insertBefore(n, t.nextSibling);
35008             }else{
35009                 t.appendChild(n);
35010             }
35011         }
35012         n.ui.focus();
35013         if(this.tree.hlDrop){
35014             n.ui.highlight();
35015         }
35016         t.ui.endDrop();
35017         this.tree.fireEvent("nodedrop", de);
35018     },
35019     
35020     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35021         if(this.tree.hlDrop){
35022             dropNode.ui.focus();
35023             dropNode.ui.highlight();
35024         }
35025         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35026     },
35027     
35028     getTree : function(){
35029         return this.tree;
35030     },
35031     
35032     removeDropIndicators : function(n){
35033         if(n && n.ddel){
35034             var el = n.ddel;
35035             Roo.fly(el).removeClass([
35036                     "x-tree-drag-insert-above",
35037                     "x-tree-drag-insert-below",
35038                     "x-tree-drag-append"]);
35039             this.lastInsertClass = "_noclass";
35040         }
35041     },
35042     
35043     beforeDragDrop : function(target, e, id){
35044         this.cancelExpand();
35045         return true;
35046     },
35047     
35048     afterRepair : function(data){
35049         if(data && Roo.enableFx){
35050             data.node.ui.highlight();
35051         }
35052         this.hideProxy();
35053     } 
35054     
35055 });
35056
35057 }
35058 /*
35059  * Based on:
35060  * Ext JS Library 1.1.1
35061  * Copyright(c) 2006-2007, Ext JS, LLC.
35062  *
35063  * Originally Released Under LGPL - original licence link has changed is not relivant.
35064  *
35065  * Fork - LGPL
35066  * <script type="text/javascript">
35067  */
35068  
35069
35070 if(Roo.dd.DragZone){
35071 Roo.tree.TreeDragZone = function(tree, config){
35072     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35073     this.tree = tree;
35074 };
35075
35076 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35077     ddGroup : "TreeDD",
35078    
35079     onBeforeDrag : function(data, e){
35080         var n = data.node;
35081         return n && n.draggable && !n.disabled;
35082     },
35083      
35084     
35085     onInitDrag : function(e){
35086         var data = this.dragData;
35087         this.tree.getSelectionModel().select(data.node);
35088         this.proxy.update("");
35089         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35090         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35091     },
35092     
35093     getRepairXY : function(e, data){
35094         return data.node.ui.getDDRepairXY();
35095     },
35096     
35097     onEndDrag : function(data, e){
35098         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35099         
35100         
35101     },
35102     
35103     onValidDrop : function(dd, e, id){
35104         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35105         this.hideProxy();
35106     },
35107     
35108     beforeInvalidDrop : function(e, id){
35109         // this scrolls the original position back into view
35110         var sm = this.tree.getSelectionModel();
35111         sm.clearSelections();
35112         sm.select(this.dragData.node);
35113     }
35114 });
35115 }/*
35116  * Based on:
35117  * Ext JS Library 1.1.1
35118  * Copyright(c) 2006-2007, Ext JS, LLC.
35119  *
35120  * Originally Released Under LGPL - original licence link has changed is not relivant.
35121  *
35122  * Fork - LGPL
35123  * <script type="text/javascript">
35124  */
35125 /**
35126  * @class Roo.tree.TreeEditor
35127  * @extends Roo.Editor
35128  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35129  * as the editor field.
35130  * @constructor
35131  * @param {Object} config (used to be the tree panel.)
35132  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35133  * 
35134  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35135  * @cfg {Roo.form.TextField|Object} field The field configuration
35136  *
35137  * 
35138  */
35139 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35140     var tree = config;
35141     var field;
35142     if (oldconfig) { // old style..
35143         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35144     } else {
35145         // new style..
35146         tree = config.tree;
35147         config.field = config.field  || {};
35148         config.field.xtype = 'TextField';
35149         field = Roo.factory(config.field, Roo.form);
35150     }
35151     config = config || {};
35152     
35153     
35154     this.addEvents({
35155         /**
35156          * @event beforenodeedit
35157          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35158          * false from the handler of this event.
35159          * @param {Editor} this
35160          * @param {Roo.tree.Node} node 
35161          */
35162         "beforenodeedit" : true
35163     });
35164     
35165     //Roo.log(config);
35166     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35167
35168     this.tree = tree;
35169
35170     tree.on('beforeclick', this.beforeNodeClick, this);
35171     tree.getTreeEl().on('mousedown', this.hide, this);
35172     this.on('complete', this.updateNode, this);
35173     this.on('beforestartedit', this.fitToTree, this);
35174     this.on('startedit', this.bindScroll, this, {delay:10});
35175     this.on('specialkey', this.onSpecialKey, this);
35176 };
35177
35178 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35179     /**
35180      * @cfg {String} alignment
35181      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35182      */
35183     alignment: "l-l",
35184     // inherit
35185     autoSize: false,
35186     /**
35187      * @cfg {Boolean} hideEl
35188      * True to hide the bound element while the editor is displayed (defaults to false)
35189      */
35190     hideEl : false,
35191     /**
35192      * @cfg {String} cls
35193      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35194      */
35195     cls: "x-small-editor x-tree-editor",
35196     /**
35197      * @cfg {Boolean} shim
35198      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35199      */
35200     shim:false,
35201     // inherit
35202     shadow:"frame",
35203     /**
35204      * @cfg {Number} maxWidth
35205      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35206      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35207      * scroll and client offsets into account prior to each edit.
35208      */
35209     maxWidth: 250,
35210
35211     editDelay : 350,
35212
35213     // private
35214     fitToTree : function(ed, el){
35215         var td = this.tree.getTreeEl().dom, nd = el.dom;
35216         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35217             td.scrollLeft = nd.offsetLeft;
35218         }
35219         var w = Math.min(
35220                 this.maxWidth,
35221                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35222         this.setSize(w, '');
35223         
35224         return this.fireEvent('beforenodeedit', this, this.editNode);
35225         
35226     },
35227
35228     // private
35229     triggerEdit : function(node){
35230         this.completeEdit();
35231         this.editNode = node;
35232         this.startEdit(node.ui.textNode, node.text);
35233     },
35234
35235     // private
35236     bindScroll : function(){
35237         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35238     },
35239
35240     // private
35241     beforeNodeClick : function(node, e){
35242         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35243         this.lastClick = new Date();
35244         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35245             e.stopEvent();
35246             this.triggerEdit(node);
35247             return false;
35248         }
35249         return true;
35250     },
35251
35252     // private
35253     updateNode : function(ed, value){
35254         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35255         this.editNode.setText(value);
35256     },
35257
35258     // private
35259     onHide : function(){
35260         Roo.tree.TreeEditor.superclass.onHide.call(this);
35261         if(this.editNode){
35262             this.editNode.ui.focus();
35263         }
35264     },
35265
35266     // private
35267     onSpecialKey : function(field, e){
35268         var k = e.getKey();
35269         if(k == e.ESC){
35270             e.stopEvent();
35271             this.cancelEdit();
35272         }else if(k == e.ENTER && !e.hasModifier()){
35273             e.stopEvent();
35274             this.completeEdit();
35275         }
35276     }
35277 });//<Script type="text/javascript">
35278 /*
35279  * Based on:
35280  * Ext JS Library 1.1.1
35281  * Copyright(c) 2006-2007, Ext JS, LLC.
35282  *
35283  * Originally Released Under LGPL - original licence link has changed is not relivant.
35284  *
35285  * Fork - LGPL
35286  * <script type="text/javascript">
35287  */
35288  
35289 /**
35290  * Not documented??? - probably should be...
35291  */
35292
35293 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35294     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35295     
35296     renderElements : function(n, a, targetNode, bulkRender){
35297         //consel.log("renderElements?");
35298         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35299
35300         var t = n.getOwnerTree();
35301         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35302         
35303         var cols = t.columns;
35304         var bw = t.borderWidth;
35305         var c = cols[0];
35306         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35307          var cb = typeof a.checked == "boolean";
35308         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35309         var colcls = 'x-t-' + tid + '-c0';
35310         var buf = [
35311             '<li class="x-tree-node">',
35312             
35313                 
35314                 '<div class="x-tree-node-el ', a.cls,'">',
35315                     // extran...
35316                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35317                 
35318                 
35319                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35320                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35321                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35322                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35323                            (a.iconCls ? ' '+a.iconCls : ''),
35324                            '" unselectable="on" />',
35325                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35326                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35327                              
35328                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35329                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35330                             '<span unselectable="on" qtip="' + tx + '">',
35331                              tx,
35332                              '</span></a>' ,
35333                     '</div>',
35334                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35335                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35336                  ];
35337         for(var i = 1, len = cols.length; i < len; i++){
35338             c = cols[i];
35339             colcls = 'x-t-' + tid + '-c' +i;
35340             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35341             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35342                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35343                       "</div>");
35344          }
35345          
35346          buf.push(
35347             '</a>',
35348             '<div class="x-clear"></div></div>',
35349             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35350             "</li>");
35351         
35352         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35353             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35354                                 n.nextSibling.ui.getEl(), buf.join(""));
35355         }else{
35356             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35357         }
35358         var el = this.wrap.firstChild;
35359         this.elRow = el;
35360         this.elNode = el.firstChild;
35361         this.ranchor = el.childNodes[1];
35362         this.ctNode = this.wrap.childNodes[1];
35363         var cs = el.firstChild.childNodes;
35364         this.indentNode = cs[0];
35365         this.ecNode = cs[1];
35366         this.iconNode = cs[2];
35367         var index = 3;
35368         if(cb){
35369             this.checkbox = cs[3];
35370             index++;
35371         }
35372         this.anchor = cs[index];
35373         
35374         this.textNode = cs[index].firstChild;
35375         
35376         //el.on("click", this.onClick, this);
35377         //el.on("dblclick", this.onDblClick, this);
35378         
35379         
35380        // console.log(this);
35381     },
35382     initEvents : function(){
35383         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35384         
35385             
35386         var a = this.ranchor;
35387
35388         var el = Roo.get(a);
35389
35390         if(Roo.isOpera){ // opera render bug ignores the CSS
35391             el.setStyle("text-decoration", "none");
35392         }
35393
35394         el.on("click", this.onClick, this);
35395         el.on("dblclick", this.onDblClick, this);
35396         el.on("contextmenu", this.onContextMenu, this);
35397         
35398     },
35399     
35400     /*onSelectedChange : function(state){
35401         if(state){
35402             this.focus();
35403             this.addClass("x-tree-selected");
35404         }else{
35405             //this.blur();
35406             this.removeClass("x-tree-selected");
35407         }
35408     },*/
35409     addClass : function(cls){
35410         if(this.elRow){
35411             Roo.fly(this.elRow).addClass(cls);
35412         }
35413         
35414     },
35415     
35416     
35417     removeClass : function(cls){
35418         if(this.elRow){
35419             Roo.fly(this.elRow).removeClass(cls);
35420         }
35421     }
35422
35423     
35424     
35425 });//<Script type="text/javascript">
35426
35427 /*
35428  * Based on:
35429  * Ext JS Library 1.1.1
35430  * Copyright(c) 2006-2007, Ext JS, LLC.
35431  *
35432  * Originally Released Under LGPL - original licence link has changed is not relivant.
35433  *
35434  * Fork - LGPL
35435  * <script type="text/javascript">
35436  */
35437  
35438
35439 /**
35440  * @class Roo.tree.ColumnTree
35441  * @extends Roo.data.TreePanel
35442  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35443  * @cfg {int} borderWidth  compined right/left border allowance
35444  * @constructor
35445  * @param {String/HTMLElement/Element} el The container element
35446  * @param {Object} config
35447  */
35448 Roo.tree.ColumnTree =  function(el, config)
35449 {
35450    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35451    this.addEvents({
35452         /**
35453         * @event resize
35454         * Fire this event on a container when it resizes
35455         * @param {int} w Width
35456         * @param {int} h Height
35457         */
35458        "resize" : true
35459     });
35460     this.on('resize', this.onResize, this);
35461 };
35462
35463 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35464     //lines:false,
35465     
35466     
35467     borderWidth: Roo.isBorderBox ? 0 : 2, 
35468     headEls : false,
35469     
35470     render : function(){
35471         // add the header.....
35472        
35473         Roo.tree.ColumnTree.superclass.render.apply(this);
35474         
35475         this.el.addClass('x-column-tree');
35476         
35477         this.headers = this.el.createChild(
35478             {cls:'x-tree-headers'},this.innerCt.dom);
35479    
35480         var cols = this.columns, c;
35481         var totalWidth = 0;
35482         this.headEls = [];
35483         var  len = cols.length;
35484         for(var i = 0; i < len; i++){
35485              c = cols[i];
35486              totalWidth += c.width;
35487             this.headEls.push(this.headers.createChild({
35488                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35489                  cn: {
35490                      cls:'x-tree-hd-text',
35491                      html: c.header
35492                  },
35493                  style:'width:'+(c.width-this.borderWidth)+'px;'
35494              }));
35495         }
35496         this.headers.createChild({cls:'x-clear'});
35497         // prevent floats from wrapping when clipped
35498         this.headers.setWidth(totalWidth);
35499         //this.innerCt.setWidth(totalWidth);
35500         this.innerCt.setStyle({ overflow: 'auto' });
35501         this.onResize(this.width, this.height);
35502              
35503         
35504     },
35505     onResize : function(w,h)
35506     {
35507         this.height = h;
35508         this.width = w;
35509         // resize cols..
35510         this.innerCt.setWidth(this.width);
35511         this.innerCt.setHeight(this.height-20);
35512         
35513         // headers...
35514         var cols = this.columns, c;
35515         var totalWidth = 0;
35516         var expEl = false;
35517         var len = cols.length;
35518         for(var i = 0; i < len; i++){
35519             c = cols[i];
35520             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35521                 // it's the expander..
35522                 expEl  = this.headEls[i];
35523                 continue;
35524             }
35525             totalWidth += c.width;
35526             
35527         }
35528         if (expEl) {
35529             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35530         }
35531         this.headers.setWidth(w-20);
35532
35533         
35534         
35535         
35536     }
35537 });
35538 /*
35539  * Based on:
35540  * Ext JS Library 1.1.1
35541  * Copyright(c) 2006-2007, Ext JS, LLC.
35542  *
35543  * Originally Released Under LGPL - original licence link has changed is not relivant.
35544  *
35545  * Fork - LGPL
35546  * <script type="text/javascript">
35547  */
35548  
35549 /**
35550  * @class Roo.menu.Menu
35551  * @extends Roo.util.Observable
35552  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35553  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35554  * @constructor
35555  * Creates a new Menu
35556  * @param {Object} config Configuration options
35557  */
35558 Roo.menu.Menu = function(config){
35559     Roo.apply(this, config);
35560     this.id = this.id || Roo.id();
35561     this.addEvents({
35562         /**
35563          * @event beforeshow
35564          * Fires before this menu is displayed
35565          * @param {Roo.menu.Menu} this
35566          */
35567         beforeshow : true,
35568         /**
35569          * @event beforehide
35570          * Fires before this menu is hidden
35571          * @param {Roo.menu.Menu} this
35572          */
35573         beforehide : true,
35574         /**
35575          * @event show
35576          * Fires after this menu is displayed
35577          * @param {Roo.menu.Menu} this
35578          */
35579         show : true,
35580         /**
35581          * @event hide
35582          * Fires after this menu is hidden
35583          * @param {Roo.menu.Menu} this
35584          */
35585         hide : true,
35586         /**
35587          * @event click
35588          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35589          * @param {Roo.menu.Menu} this
35590          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35591          * @param {Roo.EventObject} e
35592          */
35593         click : true,
35594         /**
35595          * @event mouseover
35596          * Fires when the mouse is hovering over this menu
35597          * @param {Roo.menu.Menu} this
35598          * @param {Roo.EventObject} e
35599          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35600          */
35601         mouseover : true,
35602         /**
35603          * @event mouseout
35604          * Fires when the mouse exits this menu
35605          * @param {Roo.menu.Menu} this
35606          * @param {Roo.EventObject} e
35607          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35608          */
35609         mouseout : true,
35610         /**
35611          * @event itemclick
35612          * Fires when a menu item contained in this menu is clicked
35613          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35614          * @param {Roo.EventObject} e
35615          */
35616         itemclick: true
35617     });
35618     if (this.registerMenu) {
35619         Roo.menu.MenuMgr.register(this);
35620     }
35621     
35622     var mis = this.items;
35623     this.items = new Roo.util.MixedCollection();
35624     if(mis){
35625         this.add.apply(this, mis);
35626     }
35627 };
35628
35629 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35630     /**
35631      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35632      */
35633     minWidth : 120,
35634     /**
35635      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35636      * for bottom-right shadow (defaults to "sides")
35637      */
35638     shadow : "sides",
35639     /**
35640      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35641      * this menu (defaults to "tl-tr?")
35642      */
35643     subMenuAlign : "tl-tr?",
35644     /**
35645      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35646      * relative to its element of origin (defaults to "tl-bl?")
35647      */
35648     defaultAlign : "tl-bl?",
35649     /**
35650      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35651      */
35652     allowOtherMenus : false,
35653     /**
35654      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35655      */
35656     registerMenu : true,
35657
35658     hidden:true,
35659
35660     // private
35661     render : function(){
35662         if(this.el){
35663             return;
35664         }
35665         var el = this.el = new Roo.Layer({
35666             cls: "x-menu",
35667             shadow:this.shadow,
35668             constrain: false,
35669             parentEl: this.parentEl || document.body,
35670             zindex:15000
35671         });
35672
35673         this.keyNav = new Roo.menu.MenuNav(this);
35674
35675         if(this.plain){
35676             el.addClass("x-menu-plain");
35677         }
35678         if(this.cls){
35679             el.addClass(this.cls);
35680         }
35681         // generic focus element
35682         this.focusEl = el.createChild({
35683             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35684         });
35685         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35686         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35687         
35688         ul.on("mouseover", this.onMouseOver, this);
35689         ul.on("mouseout", this.onMouseOut, this);
35690         this.items.each(function(item){
35691             if (item.hidden) {
35692                 return;
35693             }
35694             
35695             var li = document.createElement("li");
35696             li.className = "x-menu-list-item";
35697             ul.dom.appendChild(li);
35698             item.render(li, this);
35699         }, this);
35700         this.ul = ul;
35701         this.autoWidth();
35702     },
35703
35704     // private
35705     autoWidth : function(){
35706         var el = this.el, ul = this.ul;
35707         if(!el){
35708             return;
35709         }
35710         var w = this.width;
35711         if(w){
35712             el.setWidth(w);
35713         }else if(Roo.isIE){
35714             el.setWidth(this.minWidth);
35715             var t = el.dom.offsetWidth; // force recalc
35716             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35717         }
35718     },
35719
35720     // private
35721     delayAutoWidth : function(){
35722         if(this.rendered){
35723             if(!this.awTask){
35724                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35725             }
35726             this.awTask.delay(20);
35727         }
35728     },
35729
35730     // private
35731     findTargetItem : function(e){
35732         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35733         if(t && t.menuItemId){
35734             return this.items.get(t.menuItemId);
35735         }
35736     },
35737
35738     // private
35739     onClick : function(e){
35740         Roo.log("menu.onClick");
35741         var t = this.findTargetItem(e);
35742         if(!t){
35743             return;
35744         }
35745         Roo.log(e);
35746         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35747             if(t == this.activeItem && t.shouldDeactivate(e)){
35748                 this.activeItem.deactivate();
35749                 delete this.activeItem;
35750                 return;
35751             }
35752             if(t.canActivate){
35753                 this.setActiveItem(t, true);
35754             }
35755             return;
35756             
35757             
35758         }
35759         
35760         t.onClick(e);
35761         this.fireEvent("click", this, t, e);
35762     },
35763
35764     // private
35765     setActiveItem : function(item, autoExpand){
35766         if(item != this.activeItem){
35767             if(this.activeItem){
35768                 this.activeItem.deactivate();
35769             }
35770             this.activeItem = item;
35771             item.activate(autoExpand);
35772         }else if(autoExpand){
35773             item.expandMenu();
35774         }
35775     },
35776
35777     // private
35778     tryActivate : function(start, step){
35779         var items = this.items;
35780         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35781             var item = items.get(i);
35782             if(!item.disabled && item.canActivate){
35783                 this.setActiveItem(item, false);
35784                 return item;
35785             }
35786         }
35787         return false;
35788     },
35789
35790     // private
35791     onMouseOver : function(e){
35792         var t;
35793         if(t = this.findTargetItem(e)){
35794             if(t.canActivate && !t.disabled){
35795                 this.setActiveItem(t, true);
35796             }
35797         }
35798         this.fireEvent("mouseover", this, e, t);
35799     },
35800
35801     // private
35802     onMouseOut : function(e){
35803         var t;
35804         if(t = this.findTargetItem(e)){
35805             if(t == this.activeItem && t.shouldDeactivate(e)){
35806                 this.activeItem.deactivate();
35807                 delete this.activeItem;
35808             }
35809         }
35810         this.fireEvent("mouseout", this, e, t);
35811     },
35812
35813     /**
35814      * Read-only.  Returns true if the menu is currently displayed, else false.
35815      * @type Boolean
35816      */
35817     isVisible : function(){
35818         return this.el && !this.hidden;
35819     },
35820
35821     /**
35822      * Displays this menu relative to another element
35823      * @param {String/HTMLElement/Roo.Element} element The element to align to
35824      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35825      * the element (defaults to this.defaultAlign)
35826      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35827      */
35828     show : function(el, pos, parentMenu){
35829         this.parentMenu = parentMenu;
35830         if(!this.el){
35831             this.render();
35832         }
35833         this.fireEvent("beforeshow", this);
35834         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35835     },
35836
35837     /**
35838      * Displays this menu at a specific xy position
35839      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35840      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35841      */
35842     showAt : function(xy, parentMenu, /* private: */_e){
35843         this.parentMenu = parentMenu;
35844         if(!this.el){
35845             this.render();
35846         }
35847         if(_e !== false){
35848             this.fireEvent("beforeshow", this);
35849             xy = this.el.adjustForConstraints(xy);
35850         }
35851         this.el.setXY(xy);
35852         this.el.show();
35853         this.hidden = false;
35854         this.focus();
35855         this.fireEvent("show", this);
35856     },
35857
35858     focus : function(){
35859         if(!this.hidden){
35860             this.doFocus.defer(50, this);
35861         }
35862     },
35863
35864     doFocus : function(){
35865         if(!this.hidden){
35866             this.focusEl.focus();
35867         }
35868     },
35869
35870     /**
35871      * Hides this menu and optionally all parent menus
35872      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35873      */
35874     hide : function(deep){
35875         if(this.el && this.isVisible()){
35876             this.fireEvent("beforehide", this);
35877             if(this.activeItem){
35878                 this.activeItem.deactivate();
35879                 this.activeItem = null;
35880             }
35881             this.el.hide();
35882             this.hidden = true;
35883             this.fireEvent("hide", this);
35884         }
35885         if(deep === true && this.parentMenu){
35886             this.parentMenu.hide(true);
35887         }
35888     },
35889
35890     /**
35891      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35892      * Any of the following are valid:
35893      * <ul>
35894      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35895      * <li>An HTMLElement object which will be converted to a menu item</li>
35896      * <li>A menu item config object that will be created as a new menu item</li>
35897      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35898      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35899      * </ul>
35900      * Usage:
35901      * <pre><code>
35902 // Create the menu
35903 var menu = new Roo.menu.Menu();
35904
35905 // Create a menu item to add by reference
35906 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35907
35908 // Add a bunch of items at once using different methods.
35909 // Only the last item added will be returned.
35910 var item = menu.add(
35911     menuItem,                // add existing item by ref
35912     'Dynamic Item',          // new TextItem
35913     '-',                     // new separator
35914     { text: 'Config Item' }  // new item by config
35915 );
35916 </code></pre>
35917      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35918      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35919      */
35920     add : function(){
35921         var a = arguments, l = a.length, item;
35922         for(var i = 0; i < l; i++){
35923             var el = a[i];
35924             if ((typeof(el) == "object") && el.xtype && el.xns) {
35925                 el = Roo.factory(el, Roo.menu);
35926             }
35927             
35928             if(el.render){ // some kind of Item
35929                 item = this.addItem(el);
35930             }else if(typeof el == "string"){ // string
35931                 if(el == "separator" || el == "-"){
35932                     item = this.addSeparator();
35933                 }else{
35934                     item = this.addText(el);
35935                 }
35936             }else if(el.tagName || el.el){ // element
35937                 item = this.addElement(el);
35938             }else if(typeof el == "object"){ // must be menu item config?
35939                 item = this.addMenuItem(el);
35940             }
35941         }
35942         return item;
35943     },
35944
35945     /**
35946      * Returns this menu's underlying {@link Roo.Element} object
35947      * @return {Roo.Element} The element
35948      */
35949     getEl : function(){
35950         if(!this.el){
35951             this.render();
35952         }
35953         return this.el;
35954     },
35955
35956     /**
35957      * Adds a separator bar to the menu
35958      * @return {Roo.menu.Item} The menu item that was added
35959      */
35960     addSeparator : function(){
35961         return this.addItem(new Roo.menu.Separator());
35962     },
35963
35964     /**
35965      * Adds an {@link Roo.Element} object to the menu
35966      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35967      * @return {Roo.menu.Item} The menu item that was added
35968      */
35969     addElement : function(el){
35970         return this.addItem(new Roo.menu.BaseItem(el));
35971     },
35972
35973     /**
35974      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35975      * @param {Roo.menu.Item} item The menu item to add
35976      * @return {Roo.menu.Item} The menu item that was added
35977      */
35978     addItem : function(item){
35979         this.items.add(item);
35980         if(this.ul){
35981             var li = document.createElement("li");
35982             li.className = "x-menu-list-item";
35983             this.ul.dom.appendChild(li);
35984             item.render(li, this);
35985             this.delayAutoWidth();
35986         }
35987         return item;
35988     },
35989
35990     /**
35991      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35992      * @param {Object} config A MenuItem config object
35993      * @return {Roo.menu.Item} The menu item that was added
35994      */
35995     addMenuItem : function(config){
35996         if(!(config instanceof Roo.menu.Item)){
35997             if(typeof config.checked == "boolean"){ // must be check menu item config?
35998                 config = new Roo.menu.CheckItem(config);
35999             }else{
36000                 config = new Roo.menu.Item(config);
36001             }
36002         }
36003         return this.addItem(config);
36004     },
36005
36006     /**
36007      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36008      * @param {String} text The text to display in the menu item
36009      * @return {Roo.menu.Item} The menu item that was added
36010      */
36011     addText : function(text){
36012         return this.addItem(new Roo.menu.TextItem({ text : text }));
36013     },
36014
36015     /**
36016      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36017      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36018      * @param {Roo.menu.Item} item The menu item to add
36019      * @return {Roo.menu.Item} The menu item that was added
36020      */
36021     insert : function(index, item){
36022         this.items.insert(index, item);
36023         if(this.ul){
36024             var li = document.createElement("li");
36025             li.className = "x-menu-list-item";
36026             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36027             item.render(li, this);
36028             this.delayAutoWidth();
36029         }
36030         return item;
36031     },
36032
36033     /**
36034      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36035      * @param {Roo.menu.Item} item The menu item to remove
36036      */
36037     remove : function(item){
36038         this.items.removeKey(item.id);
36039         item.destroy();
36040     },
36041
36042     /**
36043      * Removes and destroys all items in the menu
36044      */
36045     removeAll : function(){
36046         var f;
36047         while(f = this.items.first()){
36048             this.remove(f);
36049         }
36050     }
36051 });
36052
36053 // MenuNav is a private utility class used internally by the Menu
36054 Roo.menu.MenuNav = function(menu){
36055     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36056     this.scope = this.menu = menu;
36057 };
36058
36059 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36060     doRelay : function(e, h){
36061         var k = e.getKey();
36062         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36063             this.menu.tryActivate(0, 1);
36064             return false;
36065         }
36066         return h.call(this.scope || this, e, this.menu);
36067     },
36068
36069     up : function(e, m){
36070         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36071             m.tryActivate(m.items.length-1, -1);
36072         }
36073     },
36074
36075     down : function(e, m){
36076         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36077             m.tryActivate(0, 1);
36078         }
36079     },
36080
36081     right : function(e, m){
36082         if(m.activeItem){
36083             m.activeItem.expandMenu(true);
36084         }
36085     },
36086
36087     left : function(e, m){
36088         m.hide();
36089         if(m.parentMenu && m.parentMenu.activeItem){
36090             m.parentMenu.activeItem.activate();
36091         }
36092     },
36093
36094     enter : function(e, m){
36095         if(m.activeItem){
36096             e.stopPropagation();
36097             m.activeItem.onClick(e);
36098             m.fireEvent("click", this, m.activeItem);
36099             return true;
36100         }
36101     }
36102 });/*
36103  * Based on:
36104  * Ext JS Library 1.1.1
36105  * Copyright(c) 2006-2007, Ext JS, LLC.
36106  *
36107  * Originally Released Under LGPL - original licence link has changed is not relivant.
36108  *
36109  * Fork - LGPL
36110  * <script type="text/javascript">
36111  */
36112  
36113 /**
36114  * @class Roo.menu.MenuMgr
36115  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36116  * @singleton
36117  */
36118 Roo.menu.MenuMgr = function(){
36119    var menus, active, groups = {}, attached = false, lastShow = new Date();
36120
36121    // private - called when first menu is created
36122    function init(){
36123        menus = {};
36124        active = new Roo.util.MixedCollection();
36125        Roo.get(document).addKeyListener(27, function(){
36126            if(active.length > 0){
36127                hideAll();
36128            }
36129        });
36130    }
36131
36132    // private
36133    function hideAll(){
36134        if(active && active.length > 0){
36135            var c = active.clone();
36136            c.each(function(m){
36137                m.hide();
36138            });
36139        }
36140    }
36141
36142    // private
36143    function onHide(m){
36144        active.remove(m);
36145        if(active.length < 1){
36146            Roo.get(document).un("mousedown", onMouseDown);
36147            attached = false;
36148        }
36149    }
36150
36151    // private
36152    function onShow(m){
36153        var last = active.last();
36154        lastShow = new Date();
36155        active.add(m);
36156        if(!attached){
36157            Roo.get(document).on("mousedown", onMouseDown);
36158            attached = true;
36159        }
36160        if(m.parentMenu){
36161           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36162           m.parentMenu.activeChild = m;
36163        }else if(last && last.isVisible()){
36164           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36165        }
36166    }
36167
36168    // private
36169    function onBeforeHide(m){
36170        if(m.activeChild){
36171            m.activeChild.hide();
36172        }
36173        if(m.autoHideTimer){
36174            clearTimeout(m.autoHideTimer);
36175            delete m.autoHideTimer;
36176        }
36177    }
36178
36179    // private
36180    function onBeforeShow(m){
36181        var pm = m.parentMenu;
36182        if(!pm && !m.allowOtherMenus){
36183            hideAll();
36184        }else if(pm && pm.activeChild && active != m){
36185            pm.activeChild.hide();
36186        }
36187    }
36188
36189    // private
36190    function onMouseDown(e){
36191        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36192            hideAll();
36193        }
36194    }
36195
36196    // private
36197    function onBeforeCheck(mi, state){
36198        if(state){
36199            var g = groups[mi.group];
36200            for(var i = 0, l = g.length; i < l; i++){
36201                if(g[i] != mi){
36202                    g[i].setChecked(false);
36203                }
36204            }
36205        }
36206    }
36207
36208    return {
36209
36210        /**
36211         * Hides all menus that are currently visible
36212         */
36213        hideAll : function(){
36214             hideAll();  
36215        },
36216
36217        // private
36218        register : function(menu){
36219            if(!menus){
36220                init();
36221            }
36222            menus[menu.id] = menu;
36223            menu.on("beforehide", onBeforeHide);
36224            menu.on("hide", onHide);
36225            menu.on("beforeshow", onBeforeShow);
36226            menu.on("show", onShow);
36227            var g = menu.group;
36228            if(g && menu.events["checkchange"]){
36229                if(!groups[g]){
36230                    groups[g] = [];
36231                }
36232                groups[g].push(menu);
36233                menu.on("checkchange", onCheck);
36234            }
36235        },
36236
36237         /**
36238          * Returns a {@link Roo.menu.Menu} object
36239          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36240          * be used to generate and return a new Menu instance.
36241          */
36242        get : function(menu){
36243            if(typeof menu == "string"){ // menu id
36244                return menus[menu];
36245            }else if(menu.events){  // menu instance
36246                return menu;
36247            }else if(typeof menu.length == 'number'){ // array of menu items?
36248                return new Roo.menu.Menu({items:menu});
36249            }else{ // otherwise, must be a config
36250                return new Roo.menu.Menu(menu);
36251            }
36252        },
36253
36254        // private
36255        unregister : function(menu){
36256            delete menus[menu.id];
36257            menu.un("beforehide", onBeforeHide);
36258            menu.un("hide", onHide);
36259            menu.un("beforeshow", onBeforeShow);
36260            menu.un("show", onShow);
36261            var g = menu.group;
36262            if(g && menu.events["checkchange"]){
36263                groups[g].remove(menu);
36264                menu.un("checkchange", onCheck);
36265            }
36266        },
36267
36268        // private
36269        registerCheckable : function(menuItem){
36270            var g = menuItem.group;
36271            if(g){
36272                if(!groups[g]){
36273                    groups[g] = [];
36274                }
36275                groups[g].push(menuItem);
36276                menuItem.on("beforecheckchange", onBeforeCheck);
36277            }
36278        },
36279
36280        // private
36281        unregisterCheckable : function(menuItem){
36282            var g = menuItem.group;
36283            if(g){
36284                groups[g].remove(menuItem);
36285                menuItem.un("beforecheckchange", onBeforeCheck);
36286            }
36287        }
36288    };
36289 }();/*
36290  * Based on:
36291  * Ext JS Library 1.1.1
36292  * Copyright(c) 2006-2007, Ext JS, LLC.
36293  *
36294  * Originally Released Under LGPL - original licence link has changed is not relivant.
36295  *
36296  * Fork - LGPL
36297  * <script type="text/javascript">
36298  */
36299  
36300
36301 /**
36302  * @class Roo.menu.BaseItem
36303  * @extends Roo.Component
36304  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36305  * management and base configuration options shared by all menu components.
36306  * @constructor
36307  * Creates a new BaseItem
36308  * @param {Object} config Configuration options
36309  */
36310 Roo.menu.BaseItem = function(config){
36311     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36312
36313     this.addEvents({
36314         /**
36315          * @event click
36316          * Fires when this item is clicked
36317          * @param {Roo.menu.BaseItem} this
36318          * @param {Roo.EventObject} e
36319          */
36320         click: true,
36321         /**
36322          * @event activate
36323          * Fires when this item is activated
36324          * @param {Roo.menu.BaseItem} this
36325          */
36326         activate : true,
36327         /**
36328          * @event deactivate
36329          * Fires when this item is deactivated
36330          * @param {Roo.menu.BaseItem} this
36331          */
36332         deactivate : true
36333     });
36334
36335     if(this.handler){
36336         this.on("click", this.handler, this.scope, true);
36337     }
36338 };
36339
36340 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36341     /**
36342      * @cfg {Function} handler
36343      * A function that will handle the click event of this menu item (defaults to undefined)
36344      */
36345     /**
36346      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36347      */
36348     canActivate : false,
36349     
36350      /**
36351      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36352      */
36353     hidden: false,
36354     
36355     /**
36356      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36357      */
36358     activeClass : "x-menu-item-active",
36359     /**
36360      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36361      */
36362     hideOnClick : true,
36363     /**
36364      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36365      */
36366     hideDelay : 100,
36367
36368     // private
36369     ctype: "Roo.menu.BaseItem",
36370
36371     // private
36372     actionMode : "container",
36373
36374     // private
36375     render : function(container, parentMenu){
36376         this.parentMenu = parentMenu;
36377         Roo.menu.BaseItem.superclass.render.call(this, container);
36378         this.container.menuItemId = this.id;
36379     },
36380
36381     // private
36382     onRender : function(container, position){
36383         this.el = Roo.get(this.el);
36384         container.dom.appendChild(this.el.dom);
36385     },
36386
36387     // private
36388     onClick : function(e){
36389         if(!this.disabled && this.fireEvent("click", this, e) !== false
36390                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36391             this.handleClick(e);
36392         }else{
36393             e.stopEvent();
36394         }
36395     },
36396
36397     // private
36398     activate : function(){
36399         if(this.disabled){
36400             return false;
36401         }
36402         var li = this.container;
36403         li.addClass(this.activeClass);
36404         this.region = li.getRegion().adjust(2, 2, -2, -2);
36405         this.fireEvent("activate", this);
36406         return true;
36407     },
36408
36409     // private
36410     deactivate : function(){
36411         this.container.removeClass(this.activeClass);
36412         this.fireEvent("deactivate", this);
36413     },
36414
36415     // private
36416     shouldDeactivate : function(e){
36417         return !this.region || !this.region.contains(e.getPoint());
36418     },
36419
36420     // private
36421     handleClick : function(e){
36422         if(this.hideOnClick){
36423             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36424         }
36425     },
36426
36427     // private
36428     expandMenu : function(autoActivate){
36429         // do nothing
36430     },
36431
36432     // private
36433     hideMenu : function(){
36434         // do nothing
36435     }
36436 });/*
36437  * Based on:
36438  * Ext JS Library 1.1.1
36439  * Copyright(c) 2006-2007, Ext JS, LLC.
36440  *
36441  * Originally Released Under LGPL - original licence link has changed is not relivant.
36442  *
36443  * Fork - LGPL
36444  * <script type="text/javascript">
36445  */
36446  
36447 /**
36448  * @class Roo.menu.Adapter
36449  * @extends Roo.menu.BaseItem
36450  * 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.
36451  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36452  * @constructor
36453  * Creates a new Adapter
36454  * @param {Object} config Configuration options
36455  */
36456 Roo.menu.Adapter = function(component, config){
36457     Roo.menu.Adapter.superclass.constructor.call(this, config);
36458     this.component = component;
36459 };
36460 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36461     // private
36462     canActivate : true,
36463
36464     // private
36465     onRender : function(container, position){
36466         this.component.render(container);
36467         this.el = this.component.getEl();
36468     },
36469
36470     // private
36471     activate : function(){
36472         if(this.disabled){
36473             return false;
36474         }
36475         this.component.focus();
36476         this.fireEvent("activate", this);
36477         return true;
36478     },
36479
36480     // private
36481     deactivate : function(){
36482         this.fireEvent("deactivate", this);
36483     },
36484
36485     // private
36486     disable : function(){
36487         this.component.disable();
36488         Roo.menu.Adapter.superclass.disable.call(this);
36489     },
36490
36491     // private
36492     enable : function(){
36493         this.component.enable();
36494         Roo.menu.Adapter.superclass.enable.call(this);
36495     }
36496 });/*
36497  * Based on:
36498  * Ext JS Library 1.1.1
36499  * Copyright(c) 2006-2007, Ext JS, LLC.
36500  *
36501  * Originally Released Under LGPL - original licence link has changed is not relivant.
36502  *
36503  * Fork - LGPL
36504  * <script type="text/javascript">
36505  */
36506
36507 /**
36508  * @class Roo.menu.TextItem
36509  * @extends Roo.menu.BaseItem
36510  * Adds a static text string to a menu, usually used as either a heading or group separator.
36511  * Note: old style constructor with text is still supported.
36512  * 
36513  * @constructor
36514  * Creates a new TextItem
36515  * @param {Object} cfg Configuration
36516  */
36517 Roo.menu.TextItem = function(cfg){
36518     if (typeof(cfg) == 'string') {
36519         this.text = cfg;
36520     } else {
36521         Roo.apply(this,cfg);
36522     }
36523     
36524     Roo.menu.TextItem.superclass.constructor.call(this);
36525 };
36526
36527 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36528     /**
36529      * @cfg {Boolean} text Text to show on item.
36530      */
36531     text : '',
36532     
36533     /**
36534      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36535      */
36536     hideOnClick : false,
36537     /**
36538      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36539      */
36540     itemCls : "x-menu-text",
36541
36542     // private
36543     onRender : function(){
36544         var s = document.createElement("span");
36545         s.className = this.itemCls;
36546         s.innerHTML = this.text;
36547         this.el = s;
36548         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36549     }
36550 });/*
36551  * Based on:
36552  * Ext JS Library 1.1.1
36553  * Copyright(c) 2006-2007, Ext JS, LLC.
36554  *
36555  * Originally Released Under LGPL - original licence link has changed is not relivant.
36556  *
36557  * Fork - LGPL
36558  * <script type="text/javascript">
36559  */
36560
36561 /**
36562  * @class Roo.menu.Separator
36563  * @extends Roo.menu.BaseItem
36564  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36565  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36566  * @constructor
36567  * @param {Object} config Configuration options
36568  */
36569 Roo.menu.Separator = function(config){
36570     Roo.menu.Separator.superclass.constructor.call(this, config);
36571 };
36572
36573 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36574     /**
36575      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36576      */
36577     itemCls : "x-menu-sep",
36578     /**
36579      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36580      */
36581     hideOnClick : false,
36582
36583     // private
36584     onRender : function(li){
36585         var s = document.createElement("span");
36586         s.className = this.itemCls;
36587         s.innerHTML = "&#160;";
36588         this.el = s;
36589         li.addClass("x-menu-sep-li");
36590         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36591     }
36592 });/*
36593  * Based on:
36594  * Ext JS Library 1.1.1
36595  * Copyright(c) 2006-2007, Ext JS, LLC.
36596  *
36597  * Originally Released Under LGPL - original licence link has changed is not relivant.
36598  *
36599  * Fork - LGPL
36600  * <script type="text/javascript">
36601  */
36602 /**
36603  * @class Roo.menu.Item
36604  * @extends Roo.menu.BaseItem
36605  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36606  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36607  * activation and click handling.
36608  * @constructor
36609  * Creates a new Item
36610  * @param {Object} config Configuration options
36611  */
36612 Roo.menu.Item = function(config){
36613     Roo.menu.Item.superclass.constructor.call(this, config);
36614     if(this.menu){
36615         this.menu = Roo.menu.MenuMgr.get(this.menu);
36616     }
36617 };
36618 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36619     
36620     /**
36621      * @cfg {String} text
36622      * The text to show on the menu item.
36623      */
36624     text: '',
36625      /**
36626      * @cfg {String} HTML to render in menu
36627      * The text to show on the menu item (HTML version).
36628      */
36629     html: '',
36630     /**
36631      * @cfg {String} icon
36632      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36633      */
36634     icon: undefined,
36635     /**
36636      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36637      */
36638     itemCls : "x-menu-item",
36639     /**
36640      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36641      */
36642     canActivate : true,
36643     /**
36644      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36645      */
36646     showDelay: 200,
36647     // doc'd in BaseItem
36648     hideDelay: 200,
36649
36650     // private
36651     ctype: "Roo.menu.Item",
36652     
36653     // private
36654     onRender : function(container, position){
36655         var el = document.createElement("a");
36656         el.hideFocus = true;
36657         el.unselectable = "on";
36658         el.href = this.href || "#";
36659         if(this.hrefTarget){
36660             el.target = this.hrefTarget;
36661         }
36662         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36663         
36664         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36665         
36666         el.innerHTML = String.format(
36667                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36668                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36669         this.el = el;
36670         Roo.menu.Item.superclass.onRender.call(this, container, position);
36671     },
36672
36673     /**
36674      * Sets the text to display in this menu item
36675      * @param {String} text The text to display
36676      * @param {Boolean} isHTML true to indicate text is pure html.
36677      */
36678     setText : function(text, isHTML){
36679         if (isHTML) {
36680             this.html = text;
36681         } else {
36682             this.text = text;
36683             this.html = '';
36684         }
36685         if(this.rendered){
36686             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36687      
36688             this.el.update(String.format(
36689                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36690                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36691             this.parentMenu.autoWidth();
36692         }
36693     },
36694
36695     // private
36696     handleClick : function(e){
36697         if(!this.href){ // if no link defined, stop the event automatically
36698             e.stopEvent();
36699         }
36700         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36701     },
36702
36703     // private
36704     activate : function(autoExpand){
36705         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36706             this.focus();
36707             if(autoExpand){
36708                 this.expandMenu();
36709             }
36710         }
36711         return true;
36712     },
36713
36714     // private
36715     shouldDeactivate : function(e){
36716         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36717             if(this.menu && this.menu.isVisible()){
36718                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36719             }
36720             return true;
36721         }
36722         return false;
36723     },
36724
36725     // private
36726     deactivate : function(){
36727         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36728         this.hideMenu();
36729     },
36730
36731     // private
36732     expandMenu : function(autoActivate){
36733         if(!this.disabled && this.menu){
36734             clearTimeout(this.hideTimer);
36735             delete this.hideTimer;
36736             if(!this.menu.isVisible() && !this.showTimer){
36737                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36738             }else if (this.menu.isVisible() && autoActivate){
36739                 this.menu.tryActivate(0, 1);
36740             }
36741         }
36742     },
36743
36744     // private
36745     deferExpand : function(autoActivate){
36746         delete this.showTimer;
36747         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36748         if(autoActivate){
36749             this.menu.tryActivate(0, 1);
36750         }
36751     },
36752
36753     // private
36754     hideMenu : function(){
36755         clearTimeout(this.showTimer);
36756         delete this.showTimer;
36757         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36758             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36759         }
36760     },
36761
36762     // private
36763     deferHide : function(){
36764         delete this.hideTimer;
36765         this.menu.hide();
36766     }
36767 });/*
36768  * Based on:
36769  * Ext JS Library 1.1.1
36770  * Copyright(c) 2006-2007, Ext JS, LLC.
36771  *
36772  * Originally Released Under LGPL - original licence link has changed is not relivant.
36773  *
36774  * Fork - LGPL
36775  * <script type="text/javascript">
36776  */
36777  
36778 /**
36779  * @class Roo.menu.CheckItem
36780  * @extends Roo.menu.Item
36781  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36782  * @constructor
36783  * Creates a new CheckItem
36784  * @param {Object} config Configuration options
36785  */
36786 Roo.menu.CheckItem = function(config){
36787     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36788     this.addEvents({
36789         /**
36790          * @event beforecheckchange
36791          * Fires before the checked value is set, providing an opportunity to cancel if needed
36792          * @param {Roo.menu.CheckItem} this
36793          * @param {Boolean} checked The new checked value that will be set
36794          */
36795         "beforecheckchange" : true,
36796         /**
36797          * @event checkchange
36798          * Fires after the checked value has been set
36799          * @param {Roo.menu.CheckItem} this
36800          * @param {Boolean} checked The checked value that was set
36801          */
36802         "checkchange" : true
36803     });
36804     if(this.checkHandler){
36805         this.on('checkchange', this.checkHandler, this.scope);
36806     }
36807 };
36808 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36809     /**
36810      * @cfg {String} group
36811      * All check items with the same group name will automatically be grouped into a single-select
36812      * radio button group (defaults to '')
36813      */
36814     /**
36815      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36816      */
36817     itemCls : "x-menu-item x-menu-check-item",
36818     /**
36819      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36820      */
36821     groupClass : "x-menu-group-item",
36822
36823     /**
36824      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36825      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36826      * initialized with checked = true will be rendered as checked.
36827      */
36828     checked: false,
36829
36830     // private
36831     ctype: "Roo.menu.CheckItem",
36832
36833     // private
36834     onRender : function(c){
36835         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36836         if(this.group){
36837             this.el.addClass(this.groupClass);
36838         }
36839         Roo.menu.MenuMgr.registerCheckable(this);
36840         if(this.checked){
36841             this.checked = false;
36842             this.setChecked(true, true);
36843         }
36844     },
36845
36846     // private
36847     destroy : function(){
36848         if(this.rendered){
36849             Roo.menu.MenuMgr.unregisterCheckable(this);
36850         }
36851         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36852     },
36853
36854     /**
36855      * Set the checked state of this item
36856      * @param {Boolean} checked The new checked value
36857      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36858      */
36859     setChecked : function(state, suppressEvent){
36860         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36861             if(this.container){
36862                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36863             }
36864             this.checked = state;
36865             if(suppressEvent !== true){
36866                 this.fireEvent("checkchange", this, state);
36867             }
36868         }
36869     },
36870
36871     // private
36872     handleClick : function(e){
36873        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36874            this.setChecked(!this.checked);
36875        }
36876        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36877     }
36878 });/*
36879  * Based on:
36880  * Ext JS Library 1.1.1
36881  * Copyright(c) 2006-2007, Ext JS, LLC.
36882  *
36883  * Originally Released Under LGPL - original licence link has changed is not relivant.
36884  *
36885  * Fork - LGPL
36886  * <script type="text/javascript">
36887  */
36888  
36889 /**
36890  * @class Roo.menu.DateItem
36891  * @extends Roo.menu.Adapter
36892  * A menu item that wraps the {@link Roo.DatPicker} component.
36893  * @constructor
36894  * Creates a new DateItem
36895  * @param {Object} config Configuration options
36896  */
36897 Roo.menu.DateItem = function(config){
36898     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36899     /** The Roo.DatePicker object @type Roo.DatePicker */
36900     this.picker = this.component;
36901     this.addEvents({select: true});
36902     
36903     this.picker.on("render", function(picker){
36904         picker.getEl().swallowEvent("click");
36905         picker.container.addClass("x-menu-date-item");
36906     });
36907
36908     this.picker.on("select", this.onSelect, this);
36909 };
36910
36911 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36912     // private
36913     onSelect : function(picker, date){
36914         this.fireEvent("select", this, date, picker);
36915         Roo.menu.DateItem.superclass.handleClick.call(this);
36916     }
36917 });/*
36918  * Based on:
36919  * Ext JS Library 1.1.1
36920  * Copyright(c) 2006-2007, Ext JS, LLC.
36921  *
36922  * Originally Released Under LGPL - original licence link has changed is not relivant.
36923  *
36924  * Fork - LGPL
36925  * <script type="text/javascript">
36926  */
36927  
36928 /**
36929  * @class Roo.menu.ColorItem
36930  * @extends Roo.menu.Adapter
36931  * A menu item that wraps the {@link Roo.ColorPalette} component.
36932  * @constructor
36933  * Creates a new ColorItem
36934  * @param {Object} config Configuration options
36935  */
36936 Roo.menu.ColorItem = function(config){
36937     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36938     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36939     this.palette = this.component;
36940     this.relayEvents(this.palette, ["select"]);
36941     if(this.selectHandler){
36942         this.on('select', this.selectHandler, this.scope);
36943     }
36944 };
36945 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36946  * Based on:
36947  * Ext JS Library 1.1.1
36948  * Copyright(c) 2006-2007, Ext JS, LLC.
36949  *
36950  * Originally Released Under LGPL - original licence link has changed is not relivant.
36951  *
36952  * Fork - LGPL
36953  * <script type="text/javascript">
36954  */
36955  
36956
36957 /**
36958  * @class Roo.menu.DateMenu
36959  * @extends Roo.menu.Menu
36960  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36961  * @constructor
36962  * Creates a new DateMenu
36963  * @param {Object} config Configuration options
36964  */
36965 Roo.menu.DateMenu = function(config){
36966     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36967     this.plain = true;
36968     var di = new Roo.menu.DateItem(config);
36969     this.add(di);
36970     /**
36971      * The {@link Roo.DatePicker} instance for this DateMenu
36972      * @type DatePicker
36973      */
36974     this.picker = di.picker;
36975     /**
36976      * @event select
36977      * @param {DatePicker} picker
36978      * @param {Date} date
36979      */
36980     this.relayEvents(di, ["select"]);
36981     this.on('beforeshow', function(){
36982         if(this.picker){
36983             this.picker.hideMonthPicker(false);
36984         }
36985     }, this);
36986 };
36987 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36988     cls:'x-date-menu'
36989 });/*
36990  * Based on:
36991  * Ext JS Library 1.1.1
36992  * Copyright(c) 2006-2007, Ext JS, LLC.
36993  *
36994  * Originally Released Under LGPL - original licence link has changed is not relivant.
36995  *
36996  * Fork - LGPL
36997  * <script type="text/javascript">
36998  */
36999  
37000
37001 /**
37002  * @class Roo.menu.ColorMenu
37003  * @extends Roo.menu.Menu
37004  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37005  * @constructor
37006  * Creates a new ColorMenu
37007  * @param {Object} config Configuration options
37008  */
37009 Roo.menu.ColorMenu = function(config){
37010     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37011     this.plain = true;
37012     var ci = new Roo.menu.ColorItem(config);
37013     this.add(ci);
37014     /**
37015      * The {@link Roo.ColorPalette} instance for this ColorMenu
37016      * @type ColorPalette
37017      */
37018     this.palette = ci.palette;
37019     /**
37020      * @event select
37021      * @param {ColorPalette} palette
37022      * @param {String} color
37023      */
37024     this.relayEvents(ci, ["select"]);
37025 };
37026 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37027  * Based on:
37028  * Ext JS Library 1.1.1
37029  * Copyright(c) 2006-2007, Ext JS, LLC.
37030  *
37031  * Originally Released Under LGPL - original licence link has changed is not relivant.
37032  *
37033  * Fork - LGPL
37034  * <script type="text/javascript">
37035  */
37036  
37037 /**
37038  * @class Roo.form.Field
37039  * @extends Roo.BoxComponent
37040  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37041  * @constructor
37042  * Creates a new Field
37043  * @param {Object} config Configuration options
37044  */
37045 Roo.form.Field = function(config){
37046     Roo.form.Field.superclass.constructor.call(this, config);
37047 };
37048
37049 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37050     /**
37051      * @cfg {String} fieldLabel Label to use when rendering a form.
37052      */
37053        /**
37054      * @cfg {String} qtip Mouse over tip
37055      */
37056      
37057     /**
37058      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37059      */
37060     invalidClass : "x-form-invalid",
37061     /**
37062      * @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")
37063      */
37064     invalidText : "The value in this field is invalid",
37065     /**
37066      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37067      */
37068     focusClass : "x-form-focus",
37069     /**
37070      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37071       automatic validation (defaults to "keyup").
37072      */
37073     validationEvent : "keyup",
37074     /**
37075      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37076      */
37077     validateOnBlur : true,
37078     /**
37079      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37080      */
37081     validationDelay : 250,
37082     /**
37083      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37084      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37085      */
37086     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37087     /**
37088      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37089      */
37090     fieldClass : "x-form-field",
37091     /**
37092      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37093      *<pre>
37094 Value         Description
37095 -----------   ----------------------------------------------------------------------
37096 qtip          Display a quick tip when the user hovers over the field
37097 title         Display a default browser title attribute popup
37098 under         Add a block div beneath the field containing the error text
37099 side          Add an error icon to the right of the field with a popup on hover
37100 [element id]  Add the error text directly to the innerHTML of the specified element
37101 </pre>
37102      */
37103     msgTarget : 'qtip',
37104     /**
37105      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37106      */
37107     msgFx : 'normal',
37108
37109     /**
37110      * @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.
37111      */
37112     readOnly : false,
37113
37114     /**
37115      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37116      */
37117     disabled : false,
37118
37119     /**
37120      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37121      */
37122     inputType : undefined,
37123     
37124     /**
37125      * @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).
37126          */
37127         tabIndex : undefined,
37128         
37129     // private
37130     isFormField : true,
37131
37132     // private
37133     hasFocus : false,
37134     /**
37135      * @property {Roo.Element} fieldEl
37136      * Element Containing the rendered Field (with label etc.)
37137      */
37138     /**
37139      * @cfg {Mixed} value A value to initialize this field with.
37140      */
37141     value : undefined,
37142
37143     /**
37144      * @cfg {String} name The field's HTML name attribute.
37145      */
37146     /**
37147      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37148      */
37149
37150         // private ??
37151         initComponent : function(){
37152         Roo.form.Field.superclass.initComponent.call(this);
37153         this.addEvents({
37154             /**
37155              * @event focus
37156              * Fires when this field receives input focus.
37157              * @param {Roo.form.Field} this
37158              */
37159             focus : true,
37160             /**
37161              * @event blur
37162              * Fires when this field loses input focus.
37163              * @param {Roo.form.Field} this
37164              */
37165             blur : true,
37166             /**
37167              * @event specialkey
37168              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37169              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37170              * @param {Roo.form.Field} this
37171              * @param {Roo.EventObject} e The event object
37172              */
37173             specialkey : true,
37174             /**
37175              * @event change
37176              * Fires just before the field blurs if the field value has changed.
37177              * @param {Roo.form.Field} this
37178              * @param {Mixed} newValue The new value
37179              * @param {Mixed} oldValue The original value
37180              */
37181             change : true,
37182             /**
37183              * @event invalid
37184              * Fires after the field has been marked as invalid.
37185              * @param {Roo.form.Field} this
37186              * @param {String} msg The validation message
37187              */
37188             invalid : true,
37189             /**
37190              * @event valid
37191              * Fires after the field has been validated with no errors.
37192              * @param {Roo.form.Field} this
37193              */
37194             valid : true,
37195              /**
37196              * @event keyup
37197              * Fires after the key up
37198              * @param {Roo.form.Field} this
37199              * @param {Roo.EventObject}  e The event Object
37200              */
37201             keyup : true
37202         });
37203     },
37204
37205     /**
37206      * Returns the name attribute of the field if available
37207      * @return {String} name The field name
37208      */
37209     getName: function(){
37210          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37211     },
37212
37213     // private
37214     onRender : function(ct, position){
37215         Roo.form.Field.superclass.onRender.call(this, ct, position);
37216         if(!this.el){
37217             var cfg = this.getAutoCreate();
37218             if(!cfg.name){
37219                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37220             }
37221             if (!cfg.name.length) {
37222                 delete cfg.name;
37223             }
37224             if(this.inputType){
37225                 cfg.type = this.inputType;
37226             }
37227             this.el = ct.createChild(cfg, position);
37228         }
37229         var type = this.el.dom.type;
37230         if(type){
37231             if(type == 'password'){
37232                 type = 'text';
37233             }
37234             this.el.addClass('x-form-'+type);
37235         }
37236         if(this.readOnly){
37237             this.el.dom.readOnly = true;
37238         }
37239         if(this.tabIndex !== undefined){
37240             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37241         }
37242
37243         this.el.addClass([this.fieldClass, this.cls]);
37244         this.initValue();
37245     },
37246
37247     /**
37248      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37249      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37250      * @return {Roo.form.Field} this
37251      */
37252     applyTo : function(target){
37253         this.allowDomMove = false;
37254         this.el = Roo.get(target);
37255         this.render(this.el.dom.parentNode);
37256         return this;
37257     },
37258
37259     // private
37260     initValue : function(){
37261         if(this.value !== undefined){
37262             this.setValue(this.value);
37263         }else if(this.el.dom.value.length > 0){
37264             this.setValue(this.el.dom.value);
37265         }
37266     },
37267
37268     /**
37269      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37270      */
37271     isDirty : function() {
37272         if(this.disabled) {
37273             return false;
37274         }
37275         return String(this.getValue()) !== String(this.originalValue);
37276     },
37277
37278     // private
37279     afterRender : function(){
37280         Roo.form.Field.superclass.afterRender.call(this);
37281         this.initEvents();
37282     },
37283
37284     // private
37285     fireKey : function(e){
37286         //Roo.log('field ' + e.getKey());
37287         if(e.isNavKeyPress()){
37288             this.fireEvent("specialkey", this, e);
37289         }
37290     },
37291
37292     /**
37293      * Resets the current field value to the originally loaded value and clears any validation messages
37294      */
37295     reset : function(){
37296         this.setValue(this.resetValue);
37297         this.clearInvalid();
37298     },
37299
37300     // private
37301     initEvents : function(){
37302         // safari killled keypress - so keydown is now used..
37303         this.el.on("keydown" , this.fireKey,  this);
37304         this.el.on("focus", this.onFocus,  this);
37305         this.el.on("blur", this.onBlur,  this);
37306         this.el.relayEvent('keyup', this);
37307
37308         // reference to original value for reset
37309         this.originalValue = this.getValue();
37310         this.resetValue =  this.getValue();
37311     },
37312
37313     // private
37314     onFocus : function(){
37315         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37316             this.el.addClass(this.focusClass);
37317         }
37318         if(!this.hasFocus){
37319             this.hasFocus = true;
37320             this.startValue = this.getValue();
37321             this.fireEvent("focus", this);
37322         }
37323     },
37324
37325     beforeBlur : Roo.emptyFn,
37326
37327     // private
37328     onBlur : function(){
37329         this.beforeBlur();
37330         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37331             this.el.removeClass(this.focusClass);
37332         }
37333         this.hasFocus = false;
37334         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37335             this.validate();
37336         }
37337         var v = this.getValue();
37338         if(String(v) !== String(this.startValue)){
37339             this.fireEvent('change', this, v, this.startValue);
37340         }
37341         this.fireEvent("blur", this);
37342     },
37343
37344     /**
37345      * Returns whether or not the field value is currently valid
37346      * @param {Boolean} preventMark True to disable marking the field invalid
37347      * @return {Boolean} True if the value is valid, else false
37348      */
37349     isValid : function(preventMark){
37350         if(this.disabled){
37351             return true;
37352         }
37353         var restore = this.preventMark;
37354         this.preventMark = preventMark === true;
37355         var v = this.validateValue(this.processValue(this.getRawValue()));
37356         this.preventMark = restore;
37357         return v;
37358     },
37359
37360     /**
37361      * Validates the field value
37362      * @return {Boolean} True if the value is valid, else false
37363      */
37364     validate : function(){
37365         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37366             this.clearInvalid();
37367             return true;
37368         }
37369         return false;
37370     },
37371
37372     processValue : function(value){
37373         return value;
37374     },
37375
37376     // private
37377     // Subclasses should provide the validation implementation by overriding this
37378     validateValue : function(value){
37379         return true;
37380     },
37381
37382     /**
37383      * Mark this field as invalid
37384      * @param {String} msg The validation message
37385      */
37386     markInvalid : function(msg){
37387         if(!this.rendered || this.preventMark){ // not rendered
37388             return;
37389         }
37390         
37391         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37392         
37393         obj.el.addClass(this.invalidClass);
37394         msg = msg || this.invalidText;
37395         switch(this.msgTarget){
37396             case 'qtip':
37397                 obj.el.dom.qtip = msg;
37398                 obj.el.dom.qclass = 'x-form-invalid-tip';
37399                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37400                     Roo.QuickTips.enable();
37401                 }
37402                 break;
37403             case 'title':
37404                 this.el.dom.title = msg;
37405                 break;
37406             case 'under':
37407                 if(!this.errorEl){
37408                     var elp = this.el.findParent('.x-form-element', 5, true);
37409                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37410                     this.errorEl.setWidth(elp.getWidth(true)-20);
37411                 }
37412                 this.errorEl.update(msg);
37413                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37414                 break;
37415             case 'side':
37416                 if(!this.errorIcon){
37417                     var elp = this.el.findParent('.x-form-element', 5, true);
37418                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37419                 }
37420                 this.alignErrorIcon();
37421                 this.errorIcon.dom.qtip = msg;
37422                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37423                 this.errorIcon.show();
37424                 this.on('resize', this.alignErrorIcon, this);
37425                 break;
37426             default:
37427                 var t = Roo.getDom(this.msgTarget);
37428                 t.innerHTML = msg;
37429                 t.style.display = this.msgDisplay;
37430                 break;
37431         }
37432         this.fireEvent('invalid', this, msg);
37433     },
37434
37435     // private
37436     alignErrorIcon : function(){
37437         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37438     },
37439
37440     /**
37441      * Clear any invalid styles/messages for this field
37442      */
37443     clearInvalid : function(){
37444         if(!this.rendered || this.preventMark){ // not rendered
37445             return;
37446         }
37447         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37448         
37449         obj.el.removeClass(this.invalidClass);
37450         switch(this.msgTarget){
37451             case 'qtip':
37452                 obj.el.dom.qtip = '';
37453                 break;
37454             case 'title':
37455                 this.el.dom.title = '';
37456                 break;
37457             case 'under':
37458                 if(this.errorEl){
37459                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37460                 }
37461                 break;
37462             case 'side':
37463                 if(this.errorIcon){
37464                     this.errorIcon.dom.qtip = '';
37465                     this.errorIcon.hide();
37466                     this.un('resize', this.alignErrorIcon, this);
37467                 }
37468                 break;
37469             default:
37470                 var t = Roo.getDom(this.msgTarget);
37471                 t.innerHTML = '';
37472                 t.style.display = 'none';
37473                 break;
37474         }
37475         this.fireEvent('valid', this);
37476     },
37477
37478     /**
37479      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37480      * @return {Mixed} value The field value
37481      */
37482     getRawValue : function(){
37483         var v = this.el.getValue();
37484         
37485         return v;
37486     },
37487
37488     /**
37489      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37490      * @return {Mixed} value The field value
37491      */
37492     getValue : function(){
37493         var v = this.el.getValue();
37494          
37495         return v;
37496     },
37497
37498     /**
37499      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37500      * @param {Mixed} value The value to set
37501      */
37502     setRawValue : function(v){
37503         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37504     },
37505
37506     /**
37507      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37508      * @param {Mixed} value The value to set
37509      */
37510     setValue : function(v){
37511         this.value = v;
37512         if(this.rendered){
37513             this.el.dom.value = (v === null || v === undefined ? '' : v);
37514              this.validate();
37515         }
37516     },
37517
37518     adjustSize : function(w, h){
37519         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37520         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37521         return s;
37522     },
37523
37524     adjustWidth : function(tag, w){
37525         tag = tag.toLowerCase();
37526         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37527             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37528                 if(tag == 'input'){
37529                     return w + 2;
37530                 }
37531                 if(tag == 'textarea'){
37532                     return w-2;
37533                 }
37534             }else if(Roo.isOpera){
37535                 if(tag == 'input'){
37536                     return w + 2;
37537                 }
37538                 if(tag == 'textarea'){
37539                     return w-2;
37540                 }
37541             }
37542         }
37543         return w;
37544     }
37545 });
37546
37547
37548 // anything other than normal should be considered experimental
37549 Roo.form.Field.msgFx = {
37550     normal : {
37551         show: function(msgEl, f){
37552             msgEl.setDisplayed('block');
37553         },
37554
37555         hide : function(msgEl, f){
37556             msgEl.setDisplayed(false).update('');
37557         }
37558     },
37559
37560     slide : {
37561         show: function(msgEl, f){
37562             msgEl.slideIn('t', {stopFx:true});
37563         },
37564
37565         hide : function(msgEl, f){
37566             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37567         }
37568     },
37569
37570     slideRight : {
37571         show: function(msgEl, f){
37572             msgEl.fixDisplay();
37573             msgEl.alignTo(f.el, 'tl-tr');
37574             msgEl.slideIn('l', {stopFx:true});
37575         },
37576
37577         hide : function(msgEl, f){
37578             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37579         }
37580     }
37581 };/*
37582  * Based on:
37583  * Ext JS Library 1.1.1
37584  * Copyright(c) 2006-2007, Ext JS, LLC.
37585  *
37586  * Originally Released Under LGPL - original licence link has changed is not relivant.
37587  *
37588  * Fork - LGPL
37589  * <script type="text/javascript">
37590  */
37591  
37592
37593 /**
37594  * @class Roo.form.TextField
37595  * @extends Roo.form.Field
37596  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37597  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37598  * @constructor
37599  * Creates a new TextField
37600  * @param {Object} config Configuration options
37601  */
37602 Roo.form.TextField = function(config){
37603     Roo.form.TextField.superclass.constructor.call(this, config);
37604     this.addEvents({
37605         /**
37606          * @event autosize
37607          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37608          * according to the default logic, but this event provides a hook for the developer to apply additional
37609          * logic at runtime to resize the field if needed.
37610              * @param {Roo.form.Field} this This text field
37611              * @param {Number} width The new field width
37612              */
37613         autosize : true
37614     });
37615 };
37616
37617 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37618     /**
37619      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37620      */
37621     grow : false,
37622     /**
37623      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37624      */
37625     growMin : 30,
37626     /**
37627      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37628      */
37629     growMax : 800,
37630     /**
37631      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37632      */
37633     vtype : null,
37634     /**
37635      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37636      */
37637     maskRe : null,
37638     /**
37639      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37640      */
37641     disableKeyFilter : false,
37642     /**
37643      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37644      */
37645     allowBlank : true,
37646     /**
37647      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37648      */
37649     minLength : 0,
37650     /**
37651      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37652      */
37653     maxLength : Number.MAX_VALUE,
37654     /**
37655      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37656      */
37657     minLengthText : "The minimum length for this field is {0}",
37658     /**
37659      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37660      */
37661     maxLengthText : "The maximum length for this field is {0}",
37662     /**
37663      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37664      */
37665     selectOnFocus : false,
37666     /**
37667      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37668      */
37669     blankText : "This field is required",
37670     /**
37671      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37672      * If available, this function will be called only after the basic validators all return true, and will be passed the
37673      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37674      */
37675     validator : null,
37676     /**
37677      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37678      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37679      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37680      */
37681     regex : null,
37682     /**
37683      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37684      */
37685     regexText : "",
37686     /**
37687      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37688      */
37689     emptyText : null,
37690    
37691
37692     // private
37693     initEvents : function()
37694     {
37695         if (this.emptyText) {
37696             this.el.attr('placeholder', this.emptyText);
37697         }
37698         
37699         Roo.form.TextField.superclass.initEvents.call(this);
37700         if(this.validationEvent == 'keyup'){
37701             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37702             this.el.on('keyup', this.filterValidation, this);
37703         }
37704         else if(this.validationEvent !== false){
37705             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37706         }
37707         
37708         if(this.selectOnFocus){
37709             this.on("focus", this.preFocus, this);
37710             
37711         }
37712         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37713             this.el.on("keypress", this.filterKeys, this);
37714         }
37715         if(this.grow){
37716             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37717             this.el.on("click", this.autoSize,  this);
37718         }
37719         if(this.el.is('input[type=password]') && Roo.isSafari){
37720             this.el.on('keydown', this.SafariOnKeyDown, this);
37721         }
37722     },
37723
37724     processValue : function(value){
37725         if(this.stripCharsRe){
37726             var newValue = value.replace(this.stripCharsRe, '');
37727             if(newValue !== value){
37728                 this.setRawValue(newValue);
37729                 return newValue;
37730             }
37731         }
37732         return value;
37733     },
37734
37735     filterValidation : function(e){
37736         if(!e.isNavKeyPress()){
37737             this.validationTask.delay(this.validationDelay);
37738         }
37739     },
37740
37741     // private
37742     onKeyUp : function(e){
37743         if(!e.isNavKeyPress()){
37744             this.autoSize();
37745         }
37746     },
37747
37748     /**
37749      * Resets the current field value to the originally-loaded value and clears any validation messages.
37750      *  
37751      */
37752     reset : function(){
37753         Roo.form.TextField.superclass.reset.call(this);
37754        
37755     },
37756
37757     
37758     // private
37759     preFocus : function(){
37760         
37761         if(this.selectOnFocus){
37762             this.el.dom.select();
37763         }
37764     },
37765
37766     
37767     // private
37768     filterKeys : function(e){
37769         var k = e.getKey();
37770         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37771             return;
37772         }
37773         var c = e.getCharCode(), cc = String.fromCharCode(c);
37774         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37775             return;
37776         }
37777         if(!this.maskRe.test(cc)){
37778             e.stopEvent();
37779         }
37780     },
37781
37782     setValue : function(v){
37783         
37784         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37785         
37786         this.autoSize();
37787     },
37788
37789     /**
37790      * Validates a value according to the field's validation rules and marks the field as invalid
37791      * if the validation fails
37792      * @param {Mixed} value The value to validate
37793      * @return {Boolean} True if the value is valid, else false
37794      */
37795     validateValue : function(value){
37796         if(value.length < 1)  { // if it's blank
37797              if(this.allowBlank){
37798                 this.clearInvalid();
37799                 return true;
37800              }else{
37801                 this.markInvalid(this.blankText);
37802                 return false;
37803              }
37804         }
37805         if(value.length < this.minLength){
37806             this.markInvalid(String.format(this.minLengthText, this.minLength));
37807             return false;
37808         }
37809         if(value.length > this.maxLength){
37810             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37811             return false;
37812         }
37813         if(this.vtype){
37814             var vt = Roo.form.VTypes;
37815             if(!vt[this.vtype](value, this)){
37816                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37817                 return false;
37818             }
37819         }
37820         if(typeof this.validator == "function"){
37821             var msg = this.validator(value);
37822             if(msg !== true){
37823                 this.markInvalid(msg);
37824                 return false;
37825             }
37826         }
37827         if(this.regex && !this.regex.test(value)){
37828             this.markInvalid(this.regexText);
37829             return false;
37830         }
37831         return true;
37832     },
37833
37834     /**
37835      * Selects text in this field
37836      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37837      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37838      */
37839     selectText : function(start, end){
37840         var v = this.getRawValue();
37841         if(v.length > 0){
37842             start = start === undefined ? 0 : start;
37843             end = end === undefined ? v.length : end;
37844             var d = this.el.dom;
37845             if(d.setSelectionRange){
37846                 d.setSelectionRange(start, end);
37847             }else if(d.createTextRange){
37848                 var range = d.createTextRange();
37849                 range.moveStart("character", start);
37850                 range.moveEnd("character", v.length-end);
37851                 range.select();
37852             }
37853         }
37854     },
37855
37856     /**
37857      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37858      * This only takes effect if grow = true, and fires the autosize event.
37859      */
37860     autoSize : function(){
37861         if(!this.grow || !this.rendered){
37862             return;
37863         }
37864         if(!this.metrics){
37865             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37866         }
37867         var el = this.el;
37868         var v = el.dom.value;
37869         var d = document.createElement('div');
37870         d.appendChild(document.createTextNode(v));
37871         v = d.innerHTML;
37872         d = null;
37873         v += "&#160;";
37874         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37875         this.el.setWidth(w);
37876         this.fireEvent("autosize", this, w);
37877     },
37878     
37879     // private
37880     SafariOnKeyDown : function(event)
37881     {
37882         // this is a workaround for a password hang bug on chrome/ webkit.
37883         
37884         var isSelectAll = false;
37885         
37886         if(this.el.dom.selectionEnd > 0){
37887             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37888         }
37889         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37890             event.preventDefault();
37891             this.setValue('');
37892             return;
37893         }
37894         
37895         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37896             
37897             event.preventDefault();
37898             // this is very hacky as keydown always get's upper case.
37899             
37900             var cc = String.fromCharCode(event.getCharCode());
37901             
37902             
37903             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37904             
37905         }
37906         
37907         
37908     }
37909 });/*
37910  * Based on:
37911  * Ext JS Library 1.1.1
37912  * Copyright(c) 2006-2007, Ext JS, LLC.
37913  *
37914  * Originally Released Under LGPL - original licence link has changed is not relivant.
37915  *
37916  * Fork - LGPL
37917  * <script type="text/javascript">
37918  */
37919  
37920 /**
37921  * @class Roo.form.Hidden
37922  * @extends Roo.form.TextField
37923  * Simple Hidden element used on forms 
37924  * 
37925  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37926  * 
37927  * @constructor
37928  * Creates a new Hidden form element.
37929  * @param {Object} config Configuration options
37930  */
37931
37932
37933
37934 // easy hidden field...
37935 Roo.form.Hidden = function(config){
37936     Roo.form.Hidden.superclass.constructor.call(this, config);
37937 };
37938   
37939 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37940     fieldLabel:      '',
37941     inputType:      'hidden',
37942     width:          50,
37943     allowBlank:     true,
37944     labelSeparator: '',
37945     hidden:         true,
37946     itemCls :       'x-form-item-display-none'
37947
37948
37949 });
37950
37951
37952 /*
37953  * Based on:
37954  * Ext JS Library 1.1.1
37955  * Copyright(c) 2006-2007, Ext JS, LLC.
37956  *
37957  * Originally Released Under LGPL - original licence link has changed is not relivant.
37958  *
37959  * Fork - LGPL
37960  * <script type="text/javascript">
37961  */
37962  
37963 /**
37964  * @class Roo.form.TriggerField
37965  * @extends Roo.form.TextField
37966  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37967  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37968  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37969  * for which you can provide a custom implementation.  For example:
37970  * <pre><code>
37971 var trigger = new Roo.form.TriggerField();
37972 trigger.onTriggerClick = myTriggerFn;
37973 trigger.applyTo('my-field');
37974 </code></pre>
37975  *
37976  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37977  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37978  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37979  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37980  * @constructor
37981  * Create a new TriggerField.
37982  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37983  * to the base TextField)
37984  */
37985 Roo.form.TriggerField = function(config){
37986     this.mimicing = false;
37987     Roo.form.TriggerField.superclass.constructor.call(this, config);
37988 };
37989
37990 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37991     /**
37992      * @cfg {String} triggerClass A CSS class to apply to the trigger
37993      */
37994     /**
37995      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37996      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37997      */
37998     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
37999     /**
38000      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38001      */
38002     hideTrigger:false,
38003
38004     /** @cfg {Boolean} grow @hide */
38005     /** @cfg {Number} growMin @hide */
38006     /** @cfg {Number} growMax @hide */
38007
38008     /**
38009      * @hide 
38010      * @method
38011      */
38012     autoSize: Roo.emptyFn,
38013     // private
38014     monitorTab : true,
38015     // private
38016     deferHeight : true,
38017
38018     
38019     actionMode : 'wrap',
38020     // private
38021     onResize : function(w, h){
38022         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38023         if(typeof w == 'number'){
38024             var x = w - this.trigger.getWidth();
38025             this.el.setWidth(this.adjustWidth('input', x));
38026             this.trigger.setStyle('left', x+'px');
38027         }
38028     },
38029
38030     // private
38031     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38032
38033     // private
38034     getResizeEl : function(){
38035         return this.wrap;
38036     },
38037
38038     // private
38039     getPositionEl : function(){
38040         return this.wrap;
38041     },
38042
38043     // private
38044     alignErrorIcon : function(){
38045         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38046     },
38047
38048     // private
38049     onRender : function(ct, position){
38050         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38051         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38052         this.trigger = this.wrap.createChild(this.triggerConfig ||
38053                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38054         if(this.hideTrigger){
38055             this.trigger.setDisplayed(false);
38056         }
38057         this.initTrigger();
38058         if(!this.width){
38059             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38060         }
38061     },
38062
38063     // private
38064     initTrigger : function(){
38065         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38066         this.trigger.addClassOnOver('x-form-trigger-over');
38067         this.trigger.addClassOnClick('x-form-trigger-click');
38068     },
38069
38070     // private
38071     onDestroy : function(){
38072         if(this.trigger){
38073             this.trigger.removeAllListeners();
38074             this.trigger.remove();
38075         }
38076         if(this.wrap){
38077             this.wrap.remove();
38078         }
38079         Roo.form.TriggerField.superclass.onDestroy.call(this);
38080     },
38081
38082     // private
38083     onFocus : function(){
38084         Roo.form.TriggerField.superclass.onFocus.call(this);
38085         if(!this.mimicing){
38086             this.wrap.addClass('x-trigger-wrap-focus');
38087             this.mimicing = true;
38088             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38089             if(this.monitorTab){
38090                 this.el.on("keydown", this.checkTab, this);
38091             }
38092         }
38093     },
38094
38095     // private
38096     checkTab : function(e){
38097         if(e.getKey() == e.TAB){
38098             this.triggerBlur();
38099         }
38100     },
38101
38102     // private
38103     onBlur : function(){
38104         // do nothing
38105     },
38106
38107     // private
38108     mimicBlur : function(e, t){
38109         if(!this.wrap.contains(t) && this.validateBlur()){
38110             this.triggerBlur();
38111         }
38112     },
38113
38114     // private
38115     triggerBlur : function(){
38116         this.mimicing = false;
38117         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38118         if(this.monitorTab){
38119             this.el.un("keydown", this.checkTab, this);
38120         }
38121         this.wrap.removeClass('x-trigger-wrap-focus');
38122         Roo.form.TriggerField.superclass.onBlur.call(this);
38123     },
38124
38125     // private
38126     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38127     validateBlur : function(e, t){
38128         return true;
38129     },
38130
38131     // private
38132     onDisable : function(){
38133         Roo.form.TriggerField.superclass.onDisable.call(this);
38134         if(this.wrap){
38135             this.wrap.addClass('x-item-disabled');
38136         }
38137     },
38138
38139     // private
38140     onEnable : function(){
38141         Roo.form.TriggerField.superclass.onEnable.call(this);
38142         if(this.wrap){
38143             this.wrap.removeClass('x-item-disabled');
38144         }
38145     },
38146
38147     // private
38148     onShow : function(){
38149         var ae = this.getActionEl();
38150         
38151         if(ae){
38152             ae.dom.style.display = '';
38153             ae.dom.style.visibility = 'visible';
38154         }
38155     },
38156
38157     // private
38158     
38159     onHide : function(){
38160         var ae = this.getActionEl();
38161         ae.dom.style.display = 'none';
38162     },
38163
38164     /**
38165      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38166      * by an implementing function.
38167      * @method
38168      * @param {EventObject} e
38169      */
38170     onTriggerClick : Roo.emptyFn
38171 });
38172
38173 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38174 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38175 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38176 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38177     initComponent : function(){
38178         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38179
38180         this.triggerConfig = {
38181             tag:'span', cls:'x-form-twin-triggers', cn:[
38182             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38183             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38184         ]};
38185     },
38186
38187     getTrigger : function(index){
38188         return this.triggers[index];
38189     },
38190
38191     initTrigger : function(){
38192         var ts = this.trigger.select('.x-form-trigger', true);
38193         this.wrap.setStyle('overflow', 'hidden');
38194         var triggerField = this;
38195         ts.each(function(t, all, index){
38196             t.hide = function(){
38197                 var w = triggerField.wrap.getWidth();
38198                 this.dom.style.display = 'none';
38199                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38200             };
38201             t.show = function(){
38202                 var w = triggerField.wrap.getWidth();
38203                 this.dom.style.display = '';
38204                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38205             };
38206             var triggerIndex = 'Trigger'+(index+1);
38207
38208             if(this['hide'+triggerIndex]){
38209                 t.dom.style.display = 'none';
38210             }
38211             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38212             t.addClassOnOver('x-form-trigger-over');
38213             t.addClassOnClick('x-form-trigger-click');
38214         }, this);
38215         this.triggers = ts.elements;
38216     },
38217
38218     onTrigger1Click : Roo.emptyFn,
38219     onTrigger2Click : Roo.emptyFn
38220 });/*
38221  * Based on:
38222  * Ext JS Library 1.1.1
38223  * Copyright(c) 2006-2007, Ext JS, LLC.
38224  *
38225  * Originally Released Under LGPL - original licence link has changed is not relivant.
38226  *
38227  * Fork - LGPL
38228  * <script type="text/javascript">
38229  */
38230  
38231 /**
38232  * @class Roo.form.TextArea
38233  * @extends Roo.form.TextField
38234  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38235  * support for auto-sizing.
38236  * @constructor
38237  * Creates a new TextArea
38238  * @param {Object} config Configuration options
38239  */
38240 Roo.form.TextArea = function(config){
38241     Roo.form.TextArea.superclass.constructor.call(this, config);
38242     // these are provided exchanges for backwards compat
38243     // minHeight/maxHeight were replaced by growMin/growMax to be
38244     // compatible with TextField growing config values
38245     if(this.minHeight !== undefined){
38246         this.growMin = this.minHeight;
38247     }
38248     if(this.maxHeight !== undefined){
38249         this.growMax = this.maxHeight;
38250     }
38251 };
38252
38253 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38254     /**
38255      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38256      */
38257     growMin : 60,
38258     /**
38259      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38260      */
38261     growMax: 1000,
38262     /**
38263      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38264      * in the field (equivalent to setting overflow: hidden, defaults to false)
38265      */
38266     preventScrollbars: false,
38267     /**
38268      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38269      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38270      */
38271
38272     // private
38273     onRender : function(ct, position){
38274         if(!this.el){
38275             this.defaultAutoCreate = {
38276                 tag: "textarea",
38277                 style:"width:300px;height:60px;",
38278                 autocomplete: "new-password"
38279             };
38280         }
38281         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38282         if(this.grow){
38283             this.textSizeEl = Roo.DomHelper.append(document.body, {
38284                 tag: "pre", cls: "x-form-grow-sizer"
38285             });
38286             if(this.preventScrollbars){
38287                 this.el.setStyle("overflow", "hidden");
38288             }
38289             this.el.setHeight(this.growMin);
38290         }
38291     },
38292
38293     onDestroy : function(){
38294         if(this.textSizeEl){
38295             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38296         }
38297         Roo.form.TextArea.superclass.onDestroy.call(this);
38298     },
38299
38300     // private
38301     onKeyUp : function(e){
38302         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38303             this.autoSize();
38304         }
38305     },
38306
38307     /**
38308      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38309      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38310      */
38311     autoSize : function(){
38312         if(!this.grow || !this.textSizeEl){
38313             return;
38314         }
38315         var el = this.el;
38316         var v = el.dom.value;
38317         var ts = this.textSizeEl;
38318
38319         ts.innerHTML = '';
38320         ts.appendChild(document.createTextNode(v));
38321         v = ts.innerHTML;
38322
38323         Roo.fly(ts).setWidth(this.el.getWidth());
38324         if(v.length < 1){
38325             v = "&#160;&#160;";
38326         }else{
38327             if(Roo.isIE){
38328                 v = v.replace(/\n/g, '<p>&#160;</p>');
38329             }
38330             v += "&#160;\n&#160;";
38331         }
38332         ts.innerHTML = v;
38333         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38334         if(h != this.lastHeight){
38335             this.lastHeight = h;
38336             this.el.setHeight(h);
38337             this.fireEvent("autosize", this, h);
38338         }
38339     }
38340 });/*
38341  * Based on:
38342  * Ext JS Library 1.1.1
38343  * Copyright(c) 2006-2007, Ext JS, LLC.
38344  *
38345  * Originally Released Under LGPL - original licence link has changed is not relivant.
38346  *
38347  * Fork - LGPL
38348  * <script type="text/javascript">
38349  */
38350  
38351
38352 /**
38353  * @class Roo.form.NumberField
38354  * @extends Roo.form.TextField
38355  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38356  * @constructor
38357  * Creates a new NumberField
38358  * @param {Object} config Configuration options
38359  */
38360 Roo.form.NumberField = function(config){
38361     Roo.form.NumberField.superclass.constructor.call(this, config);
38362 };
38363
38364 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38365     /**
38366      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38367      */
38368     fieldClass: "x-form-field x-form-num-field",
38369     /**
38370      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38371      */
38372     allowDecimals : true,
38373     /**
38374      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38375      */
38376     decimalSeparator : ".",
38377     /**
38378      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38379      */
38380     decimalPrecision : 2,
38381     /**
38382      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38383      */
38384     allowNegative : true,
38385     /**
38386      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38387      */
38388     minValue : Number.NEGATIVE_INFINITY,
38389     /**
38390      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38391      */
38392     maxValue : Number.MAX_VALUE,
38393     /**
38394      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38395      */
38396     minText : "The minimum value for this field is {0}",
38397     /**
38398      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38399      */
38400     maxText : "The maximum value for this field is {0}",
38401     /**
38402      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38403      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38404      */
38405     nanText : "{0} is not a valid number",
38406
38407     // private
38408     initEvents : function(){
38409         Roo.form.NumberField.superclass.initEvents.call(this);
38410         var allowed = "0123456789";
38411         if(this.allowDecimals){
38412             allowed += this.decimalSeparator;
38413         }
38414         if(this.allowNegative){
38415             allowed += "-";
38416         }
38417         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38418         var keyPress = function(e){
38419             var k = e.getKey();
38420             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38421                 return;
38422             }
38423             var c = e.getCharCode();
38424             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38425                 e.stopEvent();
38426             }
38427         };
38428         this.el.on("keypress", keyPress, this);
38429     },
38430
38431     // private
38432     validateValue : function(value){
38433         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38434             return false;
38435         }
38436         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38437              return true;
38438         }
38439         var num = this.parseValue(value);
38440         if(isNaN(num)){
38441             this.markInvalid(String.format(this.nanText, value));
38442             return false;
38443         }
38444         if(num < this.minValue){
38445             this.markInvalid(String.format(this.minText, this.minValue));
38446             return false;
38447         }
38448         if(num > this.maxValue){
38449             this.markInvalid(String.format(this.maxText, this.maxValue));
38450             return false;
38451         }
38452         return true;
38453     },
38454
38455     getValue : function(){
38456         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38457     },
38458
38459     // private
38460     parseValue : function(value){
38461         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38462         return isNaN(value) ? '' : value;
38463     },
38464
38465     // private
38466     fixPrecision : function(value){
38467         var nan = isNaN(value);
38468         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38469             return nan ? '' : value;
38470         }
38471         return parseFloat(value).toFixed(this.decimalPrecision);
38472     },
38473
38474     setValue : function(v){
38475         v = this.fixPrecision(v);
38476         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38477     },
38478
38479     // private
38480     decimalPrecisionFcn : function(v){
38481         return Math.floor(v);
38482     },
38483
38484     beforeBlur : function(){
38485         var v = this.parseValue(this.getRawValue());
38486         if(v){
38487             this.setValue(v);
38488         }
38489     }
38490 });/*
38491  * Based on:
38492  * Ext JS Library 1.1.1
38493  * Copyright(c) 2006-2007, Ext JS, LLC.
38494  *
38495  * Originally Released Under LGPL - original licence link has changed is not relivant.
38496  *
38497  * Fork - LGPL
38498  * <script type="text/javascript">
38499  */
38500  
38501 /**
38502  * @class Roo.form.DateField
38503  * @extends Roo.form.TriggerField
38504  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38505 * @constructor
38506 * Create a new DateField
38507 * @param {Object} config
38508  */
38509 Roo.form.DateField = function(config){
38510     Roo.form.DateField.superclass.constructor.call(this, config);
38511     
38512       this.addEvents({
38513          
38514         /**
38515          * @event select
38516          * Fires when a date is selected
38517              * @param {Roo.form.DateField} combo This combo box
38518              * @param {Date} date The date selected
38519              */
38520         'select' : true
38521          
38522     });
38523     
38524     
38525     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38526     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38527     this.ddMatch = null;
38528     if(this.disabledDates){
38529         var dd = this.disabledDates;
38530         var re = "(?:";
38531         for(var i = 0; i < dd.length; i++){
38532             re += dd[i];
38533             if(i != dd.length-1) re += "|";
38534         }
38535         this.ddMatch = new RegExp(re + ")");
38536     }
38537 };
38538
38539 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38540     /**
38541      * @cfg {String} format
38542      * The default date format string which can be overriden for localization support.  The format must be
38543      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38544      */
38545     format : "m/d/y",
38546     /**
38547      * @cfg {String} altFormats
38548      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38549      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38550      */
38551     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38552     /**
38553      * @cfg {Array} disabledDays
38554      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38555      */
38556     disabledDays : null,
38557     /**
38558      * @cfg {String} disabledDaysText
38559      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38560      */
38561     disabledDaysText : "Disabled",
38562     /**
38563      * @cfg {Array} disabledDates
38564      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38565      * expression so they are very powerful. Some examples:
38566      * <ul>
38567      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38568      * <li>["03/08", "09/16"] would disable those days for every year</li>
38569      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38570      * <li>["03/../2006"] would disable every day in March 2006</li>
38571      * <li>["^03"] would disable every day in every March</li>
38572      * </ul>
38573      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38574      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38575      */
38576     disabledDates : null,
38577     /**
38578      * @cfg {String} disabledDatesText
38579      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38580      */
38581     disabledDatesText : "Disabled",
38582     /**
38583      * @cfg {Date/String} minValue
38584      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38585      * valid format (defaults to null).
38586      */
38587     minValue : null,
38588     /**
38589      * @cfg {Date/String} maxValue
38590      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38591      * valid format (defaults to null).
38592      */
38593     maxValue : null,
38594     /**
38595      * @cfg {String} minText
38596      * The error text to display when the date in the cell is before minValue (defaults to
38597      * 'The date in this field must be after {minValue}').
38598      */
38599     minText : "The date in this field must be equal to or after {0}",
38600     /**
38601      * @cfg {String} maxText
38602      * The error text to display when the date in the cell is after maxValue (defaults to
38603      * 'The date in this field must be before {maxValue}').
38604      */
38605     maxText : "The date in this field must be equal to or before {0}",
38606     /**
38607      * @cfg {String} invalidText
38608      * The error text to display when the date in the field is invalid (defaults to
38609      * '{value} is not a valid date - it must be in the format {format}').
38610      */
38611     invalidText : "{0} is not a valid date - it must be in the format {1}",
38612     /**
38613      * @cfg {String} triggerClass
38614      * An additional CSS class used to style the trigger button.  The trigger will always get the
38615      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38616      * which displays a calendar icon).
38617      */
38618     triggerClass : 'x-form-date-trigger',
38619     
38620
38621     /**
38622      * @cfg {Boolean} useIso
38623      * if enabled, then the date field will use a hidden field to store the 
38624      * real value as iso formated date. default (false)
38625      */ 
38626     useIso : false,
38627     /**
38628      * @cfg {String/Object} autoCreate
38629      * A DomHelper element spec, or true for a default element spec (defaults to
38630      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38631      */ 
38632     // private
38633     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38634     
38635     // private
38636     hiddenField: false,
38637     
38638     onRender : function(ct, position)
38639     {
38640         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38641         if (this.useIso) {
38642             //this.el.dom.removeAttribute('name'); 
38643             Roo.log("Changing name?");
38644             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38645             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38646                     'before', true);
38647             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38648             // prevent input submission
38649             this.hiddenName = this.name;
38650         }
38651             
38652             
38653     },
38654     
38655     // private
38656     validateValue : function(value)
38657     {
38658         value = this.formatDate(value);
38659         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38660             Roo.log('super failed');
38661             return false;
38662         }
38663         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38664              return true;
38665         }
38666         var svalue = value;
38667         value = this.parseDate(value);
38668         if(!value){
38669             Roo.log('parse date failed' + svalue);
38670             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38671             return false;
38672         }
38673         var time = value.getTime();
38674         if(this.minValue && time < this.minValue.getTime()){
38675             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38676             return false;
38677         }
38678         if(this.maxValue && time > this.maxValue.getTime()){
38679             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38680             return false;
38681         }
38682         if(this.disabledDays){
38683             var day = value.getDay();
38684             for(var i = 0; i < this.disabledDays.length; i++) {
38685                 if(day === this.disabledDays[i]){
38686                     this.markInvalid(this.disabledDaysText);
38687                     return false;
38688                 }
38689             }
38690         }
38691         var fvalue = this.formatDate(value);
38692         if(this.ddMatch && this.ddMatch.test(fvalue)){
38693             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38694             return false;
38695         }
38696         return true;
38697     },
38698
38699     // private
38700     // Provides logic to override the default TriggerField.validateBlur which just returns true
38701     validateBlur : function(){
38702         return !this.menu || !this.menu.isVisible();
38703     },
38704     
38705     getName: function()
38706     {
38707         // returns hidden if it's set..
38708         if (!this.rendered) {return ''};
38709         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38710         
38711     },
38712
38713     /**
38714      * Returns the current date value of the date field.
38715      * @return {Date} The date value
38716      */
38717     getValue : function(){
38718         
38719         return  this.hiddenField ?
38720                 this.hiddenField.value :
38721                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38722     },
38723
38724     /**
38725      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38726      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38727      * (the default format used is "m/d/y").
38728      * <br />Usage:
38729      * <pre><code>
38730 //All of these calls set the same date value (May 4, 2006)
38731
38732 //Pass a date object:
38733 var dt = new Date('5/4/06');
38734 dateField.setValue(dt);
38735
38736 //Pass a date string (default format):
38737 dateField.setValue('5/4/06');
38738
38739 //Pass a date string (custom format):
38740 dateField.format = 'Y-m-d';
38741 dateField.setValue('2006-5-4');
38742 </code></pre>
38743      * @param {String/Date} date The date or valid date string
38744      */
38745     setValue : function(date){
38746         if (this.hiddenField) {
38747             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38748         }
38749         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38750         // make sure the value field is always stored as a date..
38751         this.value = this.parseDate(date);
38752         
38753         
38754     },
38755
38756     // private
38757     parseDate : function(value){
38758         if(!value || value instanceof Date){
38759             return value;
38760         }
38761         var v = Date.parseDate(value, this.format);
38762          if (!v && this.useIso) {
38763             v = Date.parseDate(value, 'Y-m-d');
38764         }
38765         if(!v && this.altFormats){
38766             if(!this.altFormatsArray){
38767                 this.altFormatsArray = this.altFormats.split("|");
38768             }
38769             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38770                 v = Date.parseDate(value, this.altFormatsArray[i]);
38771             }
38772         }
38773         return v;
38774     },
38775
38776     // private
38777     formatDate : function(date, fmt){
38778         return (!date || !(date instanceof Date)) ?
38779                date : date.dateFormat(fmt || this.format);
38780     },
38781
38782     // private
38783     menuListeners : {
38784         select: function(m, d){
38785             
38786             this.setValue(d);
38787             this.fireEvent('select', this, d);
38788         },
38789         show : function(){ // retain focus styling
38790             this.onFocus();
38791         },
38792         hide : function(){
38793             this.focus.defer(10, this);
38794             var ml = this.menuListeners;
38795             this.menu.un("select", ml.select,  this);
38796             this.menu.un("show", ml.show,  this);
38797             this.menu.un("hide", ml.hide,  this);
38798         }
38799     },
38800
38801     // private
38802     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38803     onTriggerClick : function(){
38804         if(this.disabled){
38805             return;
38806         }
38807         if(this.menu == null){
38808             this.menu = new Roo.menu.DateMenu();
38809         }
38810         Roo.apply(this.menu.picker,  {
38811             showClear: this.allowBlank,
38812             minDate : this.minValue,
38813             maxDate : this.maxValue,
38814             disabledDatesRE : this.ddMatch,
38815             disabledDatesText : this.disabledDatesText,
38816             disabledDays : this.disabledDays,
38817             disabledDaysText : this.disabledDaysText,
38818             format : this.useIso ? 'Y-m-d' : this.format,
38819             minText : String.format(this.minText, this.formatDate(this.minValue)),
38820             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38821         });
38822         this.menu.on(Roo.apply({}, this.menuListeners, {
38823             scope:this
38824         }));
38825         this.menu.picker.setValue(this.getValue() || new Date());
38826         this.menu.show(this.el, "tl-bl?");
38827     },
38828
38829     beforeBlur : function(){
38830         var v = this.parseDate(this.getRawValue());
38831         if(v){
38832             this.setValue(v);
38833         }
38834     },
38835
38836     /*@
38837      * overide
38838      * 
38839      */
38840     isDirty : function() {
38841         if(this.disabled) {
38842             return false;
38843         }
38844         
38845         if(typeof(this.startValue) === 'undefined'){
38846             return false;
38847         }
38848         
38849         return String(this.getValue()) !== String(this.startValue);
38850         
38851     }
38852 });/*
38853  * Based on:
38854  * Ext JS Library 1.1.1
38855  * Copyright(c) 2006-2007, Ext JS, LLC.
38856  *
38857  * Originally Released Under LGPL - original licence link has changed is not relivant.
38858  *
38859  * Fork - LGPL
38860  * <script type="text/javascript">
38861  */
38862  
38863 /**
38864  * @class Roo.form.MonthField
38865  * @extends Roo.form.TriggerField
38866  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38867 * @constructor
38868 * Create a new MonthField
38869 * @param {Object} config
38870  */
38871 Roo.form.MonthField = function(config){
38872     
38873     Roo.form.MonthField.superclass.constructor.call(this, config);
38874     
38875       this.addEvents({
38876          
38877         /**
38878          * @event select
38879          * Fires when a date is selected
38880              * @param {Roo.form.MonthFieeld} combo This combo box
38881              * @param {Date} date The date selected
38882              */
38883         'select' : true
38884          
38885     });
38886     
38887     
38888     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38889     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38890     this.ddMatch = null;
38891     if(this.disabledDates){
38892         var dd = this.disabledDates;
38893         var re = "(?:";
38894         for(var i = 0; i < dd.length; i++){
38895             re += dd[i];
38896             if(i != dd.length-1) re += "|";
38897         }
38898         this.ddMatch = new RegExp(re + ")");
38899     }
38900 };
38901
38902 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38903     /**
38904      * @cfg {String} format
38905      * The default date format string which can be overriden for localization support.  The format must be
38906      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38907      */
38908     format : "M Y",
38909     /**
38910      * @cfg {String} altFormats
38911      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38912      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38913      */
38914     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38915     /**
38916      * @cfg {Array} disabledDays
38917      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38918      */
38919     disabledDays : [0,1,2,3,4,5,6],
38920     /**
38921      * @cfg {String} disabledDaysText
38922      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38923      */
38924     disabledDaysText : "Disabled",
38925     /**
38926      * @cfg {Array} disabledDates
38927      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38928      * expression so they are very powerful. Some examples:
38929      * <ul>
38930      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38931      * <li>["03/08", "09/16"] would disable those days for every year</li>
38932      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38933      * <li>["03/../2006"] would disable every day in March 2006</li>
38934      * <li>["^03"] would disable every day in every March</li>
38935      * </ul>
38936      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38937      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38938      */
38939     disabledDates : null,
38940     /**
38941      * @cfg {String} disabledDatesText
38942      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38943      */
38944     disabledDatesText : "Disabled",
38945     /**
38946      * @cfg {Date/String} minValue
38947      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38948      * valid format (defaults to null).
38949      */
38950     minValue : null,
38951     /**
38952      * @cfg {Date/String} maxValue
38953      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38954      * valid format (defaults to null).
38955      */
38956     maxValue : null,
38957     /**
38958      * @cfg {String} minText
38959      * The error text to display when the date in the cell is before minValue (defaults to
38960      * 'The date in this field must be after {minValue}').
38961      */
38962     minText : "The date in this field must be equal to or after {0}",
38963     /**
38964      * @cfg {String} maxTextf
38965      * The error text to display when the date in the cell is after maxValue (defaults to
38966      * 'The date in this field must be before {maxValue}').
38967      */
38968     maxText : "The date in this field must be equal to or before {0}",
38969     /**
38970      * @cfg {String} invalidText
38971      * The error text to display when the date in the field is invalid (defaults to
38972      * '{value} is not a valid date - it must be in the format {format}').
38973      */
38974     invalidText : "{0} is not a valid date - it must be in the format {1}",
38975     /**
38976      * @cfg {String} triggerClass
38977      * An additional CSS class used to style the trigger button.  The trigger will always get the
38978      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38979      * which displays a calendar icon).
38980      */
38981     triggerClass : 'x-form-date-trigger',
38982     
38983
38984     /**
38985      * @cfg {Boolean} useIso
38986      * if enabled, then the date field will use a hidden field to store the 
38987      * real value as iso formated date. default (true)
38988      */ 
38989     useIso : true,
38990     /**
38991      * @cfg {String/Object} autoCreate
38992      * A DomHelper element spec, or true for a default element spec (defaults to
38993      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38994      */ 
38995     // private
38996     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
38997     
38998     // private
38999     hiddenField: false,
39000     
39001     hideMonthPicker : false,
39002     
39003     onRender : function(ct, position)
39004     {
39005         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39006         if (this.useIso) {
39007             this.el.dom.removeAttribute('name'); 
39008             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39009                     'before', true);
39010             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39011             // prevent input submission
39012             this.hiddenName = this.name;
39013         }
39014             
39015             
39016     },
39017     
39018     // private
39019     validateValue : function(value)
39020     {
39021         value = this.formatDate(value);
39022         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39023             return false;
39024         }
39025         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39026              return true;
39027         }
39028         var svalue = value;
39029         value = this.parseDate(value);
39030         if(!value){
39031             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39032             return false;
39033         }
39034         var time = value.getTime();
39035         if(this.minValue && time < this.minValue.getTime()){
39036             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39037             return false;
39038         }
39039         if(this.maxValue && time > this.maxValue.getTime()){
39040             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39041             return false;
39042         }
39043         /*if(this.disabledDays){
39044             var day = value.getDay();
39045             for(var i = 0; i < this.disabledDays.length; i++) {
39046                 if(day === this.disabledDays[i]){
39047                     this.markInvalid(this.disabledDaysText);
39048                     return false;
39049                 }
39050             }
39051         }
39052         */
39053         var fvalue = this.formatDate(value);
39054         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39055             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39056             return false;
39057         }
39058         */
39059         return true;
39060     },
39061
39062     // private
39063     // Provides logic to override the default TriggerField.validateBlur which just returns true
39064     validateBlur : function(){
39065         return !this.menu || !this.menu.isVisible();
39066     },
39067
39068     /**
39069      * Returns the current date value of the date field.
39070      * @return {Date} The date value
39071      */
39072     getValue : function(){
39073         
39074         
39075         
39076         return  this.hiddenField ?
39077                 this.hiddenField.value :
39078                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39079     },
39080
39081     /**
39082      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39083      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39084      * (the default format used is "m/d/y").
39085      * <br />Usage:
39086      * <pre><code>
39087 //All of these calls set the same date value (May 4, 2006)
39088
39089 //Pass a date object:
39090 var dt = new Date('5/4/06');
39091 monthField.setValue(dt);
39092
39093 //Pass a date string (default format):
39094 monthField.setValue('5/4/06');
39095
39096 //Pass a date string (custom format):
39097 monthField.format = 'Y-m-d';
39098 monthField.setValue('2006-5-4');
39099 </code></pre>
39100      * @param {String/Date} date The date or valid date string
39101      */
39102     setValue : function(date){
39103         Roo.log('month setValue' + date);
39104         // can only be first of month..
39105         
39106         var val = this.parseDate(date);
39107         
39108         if (this.hiddenField) {
39109             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39110         }
39111         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39112         this.value = this.parseDate(date);
39113     },
39114
39115     // private
39116     parseDate : function(value){
39117         if(!value || value instanceof Date){
39118             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39119             return value;
39120         }
39121         var v = Date.parseDate(value, this.format);
39122         if (!v && this.useIso) {
39123             v = Date.parseDate(value, 'Y-m-d');
39124         }
39125         if (v) {
39126             // 
39127             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39128         }
39129         
39130         
39131         if(!v && this.altFormats){
39132             if(!this.altFormatsArray){
39133                 this.altFormatsArray = this.altFormats.split("|");
39134             }
39135             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39136                 v = Date.parseDate(value, this.altFormatsArray[i]);
39137             }
39138         }
39139         return v;
39140     },
39141
39142     // private
39143     formatDate : function(date, fmt){
39144         return (!date || !(date instanceof Date)) ?
39145                date : date.dateFormat(fmt || this.format);
39146     },
39147
39148     // private
39149     menuListeners : {
39150         select: function(m, d){
39151             this.setValue(d);
39152             this.fireEvent('select', this, d);
39153         },
39154         show : function(){ // retain focus styling
39155             this.onFocus();
39156         },
39157         hide : function(){
39158             this.focus.defer(10, this);
39159             var ml = this.menuListeners;
39160             this.menu.un("select", ml.select,  this);
39161             this.menu.un("show", ml.show,  this);
39162             this.menu.un("hide", ml.hide,  this);
39163         }
39164     },
39165     // private
39166     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39167     onTriggerClick : function(){
39168         if(this.disabled){
39169             return;
39170         }
39171         if(this.menu == null){
39172             this.menu = new Roo.menu.DateMenu();
39173            
39174         }
39175         
39176         Roo.apply(this.menu.picker,  {
39177             
39178             showClear: this.allowBlank,
39179             minDate : this.minValue,
39180             maxDate : this.maxValue,
39181             disabledDatesRE : this.ddMatch,
39182             disabledDatesText : this.disabledDatesText,
39183             
39184             format : this.useIso ? 'Y-m-d' : this.format,
39185             minText : String.format(this.minText, this.formatDate(this.minValue)),
39186             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39187             
39188         });
39189          this.menu.on(Roo.apply({}, this.menuListeners, {
39190             scope:this
39191         }));
39192        
39193         
39194         var m = this.menu;
39195         var p = m.picker;
39196         
39197         // hide month picker get's called when we called by 'before hide';
39198         
39199         var ignorehide = true;
39200         p.hideMonthPicker  = function(disableAnim){
39201             if (ignorehide) {
39202                 return;
39203             }
39204              if(this.monthPicker){
39205                 Roo.log("hideMonthPicker called");
39206                 if(disableAnim === true){
39207                     this.monthPicker.hide();
39208                 }else{
39209                     this.monthPicker.slideOut('t', {duration:.2});
39210                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39211                     p.fireEvent("select", this, this.value);
39212                     m.hide();
39213                 }
39214             }
39215         }
39216         
39217         Roo.log('picker set value');
39218         Roo.log(this.getValue());
39219         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39220         m.show(this.el, 'tl-bl?');
39221         ignorehide  = false;
39222         // this will trigger hideMonthPicker..
39223         
39224         
39225         // hidden the day picker
39226         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39227         
39228         
39229         
39230       
39231         
39232         p.showMonthPicker.defer(100, p);
39233     
39234         
39235        
39236     },
39237
39238     beforeBlur : function(){
39239         var v = this.parseDate(this.getRawValue());
39240         if(v){
39241             this.setValue(v);
39242         }
39243     }
39244
39245     /** @cfg {Boolean} grow @hide */
39246     /** @cfg {Number} growMin @hide */
39247     /** @cfg {Number} growMax @hide */
39248     /**
39249      * @hide
39250      * @method autoSize
39251      */
39252 });/*
39253  * Based on:
39254  * Ext JS Library 1.1.1
39255  * Copyright(c) 2006-2007, Ext JS, LLC.
39256  *
39257  * Originally Released Under LGPL - original licence link has changed is not relivant.
39258  *
39259  * Fork - LGPL
39260  * <script type="text/javascript">
39261  */
39262  
39263
39264 /**
39265  * @class Roo.form.ComboBox
39266  * @extends Roo.form.TriggerField
39267  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39268  * @constructor
39269  * Create a new ComboBox.
39270  * @param {Object} config Configuration options
39271  */
39272 Roo.form.ComboBox = function(config){
39273     Roo.form.ComboBox.superclass.constructor.call(this, config);
39274     this.addEvents({
39275         /**
39276          * @event expand
39277          * Fires when the dropdown list is expanded
39278              * @param {Roo.form.ComboBox} combo This combo box
39279              */
39280         'expand' : true,
39281         /**
39282          * @event collapse
39283          * Fires when the dropdown list is collapsed
39284              * @param {Roo.form.ComboBox} combo This combo box
39285              */
39286         'collapse' : true,
39287         /**
39288          * @event beforeselect
39289          * Fires before a list item is selected. Return false to cancel the selection.
39290              * @param {Roo.form.ComboBox} combo This combo box
39291              * @param {Roo.data.Record} record The data record returned from the underlying store
39292              * @param {Number} index The index of the selected item in the dropdown list
39293              */
39294         'beforeselect' : true,
39295         /**
39296          * @event select
39297          * Fires when a list item is selected
39298              * @param {Roo.form.ComboBox} combo This combo box
39299              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39300              * @param {Number} index The index of the selected item in the dropdown list
39301              */
39302         'select' : true,
39303         /**
39304          * @event beforequery
39305          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39306          * The event object passed has these properties:
39307              * @param {Roo.form.ComboBox} combo This combo box
39308              * @param {String} query The query
39309              * @param {Boolean} forceAll true to force "all" query
39310              * @param {Boolean} cancel true to cancel the query
39311              * @param {Object} e The query event object
39312              */
39313         'beforequery': true,
39314          /**
39315          * @event add
39316          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39317              * @param {Roo.form.ComboBox} combo This combo box
39318              */
39319         'add' : true,
39320         /**
39321          * @event edit
39322          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39323              * @param {Roo.form.ComboBox} combo This combo box
39324              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39325              */
39326         'edit' : true
39327         
39328         
39329     });
39330     if(this.transform){
39331         this.allowDomMove = false;
39332         var s = Roo.getDom(this.transform);
39333         if(!this.hiddenName){
39334             this.hiddenName = s.name;
39335         }
39336         if(!this.store){
39337             this.mode = 'local';
39338             var d = [], opts = s.options;
39339             for(var i = 0, len = opts.length;i < len; i++){
39340                 var o = opts[i];
39341                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39342                 if(o.selected) {
39343                     this.value = value;
39344                 }
39345                 d.push([value, o.text]);
39346             }
39347             this.store = new Roo.data.SimpleStore({
39348                 'id': 0,
39349                 fields: ['value', 'text'],
39350                 data : d
39351             });
39352             this.valueField = 'value';
39353             this.displayField = 'text';
39354         }
39355         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39356         if(!this.lazyRender){
39357             this.target = true;
39358             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39359             s.parentNode.removeChild(s); // remove it
39360             this.render(this.el.parentNode);
39361         }else{
39362             s.parentNode.removeChild(s); // remove it
39363         }
39364
39365     }
39366     if (this.store) {
39367         this.store = Roo.factory(this.store, Roo.data);
39368     }
39369     
39370     this.selectedIndex = -1;
39371     if(this.mode == 'local'){
39372         if(config.queryDelay === undefined){
39373             this.queryDelay = 10;
39374         }
39375         if(config.minChars === undefined){
39376             this.minChars = 0;
39377         }
39378     }
39379 };
39380
39381 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39382     /**
39383      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39384      */
39385     /**
39386      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39387      * rendering into an Roo.Editor, defaults to false)
39388      */
39389     /**
39390      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39391      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39392      */
39393     /**
39394      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39395      */
39396     /**
39397      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39398      * the dropdown list (defaults to undefined, with no header element)
39399      */
39400
39401      /**
39402      * @cfg {String/Roo.Template} tpl The template to use to render the output
39403      */
39404      
39405     // private
39406     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39407     /**
39408      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39409      */
39410     listWidth: undefined,
39411     /**
39412      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39413      * mode = 'remote' or 'text' if mode = 'local')
39414      */
39415     displayField: undefined,
39416     /**
39417      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39418      * mode = 'remote' or 'value' if mode = 'local'). 
39419      * Note: use of a valueField requires the user make a selection
39420      * in order for a value to be mapped.
39421      */
39422     valueField: undefined,
39423     
39424     
39425     /**
39426      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39427      * field's data value (defaults to the underlying DOM element's name)
39428      */
39429     hiddenName: undefined,
39430     /**
39431      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39432      */
39433     listClass: '',
39434     /**
39435      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39436      */
39437     selectedClass: 'x-combo-selected',
39438     /**
39439      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39440      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39441      * which displays a downward arrow icon).
39442      */
39443     triggerClass : 'x-form-arrow-trigger',
39444     /**
39445      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39446      */
39447     shadow:'sides',
39448     /**
39449      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39450      * anchor positions (defaults to 'tl-bl')
39451      */
39452     listAlign: 'tl-bl?',
39453     /**
39454      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39455      */
39456     maxHeight: 300,
39457     /**
39458      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39459      * query specified by the allQuery config option (defaults to 'query')
39460      */
39461     triggerAction: 'query',
39462     /**
39463      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39464      * (defaults to 4, does not apply if editable = false)
39465      */
39466     minChars : 4,
39467     /**
39468      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39469      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39470      */
39471     typeAhead: false,
39472     /**
39473      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39474      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39475      */
39476     queryDelay: 500,
39477     /**
39478      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39479      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39480      */
39481     pageSize: 0,
39482     /**
39483      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39484      * when editable = true (defaults to false)
39485      */
39486     selectOnFocus:false,
39487     /**
39488      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39489      */
39490     queryParam: 'query',
39491     /**
39492      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39493      * when mode = 'remote' (defaults to 'Loading...')
39494      */
39495     loadingText: 'Loading...',
39496     /**
39497      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39498      */
39499     resizable: false,
39500     /**
39501      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39502      */
39503     handleHeight : 8,
39504     /**
39505      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39506      * traditional select (defaults to true)
39507      */
39508     editable: true,
39509     /**
39510      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39511      */
39512     allQuery: '',
39513     /**
39514      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39515      */
39516     mode: 'remote',
39517     /**
39518      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39519      * listWidth has a higher value)
39520      */
39521     minListWidth : 70,
39522     /**
39523      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39524      * allow the user to set arbitrary text into the field (defaults to false)
39525      */
39526     forceSelection:false,
39527     /**
39528      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39529      * if typeAhead = true (defaults to 250)
39530      */
39531     typeAheadDelay : 250,
39532     /**
39533      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39534      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39535      */
39536     valueNotFoundText : undefined,
39537     /**
39538      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39539      */
39540     blockFocus : false,
39541     
39542     /**
39543      * @cfg {Boolean} disableClear Disable showing of clear button.
39544      */
39545     disableClear : false,
39546     /**
39547      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39548      */
39549     alwaysQuery : false,
39550     
39551     //private
39552     addicon : false,
39553     editicon: false,
39554     
39555     // element that contains real text value.. (when hidden is used..)
39556      
39557     // private
39558     onRender : function(ct, position){
39559         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39560         if(this.hiddenName){
39561             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39562                     'before', true);
39563             this.hiddenField.value =
39564                 this.hiddenValue !== undefined ? this.hiddenValue :
39565                 this.value !== undefined ? this.value : '';
39566
39567             // prevent input submission
39568             this.el.dom.removeAttribute('name');
39569              
39570              
39571         }
39572         if(Roo.isGecko){
39573             this.el.dom.setAttribute('autocomplete', 'off');
39574         }
39575
39576         var cls = 'x-combo-list';
39577
39578         this.list = new Roo.Layer({
39579             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39580         });
39581
39582         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39583         this.list.setWidth(lw);
39584         this.list.swallowEvent('mousewheel');
39585         this.assetHeight = 0;
39586
39587         if(this.title){
39588             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39589             this.assetHeight += this.header.getHeight();
39590         }
39591
39592         this.innerList = this.list.createChild({cls:cls+'-inner'});
39593         this.innerList.on('mouseover', this.onViewOver, this);
39594         this.innerList.on('mousemove', this.onViewMove, this);
39595         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39596         
39597         if(this.allowBlank && !this.pageSize && !this.disableClear){
39598             this.footer = this.list.createChild({cls:cls+'-ft'});
39599             this.pageTb = new Roo.Toolbar(this.footer);
39600            
39601         }
39602         if(this.pageSize){
39603             this.footer = this.list.createChild({cls:cls+'-ft'});
39604             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39605                     {pageSize: this.pageSize});
39606             
39607         }
39608         
39609         if (this.pageTb && this.allowBlank && !this.disableClear) {
39610             var _this = this;
39611             this.pageTb.add(new Roo.Toolbar.Fill(), {
39612                 cls: 'x-btn-icon x-btn-clear',
39613                 text: '&#160;',
39614                 handler: function()
39615                 {
39616                     _this.collapse();
39617                     _this.clearValue();
39618                     _this.onSelect(false, -1);
39619                 }
39620             });
39621         }
39622         if (this.footer) {
39623             this.assetHeight += this.footer.getHeight();
39624         }
39625         
39626
39627         if(!this.tpl){
39628             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39629         }
39630
39631         this.view = new Roo.View(this.innerList, this.tpl, {
39632             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39633         });
39634
39635         this.view.on('click', this.onViewClick, this);
39636
39637         this.store.on('beforeload', this.onBeforeLoad, this);
39638         this.store.on('load', this.onLoad, this);
39639         this.store.on('loadexception', this.onLoadException, this);
39640
39641         if(this.resizable){
39642             this.resizer = new Roo.Resizable(this.list,  {
39643                pinned:true, handles:'se'
39644             });
39645             this.resizer.on('resize', function(r, w, h){
39646                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39647                 this.listWidth = w;
39648                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39649                 this.restrictHeight();
39650             }, this);
39651             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39652         }
39653         if(!this.editable){
39654             this.editable = true;
39655             this.setEditable(false);
39656         }  
39657         
39658         
39659         if (typeof(this.events.add.listeners) != 'undefined') {
39660             
39661             this.addicon = this.wrap.createChild(
39662                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39663        
39664             this.addicon.on('click', function(e) {
39665                 this.fireEvent('add', this);
39666             }, this);
39667         }
39668         if (typeof(this.events.edit.listeners) != 'undefined') {
39669             
39670             this.editicon = this.wrap.createChild(
39671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39672             if (this.addicon) {
39673                 this.editicon.setStyle('margin-left', '40px');
39674             }
39675             this.editicon.on('click', function(e) {
39676                 
39677                 // we fire even  if inothing is selected..
39678                 this.fireEvent('edit', this, this.lastData );
39679                 
39680             }, this);
39681         }
39682         
39683         
39684         
39685     },
39686
39687     // private
39688     initEvents : function(){
39689         Roo.form.ComboBox.superclass.initEvents.call(this);
39690
39691         this.keyNav = new Roo.KeyNav(this.el, {
39692             "up" : function(e){
39693                 this.inKeyMode = true;
39694                 this.selectPrev();
39695             },
39696
39697             "down" : function(e){
39698                 if(!this.isExpanded()){
39699                     this.onTriggerClick();
39700                 }else{
39701                     this.inKeyMode = true;
39702                     this.selectNext();
39703                 }
39704             },
39705
39706             "enter" : function(e){
39707                 this.onViewClick();
39708                 //return true;
39709             },
39710
39711             "esc" : function(e){
39712                 this.collapse();
39713             },
39714
39715             "tab" : function(e){
39716                 this.onViewClick(false);
39717                 this.fireEvent("specialkey", this, e);
39718                 return true;
39719             },
39720
39721             scope : this,
39722
39723             doRelay : function(foo, bar, hname){
39724                 if(hname == 'down' || this.scope.isExpanded()){
39725                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39726                 }
39727                 return true;
39728             },
39729
39730             forceKeyDown: true
39731         });
39732         this.queryDelay = Math.max(this.queryDelay || 10,
39733                 this.mode == 'local' ? 10 : 250);
39734         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39735         if(this.typeAhead){
39736             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39737         }
39738         if(this.editable !== false){
39739             this.el.on("keyup", this.onKeyUp, this);
39740         }
39741         if(this.forceSelection){
39742             this.on('blur', this.doForce, this);
39743         }
39744     },
39745
39746     onDestroy : function(){
39747         if(this.view){
39748             this.view.setStore(null);
39749             this.view.el.removeAllListeners();
39750             this.view.el.remove();
39751             this.view.purgeListeners();
39752         }
39753         if(this.list){
39754             this.list.destroy();
39755         }
39756         if(this.store){
39757             this.store.un('beforeload', this.onBeforeLoad, this);
39758             this.store.un('load', this.onLoad, this);
39759             this.store.un('loadexception', this.onLoadException, this);
39760         }
39761         Roo.form.ComboBox.superclass.onDestroy.call(this);
39762     },
39763
39764     // private
39765     fireKey : function(e){
39766         if(e.isNavKeyPress() && !this.list.isVisible()){
39767             this.fireEvent("specialkey", this, e);
39768         }
39769     },
39770
39771     // private
39772     onResize: function(w, h){
39773         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39774         
39775         if(typeof w != 'number'){
39776             // we do not handle it!?!?
39777             return;
39778         }
39779         var tw = this.trigger.getWidth();
39780         tw += this.addicon ? this.addicon.getWidth() : 0;
39781         tw += this.editicon ? this.editicon.getWidth() : 0;
39782         var x = w - tw;
39783         this.el.setWidth( this.adjustWidth('input', x));
39784             
39785         this.trigger.setStyle('left', x+'px');
39786         
39787         if(this.list && this.listWidth === undefined){
39788             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39789             this.list.setWidth(lw);
39790             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39791         }
39792         
39793     
39794         
39795     },
39796
39797     /**
39798      * Allow or prevent the user from directly editing the field text.  If false is passed,
39799      * the user will only be able to select from the items defined in the dropdown list.  This method
39800      * is the runtime equivalent of setting the 'editable' config option at config time.
39801      * @param {Boolean} value True to allow the user to directly edit the field text
39802      */
39803     setEditable : function(value){
39804         if(value == this.editable){
39805             return;
39806         }
39807         this.editable = value;
39808         if(!value){
39809             this.el.dom.setAttribute('readOnly', true);
39810             this.el.on('mousedown', this.onTriggerClick,  this);
39811             this.el.addClass('x-combo-noedit');
39812         }else{
39813             this.el.dom.setAttribute('readOnly', false);
39814             this.el.un('mousedown', this.onTriggerClick,  this);
39815             this.el.removeClass('x-combo-noedit');
39816         }
39817     },
39818
39819     // private
39820     onBeforeLoad : function(){
39821         if(!this.hasFocus){
39822             return;
39823         }
39824         this.innerList.update(this.loadingText ?
39825                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39826         this.restrictHeight();
39827         this.selectedIndex = -1;
39828     },
39829
39830     // private
39831     onLoad : function(){
39832         if(!this.hasFocus){
39833             return;
39834         }
39835         if(this.store.getCount() > 0){
39836             this.expand();
39837             this.restrictHeight();
39838             if(this.lastQuery == this.allQuery){
39839                 if(this.editable){
39840                     this.el.dom.select();
39841                 }
39842                 if(!this.selectByValue(this.value, true)){
39843                     this.select(0, true);
39844                 }
39845             }else{
39846                 this.selectNext();
39847                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39848                     this.taTask.delay(this.typeAheadDelay);
39849                 }
39850             }
39851         }else{
39852             this.onEmptyResults();
39853         }
39854         //this.el.focus();
39855     },
39856     // private
39857     onLoadException : function()
39858     {
39859         this.collapse();
39860         Roo.log(this.store.reader.jsonData);
39861         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39862             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39863         }
39864         
39865         
39866     },
39867     // private
39868     onTypeAhead : function(){
39869         if(this.store.getCount() > 0){
39870             var r = this.store.getAt(0);
39871             var newValue = r.data[this.displayField];
39872             var len = newValue.length;
39873             var selStart = this.getRawValue().length;
39874             if(selStart != len){
39875                 this.setRawValue(newValue);
39876                 this.selectText(selStart, newValue.length);
39877             }
39878         }
39879     },
39880
39881     // private
39882     onSelect : function(record, index){
39883         if(this.fireEvent('beforeselect', this, record, index) !== false){
39884             this.setFromData(index > -1 ? record.data : false);
39885             this.collapse();
39886             this.fireEvent('select', this, record, index);
39887         }
39888     },
39889
39890     /**
39891      * Returns the currently selected field value or empty string if no value is set.
39892      * @return {String} value The selected value
39893      */
39894     getValue : function(){
39895         if(this.valueField){
39896             return typeof this.value != 'undefined' ? this.value : '';
39897         }
39898         return Roo.form.ComboBox.superclass.getValue.call(this);
39899     },
39900
39901     /**
39902      * Clears any text/value currently set in the field
39903      */
39904     clearValue : function(){
39905         if(this.hiddenField){
39906             this.hiddenField.value = '';
39907         }
39908         this.value = '';
39909         this.setRawValue('');
39910         this.lastSelectionText = '';
39911         
39912     },
39913
39914     /**
39915      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39916      * will be displayed in the field.  If the value does not match the data value of an existing item,
39917      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39918      * Otherwise the field will be blank (although the value will still be set).
39919      * @param {String} value The value to match
39920      */
39921     setValue : function(v){
39922         var text = v;
39923         if(this.valueField){
39924             var r = this.findRecord(this.valueField, v);
39925             if(r){
39926                 text = r.data[this.displayField];
39927             }else if(this.valueNotFoundText !== undefined){
39928                 text = this.valueNotFoundText;
39929             }
39930         }
39931         this.lastSelectionText = text;
39932         if(this.hiddenField){
39933             this.hiddenField.value = v;
39934         }
39935         Roo.form.ComboBox.superclass.setValue.call(this, text);
39936         this.value = v;
39937     },
39938     /**
39939      * @property {Object} the last set data for the element
39940      */
39941     
39942     lastData : false,
39943     /**
39944      * Sets the value of the field based on a object which is related to the record format for the store.
39945      * @param {Object} value the value to set as. or false on reset?
39946      */
39947     setFromData : function(o){
39948         var dv = ''; // display value
39949         var vv = ''; // value value..
39950         this.lastData = o;
39951         if (this.displayField) {
39952             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39953         } else {
39954             // this is an error condition!!!
39955             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39956         }
39957         
39958         if(this.valueField){
39959             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39960         }
39961         if(this.hiddenField){
39962             this.hiddenField.value = vv;
39963             
39964             this.lastSelectionText = dv;
39965             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39966             this.value = vv;
39967             return;
39968         }
39969         // no hidden field.. - we store the value in 'value', but still display
39970         // display field!!!!
39971         this.lastSelectionText = dv;
39972         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39973         this.value = vv;
39974         
39975         
39976     },
39977     // private
39978     reset : function(){
39979         // overridden so that last data is reset..
39980         this.setValue(this.resetValue);
39981         this.clearInvalid();
39982         this.lastData = false;
39983         if (this.view) {
39984             this.view.clearSelections();
39985         }
39986     },
39987     // private
39988     findRecord : function(prop, value){
39989         var record;
39990         if(this.store.getCount() > 0){
39991             this.store.each(function(r){
39992                 if(r.data[prop] == value){
39993                     record = r;
39994                     return false;
39995                 }
39996                 return true;
39997             });
39998         }
39999         return record;
40000     },
40001     
40002     getName: function()
40003     {
40004         // returns hidden if it's set..
40005         if (!this.rendered) {return ''};
40006         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40007         
40008     },
40009     // private
40010     onViewMove : function(e, t){
40011         this.inKeyMode = false;
40012     },
40013
40014     // private
40015     onViewOver : function(e, t){
40016         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40017             return;
40018         }
40019         var item = this.view.findItemFromChild(t);
40020         if(item){
40021             var index = this.view.indexOf(item);
40022             this.select(index, false);
40023         }
40024     },
40025
40026     // private
40027     onViewClick : function(doFocus)
40028     {
40029         var index = this.view.getSelectedIndexes()[0];
40030         var r = this.store.getAt(index);
40031         if(r){
40032             this.onSelect(r, index);
40033         }
40034         if(doFocus !== false && !this.blockFocus){
40035             this.el.focus();
40036         }
40037     },
40038
40039     // private
40040     restrictHeight : function(){
40041         this.innerList.dom.style.height = '';
40042         var inner = this.innerList.dom;
40043         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40044         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40045         this.list.beginUpdate();
40046         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40047         this.list.alignTo(this.el, this.listAlign);
40048         this.list.endUpdate();
40049     },
40050
40051     // private
40052     onEmptyResults : function(){
40053         this.collapse();
40054     },
40055
40056     /**
40057      * Returns true if the dropdown list is expanded, else false.
40058      */
40059     isExpanded : function(){
40060         return this.list.isVisible();
40061     },
40062
40063     /**
40064      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40065      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40066      * @param {String} value The data value of the item to select
40067      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40068      * selected item if it is not currently in view (defaults to true)
40069      * @return {Boolean} True if the value matched an item in the list, else false
40070      */
40071     selectByValue : function(v, scrollIntoView){
40072         if(v !== undefined && v !== null){
40073             var r = this.findRecord(this.valueField || this.displayField, v);
40074             if(r){
40075                 this.select(this.store.indexOf(r), scrollIntoView);
40076                 return true;
40077             }
40078         }
40079         return false;
40080     },
40081
40082     /**
40083      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40084      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40085      * @param {Number} index The zero-based index of the list item to select
40086      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40087      * selected item if it is not currently in view (defaults to true)
40088      */
40089     select : function(index, scrollIntoView){
40090         this.selectedIndex = index;
40091         this.view.select(index);
40092         if(scrollIntoView !== false){
40093             var el = this.view.getNode(index);
40094             if(el){
40095                 this.innerList.scrollChildIntoView(el, false);
40096             }
40097         }
40098     },
40099
40100     // private
40101     selectNext : function(){
40102         var ct = this.store.getCount();
40103         if(ct > 0){
40104             if(this.selectedIndex == -1){
40105                 this.select(0);
40106             }else if(this.selectedIndex < ct-1){
40107                 this.select(this.selectedIndex+1);
40108             }
40109         }
40110     },
40111
40112     // private
40113     selectPrev : function(){
40114         var ct = this.store.getCount();
40115         if(ct > 0){
40116             if(this.selectedIndex == -1){
40117                 this.select(0);
40118             }else if(this.selectedIndex != 0){
40119                 this.select(this.selectedIndex-1);
40120             }
40121         }
40122     },
40123
40124     // private
40125     onKeyUp : function(e){
40126         if(this.editable !== false && !e.isSpecialKey()){
40127             this.lastKey = e.getKey();
40128             this.dqTask.delay(this.queryDelay);
40129         }
40130     },
40131
40132     // private
40133     validateBlur : function(){
40134         return !this.list || !this.list.isVisible();   
40135     },
40136
40137     // private
40138     initQuery : function(){
40139         this.doQuery(this.getRawValue());
40140     },
40141
40142     // private
40143     doForce : function(){
40144         if(this.el.dom.value.length > 0){
40145             this.el.dom.value =
40146                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40147              
40148         }
40149     },
40150
40151     /**
40152      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40153      * query allowing the query action to be canceled if needed.
40154      * @param {String} query The SQL query to execute
40155      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40156      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40157      * saved in the current store (defaults to false)
40158      */
40159     doQuery : function(q, forceAll){
40160         if(q === undefined || q === null){
40161             q = '';
40162         }
40163         var qe = {
40164             query: q,
40165             forceAll: forceAll,
40166             combo: this,
40167             cancel:false
40168         };
40169         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40170             return false;
40171         }
40172         q = qe.query;
40173         forceAll = qe.forceAll;
40174         if(forceAll === true || (q.length >= this.minChars)){
40175             if(this.lastQuery != q || this.alwaysQuery){
40176                 this.lastQuery = q;
40177                 if(this.mode == 'local'){
40178                     this.selectedIndex = -1;
40179                     if(forceAll){
40180                         this.store.clearFilter();
40181                     }else{
40182                         this.store.filter(this.displayField, q);
40183                     }
40184                     this.onLoad();
40185                 }else{
40186                     this.store.baseParams[this.queryParam] = q;
40187                     this.store.load({
40188                         params: this.getParams(q)
40189                     });
40190                     this.expand();
40191                 }
40192             }else{
40193                 this.selectedIndex = -1;
40194                 this.onLoad();   
40195             }
40196         }
40197     },
40198
40199     // private
40200     getParams : function(q){
40201         var p = {};
40202         //p[this.queryParam] = q;
40203         if(this.pageSize){
40204             p.start = 0;
40205             p.limit = this.pageSize;
40206         }
40207         return p;
40208     },
40209
40210     /**
40211      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40212      */
40213     collapse : function(){
40214         if(!this.isExpanded()){
40215             return;
40216         }
40217         this.list.hide();
40218         Roo.get(document).un('mousedown', this.collapseIf, this);
40219         Roo.get(document).un('mousewheel', this.collapseIf, this);
40220         if (!this.editable) {
40221             Roo.get(document).un('keydown', this.listKeyPress, this);
40222         }
40223         this.fireEvent('collapse', this);
40224     },
40225
40226     // private
40227     collapseIf : function(e){
40228         if(!e.within(this.wrap) && !e.within(this.list)){
40229             this.collapse();
40230         }
40231     },
40232
40233     /**
40234      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40235      */
40236     expand : function(){
40237         if(this.isExpanded() || !this.hasFocus){
40238             return;
40239         }
40240         this.list.alignTo(this.el, this.listAlign);
40241         this.list.show();
40242         Roo.get(document).on('mousedown', this.collapseIf, this);
40243         Roo.get(document).on('mousewheel', this.collapseIf, this);
40244         if (!this.editable) {
40245             Roo.get(document).on('keydown', this.listKeyPress, this);
40246         }
40247         
40248         this.fireEvent('expand', this);
40249     },
40250
40251     // private
40252     // Implements the default empty TriggerField.onTriggerClick function
40253     onTriggerClick : function(){
40254         if(this.disabled){
40255             return;
40256         }
40257         if(this.isExpanded()){
40258             this.collapse();
40259             if (!this.blockFocus) {
40260                 this.el.focus();
40261             }
40262             
40263         }else {
40264             this.hasFocus = true;
40265             if(this.triggerAction == 'all') {
40266                 this.doQuery(this.allQuery, true);
40267             } else {
40268                 this.doQuery(this.getRawValue());
40269             }
40270             if (!this.blockFocus) {
40271                 this.el.focus();
40272             }
40273         }
40274     },
40275     listKeyPress : function(e)
40276     {
40277         //Roo.log('listkeypress');
40278         // scroll to first matching element based on key pres..
40279         if (e.isSpecialKey()) {
40280             return false;
40281         }
40282         var k = String.fromCharCode(e.getKey()).toUpperCase();
40283         //Roo.log(k);
40284         var match  = false;
40285         var csel = this.view.getSelectedNodes();
40286         var cselitem = false;
40287         if (csel.length) {
40288             var ix = this.view.indexOf(csel[0]);
40289             cselitem  = this.store.getAt(ix);
40290             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40291                 cselitem = false;
40292             }
40293             
40294         }
40295         
40296         this.store.each(function(v) { 
40297             if (cselitem) {
40298                 // start at existing selection.
40299                 if (cselitem.id == v.id) {
40300                     cselitem = false;
40301                 }
40302                 return;
40303             }
40304                 
40305             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40306                 match = this.store.indexOf(v);
40307                 return false;
40308             }
40309         }, this);
40310         
40311         if (match === false) {
40312             return true; // no more action?
40313         }
40314         // scroll to?
40315         this.view.select(match);
40316         var sn = Roo.get(this.view.getSelectedNodes()[0])
40317         sn.scrollIntoView(sn.dom.parentNode, false);
40318     }
40319
40320     /** 
40321     * @cfg {Boolean} grow 
40322     * @hide 
40323     */
40324     /** 
40325     * @cfg {Number} growMin 
40326     * @hide 
40327     */
40328     /** 
40329     * @cfg {Number} growMax 
40330     * @hide 
40331     */
40332     /**
40333      * @hide
40334      * @method autoSize
40335      */
40336 });/*
40337  * Copyright(c) 2010-2012, Roo J Solutions Limited
40338  *
40339  * Licence LGPL
40340  *
40341  */
40342
40343 /**
40344  * @class Roo.form.ComboBoxArray
40345  * @extends Roo.form.TextField
40346  * A facebook style adder... for lists of email / people / countries  etc...
40347  * pick multiple items from a combo box, and shows each one.
40348  *
40349  *  Fred [x]  Brian [x]  [Pick another |v]
40350  *
40351  *
40352  *  For this to work: it needs various extra information
40353  *    - normal combo problay has
40354  *      name, hiddenName
40355  *    + displayField, valueField
40356  *
40357  *    For our purpose...
40358  *
40359  *
40360  *   If we change from 'extends' to wrapping...
40361  *   
40362  *  
40363  *
40364  
40365  
40366  * @constructor
40367  * Create a new ComboBoxArray.
40368  * @param {Object} config Configuration options
40369  */
40370  
40371
40372 Roo.form.ComboBoxArray = function(config)
40373 {
40374     this.addEvents({
40375         /**
40376          * @event beforeremove
40377          * Fires before remove the value from the list
40378              * @param {Roo.form.ComboBoxArray} _self This combo box array
40379              * @param {Roo.form.ComboBoxArray.Item} item removed item
40380              */
40381         'beforeremove' : true,
40382         /**
40383          * @event remove
40384          * Fires when remove the value from the list
40385              * @param {Roo.form.ComboBoxArray} _self This combo box array
40386              * @param {Roo.form.ComboBoxArray.Item} item removed item
40387              */
40388         'remove' : true
40389         
40390         
40391     });
40392     
40393     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40394     
40395     this.items = new Roo.util.MixedCollection(false);
40396     
40397     // construct the child combo...
40398     
40399     
40400     
40401     
40402    
40403     
40404 }
40405
40406  
40407 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40408
40409     /**
40410      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40411      */
40412     
40413     lastData : false,
40414     
40415     // behavies liek a hiddne field
40416     inputType:      'hidden',
40417     /**
40418      * @cfg {Number} width The width of the box that displays the selected element
40419      */ 
40420     width:          300,
40421
40422     
40423     
40424     /**
40425      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40426      */
40427     name : false,
40428     /**
40429      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40430      */
40431     hiddenName : false,
40432     
40433     
40434     // private the array of items that are displayed..
40435     items  : false,
40436     // private - the hidden field el.
40437     hiddenEl : false,
40438     // private - the filed el..
40439     el : false,
40440     
40441     //validateValue : function() { return true; }, // all values are ok!
40442     //onAddClick: function() { },
40443     
40444     onRender : function(ct, position) 
40445     {
40446         
40447         // create the standard hidden element
40448         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40449         
40450         
40451         // give fake names to child combo;
40452         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40453         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40454         
40455         this.combo = Roo.factory(this.combo, Roo.form);
40456         this.combo.onRender(ct, position);
40457         if (typeof(this.combo.width) != 'undefined') {
40458             this.combo.onResize(this.combo.width,0);
40459         }
40460         
40461         this.combo.initEvents();
40462         
40463         // assigned so form know we need to do this..
40464         this.store          = this.combo.store;
40465         this.valueField     = this.combo.valueField;
40466         this.displayField   = this.combo.displayField ;
40467         
40468         
40469         this.combo.wrap.addClass('x-cbarray-grp');
40470         
40471         var cbwrap = this.combo.wrap.createChild(
40472             {tag: 'div', cls: 'x-cbarray-cb'},
40473             this.combo.el.dom
40474         );
40475         
40476              
40477         this.hiddenEl = this.combo.wrap.createChild({
40478             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40479         });
40480         this.el = this.combo.wrap.createChild({
40481             tag: 'input',  type:'hidden' , name: this.name, value : ''
40482         });
40483          //   this.el.dom.removeAttribute("name");
40484         
40485         
40486         this.outerWrap = this.combo.wrap;
40487         this.wrap = cbwrap;
40488         
40489         this.outerWrap.setWidth(this.width);
40490         this.outerWrap.dom.removeChild(this.el.dom);
40491         
40492         this.wrap.dom.appendChild(this.el.dom);
40493         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40494         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40495         
40496         this.combo.trigger.setStyle('position','relative');
40497         this.combo.trigger.setStyle('left', '0px');
40498         this.combo.trigger.setStyle('top', '2px');
40499         
40500         this.combo.el.setStyle('vertical-align', 'text-bottom');
40501         
40502         //this.trigger.setStyle('vertical-align', 'top');
40503         
40504         // this should use the code from combo really... on('add' ....)
40505         if (this.adder) {
40506             
40507         
40508             this.adder = this.outerWrap.createChild(
40509                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40510             var _t = this;
40511             this.adder.on('click', function(e) {
40512                 _t.fireEvent('adderclick', this, e);
40513             }, _t);
40514         }
40515         //var _t = this;
40516         //this.adder.on('click', this.onAddClick, _t);
40517         
40518         
40519         this.combo.on('select', function(cb, rec, ix) {
40520             this.addItem(rec.data);
40521             
40522             cb.setValue('');
40523             cb.el.dom.value = '';
40524             //cb.lastData = rec.data;
40525             // add to list
40526             
40527         }, this);
40528         
40529         
40530     },
40531     
40532     
40533     getName: function()
40534     {
40535         // returns hidden if it's set..
40536         if (!this.rendered) {return ''};
40537         return  this.hiddenName ? this.hiddenName : this.name;
40538         
40539     },
40540     
40541     
40542     onResize: function(w, h){
40543         
40544         return;
40545         // not sure if this is needed..
40546         //this.combo.onResize(w,h);
40547         
40548         if(typeof w != 'number'){
40549             // we do not handle it!?!?
40550             return;
40551         }
40552         var tw = this.combo.trigger.getWidth();
40553         tw += this.addicon ? this.addicon.getWidth() : 0;
40554         tw += this.editicon ? this.editicon.getWidth() : 0;
40555         var x = w - tw;
40556         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40557             
40558         this.combo.trigger.setStyle('left', '0px');
40559         
40560         if(this.list && this.listWidth === undefined){
40561             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40562             this.list.setWidth(lw);
40563             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40564         }
40565         
40566     
40567         
40568     },
40569     
40570     addItem: function(rec)
40571     {
40572         var valueField = this.combo.valueField;
40573         var displayField = this.combo.displayField;
40574         if (this.items.indexOfKey(rec[valueField]) > -1) {
40575             //console.log("GOT " + rec.data.id);
40576             return;
40577         }
40578         
40579         var x = new Roo.form.ComboBoxArray.Item({
40580             //id : rec[this.idField],
40581             data : rec,
40582             displayField : displayField ,
40583             tipField : displayField ,
40584             cb : this
40585         });
40586         // use the 
40587         this.items.add(rec[valueField],x);
40588         // add it before the element..
40589         this.updateHiddenEl();
40590         x.render(this.outerWrap, this.wrap.dom);
40591         // add the image handler..
40592     },
40593     
40594     updateHiddenEl : function()
40595     {
40596         this.validate();
40597         if (!this.hiddenEl) {
40598             return;
40599         }
40600         var ar = [];
40601         var idField = this.combo.valueField;
40602         
40603         this.items.each(function(f) {
40604             ar.push(f.data[idField]);
40605            
40606         });
40607         this.hiddenEl.dom.value = ar.join(',');
40608         this.validate();
40609     },
40610     
40611     reset : function()
40612     {
40613         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40614         this.items.each(function(f) {
40615            f.remove(); 
40616         });
40617         this.el.dom.value = '';
40618         if (this.hiddenEl) {
40619             this.hiddenEl.dom.value = '';
40620         }
40621         
40622     },
40623     getValue: function()
40624     {
40625         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40626     },
40627     setValue: function(v) // not a valid action - must use addItems..
40628     {
40629          
40630         this.reset();
40631         
40632         
40633         
40634         if (this.store.isLocal && (typeof(v) == 'string')) {
40635             // then we can use the store to find the values..
40636             // comma seperated at present.. this needs to allow JSON based encoding..
40637             this.hiddenEl.value  = v;
40638             var v_ar = [];
40639             Roo.each(v.split(','), function(k) {
40640                 Roo.log("CHECK " + this.valueField + ',' + k);
40641                 var li = this.store.query(this.valueField, k);
40642                 if (!li.length) {
40643                     return;
40644                 }
40645                 var add = {};
40646                 add[this.valueField] = k;
40647                 add[this.displayField] = li.item(0).data[this.displayField];
40648                 
40649                 this.addItem(add);
40650             }, this) 
40651              
40652         }
40653         if (typeof(v) == 'object' ) {
40654             // then let's assume it's an array of objects..
40655             Roo.each(v, function(l) {
40656                 this.addItem(l);
40657             }, this);
40658              
40659         }
40660         
40661         
40662     },
40663     setFromData: function(v)
40664     {
40665         // this recieves an object, if setValues is called.
40666         this.reset();
40667         this.el.dom.value = v[this.displayField];
40668         this.hiddenEl.dom.value = v[this.valueField];
40669         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40670             return;
40671         }
40672         var kv = v[this.valueField];
40673         var dv = v[this.displayField];
40674         kv = typeof(kv) != 'string' ? '' : kv;
40675         dv = typeof(dv) != 'string' ? '' : dv;
40676         
40677         
40678         var keys = kv.split(',');
40679         var display = dv.split(',');
40680         for (var i = 0 ; i < keys.length; i++) {
40681             
40682             add = {};
40683             add[this.valueField] = keys[i];
40684             add[this.displayField] = display[i];
40685             this.addItem(add);
40686         }
40687       
40688         
40689     },
40690     
40691     /**
40692      * Validates the combox array value
40693      * @return {Boolean} True if the value is valid, else false
40694      */
40695     validate : function(){
40696         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40697             this.clearInvalid();
40698             return true;
40699         }
40700         return false;
40701     },
40702     
40703     validateValue : function(value){
40704         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40705         
40706     },
40707     
40708     /*@
40709      * overide
40710      * 
40711      */
40712     isDirty : function() {
40713         if(this.disabled) {
40714             return false;
40715         }
40716         
40717         try {
40718             var d = Roo.decode(String(this.originalValue));
40719         } catch (e) {
40720             return String(this.getValue()) !== String(this.originalValue);
40721         }
40722         
40723         var originalValue = [];
40724         
40725         for (var i = 0; i < d.length; i++){
40726             originalValue.push(d[i][this.valueField]);
40727         }
40728         
40729         return String(this.getValue()) !== String(originalValue.join(','));
40730         
40731     }
40732     
40733 });
40734
40735
40736
40737 /**
40738  * @class Roo.form.ComboBoxArray.Item
40739  * @extends Roo.BoxComponent
40740  * A selected item in the list
40741  *  Fred [x]  Brian [x]  [Pick another |v]
40742  * 
40743  * @constructor
40744  * Create a new item.
40745  * @param {Object} config Configuration options
40746  */
40747  
40748 Roo.form.ComboBoxArray.Item = function(config) {
40749     config.id = Roo.id();
40750     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40751 }
40752
40753 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40754     data : {},
40755     cb: false,
40756     displayField : false,
40757     tipField : false,
40758     
40759     
40760     defaultAutoCreate : {
40761         tag: 'div',
40762         cls: 'x-cbarray-item',
40763         cn : [ 
40764             { tag: 'div' },
40765             {
40766                 tag: 'img',
40767                 width:16,
40768                 height : 16,
40769                 src : Roo.BLANK_IMAGE_URL ,
40770                 align: 'center'
40771             }
40772         ]
40773         
40774     },
40775     
40776  
40777     onRender : function(ct, position)
40778     {
40779         Roo.form.Field.superclass.onRender.call(this, ct, position);
40780         
40781         if(!this.el){
40782             var cfg = this.getAutoCreate();
40783             this.el = ct.createChild(cfg, position);
40784         }
40785         
40786         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40787         
40788         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40789             this.cb.renderer(this.data) :
40790             String.format('{0}',this.data[this.displayField]);
40791         
40792             
40793         this.el.child('div').dom.setAttribute('qtip',
40794                         String.format('{0}',this.data[this.tipField])
40795         );
40796         
40797         this.el.child('img').on('click', this.remove, this);
40798         
40799     },
40800    
40801     remove : function()
40802     {
40803         if(this.cb.disabled){
40804             return;
40805         }
40806         
40807         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40808             this.cb.items.remove(this);
40809             this.el.child('img').un('click', this.remove, this);
40810             this.el.remove();
40811             this.cb.updateHiddenEl();
40812
40813             this.cb.fireEvent('remove', this.cb, this);
40814         }
40815         
40816     }
40817 });/*
40818  * Based on:
40819  * Ext JS Library 1.1.1
40820  * Copyright(c) 2006-2007, Ext JS, LLC.
40821  *
40822  * Originally Released Under LGPL - original licence link has changed is not relivant.
40823  *
40824  * Fork - LGPL
40825  * <script type="text/javascript">
40826  */
40827 /**
40828  * @class Roo.form.Checkbox
40829  * @extends Roo.form.Field
40830  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40831  * @constructor
40832  * Creates a new Checkbox
40833  * @param {Object} config Configuration options
40834  */
40835 Roo.form.Checkbox = function(config){
40836     Roo.form.Checkbox.superclass.constructor.call(this, config);
40837     this.addEvents({
40838         /**
40839          * @event check
40840          * Fires when the checkbox is checked or unchecked.
40841              * @param {Roo.form.Checkbox} this This checkbox
40842              * @param {Boolean} checked The new checked value
40843              */
40844         check : true
40845     });
40846 };
40847
40848 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40849     /**
40850      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40851      */
40852     focusClass : undefined,
40853     /**
40854      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40855      */
40856     fieldClass: "x-form-field",
40857     /**
40858      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40859      */
40860     checked: false,
40861     /**
40862      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40863      * {tag: "input", type: "checkbox", autocomplete: "off"})
40864      */
40865     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40866     /**
40867      * @cfg {String} boxLabel The text that appears beside the checkbox
40868      */
40869     boxLabel : "",
40870     /**
40871      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40872      */  
40873     inputValue : '1',
40874     /**
40875      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40876      */
40877      valueOff: '0', // value when not checked..
40878
40879     actionMode : 'viewEl', 
40880     //
40881     // private
40882     itemCls : 'x-menu-check-item x-form-item',
40883     groupClass : 'x-menu-group-item',
40884     inputType : 'hidden',
40885     
40886     
40887     inSetChecked: false, // check that we are not calling self...
40888     
40889     inputElement: false, // real input element?
40890     basedOn: false, // ????
40891     
40892     isFormField: true, // not sure where this is needed!!!!
40893
40894     onResize : function(){
40895         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40896         if(!this.boxLabel){
40897             this.el.alignTo(this.wrap, 'c-c');
40898         }
40899     },
40900
40901     initEvents : function(){
40902         Roo.form.Checkbox.superclass.initEvents.call(this);
40903         this.el.on("click", this.onClick,  this);
40904         this.el.on("change", this.onClick,  this);
40905     },
40906
40907
40908     getResizeEl : function(){
40909         return this.wrap;
40910     },
40911
40912     getPositionEl : function(){
40913         return this.wrap;
40914     },
40915
40916     // private
40917     onRender : function(ct, position){
40918         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40919         /*
40920         if(this.inputValue !== undefined){
40921             this.el.dom.value = this.inputValue;
40922         }
40923         */
40924         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40925         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40926         var viewEl = this.wrap.createChild({ 
40927             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40928         this.viewEl = viewEl;   
40929         this.wrap.on('click', this.onClick,  this); 
40930         
40931         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40932         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40933         
40934         
40935         
40936         if(this.boxLabel){
40937             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40938         //    viewEl.on('click', this.onClick,  this); 
40939         }
40940         //if(this.checked){
40941             this.setChecked(this.checked);
40942         //}else{
40943             //this.checked = this.el.dom;
40944         //}
40945
40946     },
40947
40948     // private
40949     initValue : Roo.emptyFn,
40950
40951     /**
40952      * Returns the checked state of the checkbox.
40953      * @return {Boolean} True if checked, else false
40954      */
40955     getValue : function(){
40956         if(this.el){
40957             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40958         }
40959         return this.valueOff;
40960         
40961     },
40962
40963         // private
40964     onClick : function(){ 
40965         if (this.disabled) {
40966             return;
40967         }
40968         this.setChecked(!this.checked);
40969
40970         //if(this.el.dom.checked != this.checked){
40971         //    this.setValue(this.el.dom.checked);
40972        // }
40973     },
40974
40975     /**
40976      * Sets the checked state of the checkbox.
40977      * On is always based on a string comparison between inputValue and the param.
40978      * @param {Boolean/String} value - the value to set 
40979      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40980      */
40981     setValue : function(v,suppressEvent){
40982         
40983         
40984         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40985         //if(this.el && this.el.dom){
40986         //    this.el.dom.checked = this.checked;
40987         //    this.el.dom.defaultChecked = this.checked;
40988         //}
40989         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40990         //this.fireEvent("check", this, this.checked);
40991     },
40992     // private..
40993     setChecked : function(state,suppressEvent)
40994     {
40995         if (this.inSetChecked) {
40996             this.checked = state;
40997             return;
40998         }
40999         
41000     
41001         if(this.wrap){
41002             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41003         }
41004         this.checked = state;
41005         if(suppressEvent !== true){
41006             this.fireEvent('check', this, state);
41007         }
41008         this.inSetChecked = true;
41009         this.el.dom.value = state ? this.inputValue : this.valueOff;
41010         this.inSetChecked = false;
41011         
41012     },
41013     // handle setting of hidden value by some other method!!?!?
41014     setFromHidden: function()
41015     {
41016         if(!this.el){
41017             return;
41018         }
41019         //console.log("SET FROM HIDDEN");
41020         //alert('setFrom hidden');
41021         this.setValue(this.el.dom.value);
41022     },
41023     
41024     onDestroy : function()
41025     {
41026         if(this.viewEl){
41027             Roo.get(this.viewEl).remove();
41028         }
41029          
41030         Roo.form.Checkbox.superclass.onDestroy.call(this);
41031     }
41032
41033 });/*
41034  * Based on:
41035  * Ext JS Library 1.1.1
41036  * Copyright(c) 2006-2007, Ext JS, LLC.
41037  *
41038  * Originally Released Under LGPL - original licence link has changed is not relivant.
41039  *
41040  * Fork - LGPL
41041  * <script type="text/javascript">
41042  */
41043  
41044 /**
41045  * @class Roo.form.Radio
41046  * @extends Roo.form.Checkbox
41047  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41048  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41049  * @constructor
41050  * Creates a new Radio
41051  * @param {Object} config Configuration options
41052  */
41053 Roo.form.Radio = function(){
41054     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41055 };
41056 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41057     inputType: 'radio',
41058
41059     /**
41060      * If this radio is part of a group, it will return the selected value
41061      * @return {String}
41062      */
41063     getGroupValue : function(){
41064         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41065     },
41066     
41067     
41068     onRender : function(ct, position){
41069         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41070         
41071         if(this.inputValue !== undefined){
41072             this.el.dom.value = this.inputValue;
41073         }
41074          
41075         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41076         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41077         //var viewEl = this.wrap.createChild({ 
41078         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41079         //this.viewEl = viewEl;   
41080         //this.wrap.on('click', this.onClick,  this); 
41081         
41082         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41083         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41084         
41085         
41086         
41087         if(this.boxLabel){
41088             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41089         //    viewEl.on('click', this.onClick,  this); 
41090         }
41091          if(this.checked){
41092             this.el.dom.checked =   'checked' ;
41093         }
41094          
41095     } 
41096     
41097     
41098 });//<script type="text/javascript">
41099
41100 /*
41101  * Based  Ext JS Library 1.1.1
41102  * Copyright(c) 2006-2007, Ext JS, LLC.
41103  * LGPL
41104  *
41105  */
41106  
41107 /**
41108  * @class Roo.HtmlEditorCore
41109  * @extends Roo.Component
41110  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41111  *
41112  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41113  */
41114
41115 Roo.HtmlEditorCore = function(config){
41116     
41117     
41118     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41119     
41120     
41121     this.addEvents({
41122         /**
41123          * @event initialize
41124          * Fires when the editor is fully initialized (including the iframe)
41125          * @param {Roo.HtmlEditorCore} this
41126          */
41127         initialize: true,
41128         /**
41129          * @event activate
41130          * Fires when the editor is first receives the focus. Any insertion must wait
41131          * until after this event.
41132          * @param {Roo.HtmlEditorCore} this
41133          */
41134         activate: true,
41135          /**
41136          * @event beforesync
41137          * Fires before the textarea is updated with content from the editor iframe. Return false
41138          * to cancel the sync.
41139          * @param {Roo.HtmlEditorCore} this
41140          * @param {String} html
41141          */
41142         beforesync: true,
41143          /**
41144          * @event beforepush
41145          * Fires before the iframe editor is updated with content from the textarea. Return false
41146          * to cancel the push.
41147          * @param {Roo.HtmlEditorCore} this
41148          * @param {String} html
41149          */
41150         beforepush: true,
41151          /**
41152          * @event sync
41153          * Fires when the textarea is updated with content from the editor iframe.
41154          * @param {Roo.HtmlEditorCore} this
41155          * @param {String} html
41156          */
41157         sync: true,
41158          /**
41159          * @event push
41160          * Fires when the iframe editor is updated with content from the textarea.
41161          * @param {Roo.HtmlEditorCore} this
41162          * @param {String} html
41163          */
41164         push: true,
41165         
41166         /**
41167          * @event editorevent
41168          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41169          * @param {Roo.HtmlEditorCore} this
41170          */
41171         editorevent: true
41172         
41173     });
41174     
41175     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41176     
41177     // defaults : white / black...
41178     this.applyBlacklists();
41179     
41180     
41181     
41182 };
41183
41184
41185 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41186
41187
41188      /**
41189      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41190      */
41191     
41192     owner : false,
41193     
41194      /**
41195      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41196      *                        Roo.resizable.
41197      */
41198     resizable : false,
41199      /**
41200      * @cfg {Number} height (in pixels)
41201      */   
41202     height: 300,
41203    /**
41204      * @cfg {Number} width (in pixels)
41205      */   
41206     width: 500,
41207     
41208     /**
41209      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41210      * 
41211      */
41212     stylesheets: false,
41213     
41214     // id of frame..
41215     frameId: false,
41216     
41217     // private properties
41218     validationEvent : false,
41219     deferHeight: true,
41220     initialized : false,
41221     activated : false,
41222     sourceEditMode : false,
41223     onFocus : Roo.emptyFn,
41224     iframePad:3,
41225     hideMode:'offsets',
41226     
41227     clearUp: true,
41228     
41229     // blacklist + whitelisted elements..
41230     black: false,
41231     white: false,
41232      
41233     
41234
41235     /**
41236      * Protected method that will not generally be called directly. It
41237      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41238      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41239      */
41240     getDocMarkup : function(){
41241         // body styles..
41242         var st = '';
41243         
41244         // inherit styels from page...?? 
41245         if (this.stylesheets === false) {
41246             
41247             Roo.get(document.head).select('style').each(function(node) {
41248                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41249             });
41250             
41251             Roo.get(document.head).select('link').each(function(node) { 
41252                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41253             });
41254             
41255         } else if (!this.stylesheets.length) {
41256                 // simple..
41257                 st = '<style type="text/css">' +
41258                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41259                    '</style>';
41260         } else { 
41261             
41262         }
41263         
41264         st +=  '<style type="text/css">' +
41265             'IMG { cursor: pointer } ' +
41266         '</style>';
41267
41268         
41269         return '<html><head>' + st  +
41270             //<style type="text/css">' +
41271             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41272             //'</style>' +
41273             ' </head><body class="roo-htmleditor-body"></body></html>';
41274     },
41275
41276     // private
41277     onRender : function(ct, position)
41278     {
41279         var _t = this;
41280         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41281         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41282         
41283         
41284         this.el.dom.style.border = '0 none';
41285         this.el.dom.setAttribute('tabIndex', -1);
41286         this.el.addClass('x-hidden hide');
41287         
41288         
41289         
41290         if(Roo.isIE){ // fix IE 1px bogus margin
41291             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41292         }
41293        
41294         
41295         this.frameId = Roo.id();
41296         
41297          
41298         
41299         var iframe = this.owner.wrap.createChild({
41300             tag: 'iframe',
41301             cls: 'form-control', // bootstrap..
41302             id: this.frameId,
41303             name: this.frameId,
41304             frameBorder : 'no',
41305             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41306         }, this.el
41307         );
41308         
41309         
41310         this.iframe = iframe.dom;
41311
41312          this.assignDocWin();
41313         
41314         this.doc.designMode = 'on';
41315        
41316         this.doc.open();
41317         this.doc.write(this.getDocMarkup());
41318         this.doc.close();
41319
41320         
41321         var task = { // must defer to wait for browser to be ready
41322             run : function(){
41323                 //console.log("run task?" + this.doc.readyState);
41324                 this.assignDocWin();
41325                 if(this.doc.body || this.doc.readyState == 'complete'){
41326                     try {
41327                         this.doc.designMode="on";
41328                     } catch (e) {
41329                         return;
41330                     }
41331                     Roo.TaskMgr.stop(task);
41332                     this.initEditor.defer(10, this);
41333                 }
41334             },
41335             interval : 10,
41336             duration: 10000,
41337             scope: this
41338         };
41339         Roo.TaskMgr.start(task);
41340
41341     },
41342
41343     // private
41344     onResize : function(w, h)
41345     {
41346          Roo.log('resize: ' +w + ',' + h );
41347         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41348         if(!this.iframe){
41349             return;
41350         }
41351         if(typeof w == 'number'){
41352             
41353             this.iframe.style.width = w + 'px';
41354         }
41355         if(typeof h == 'number'){
41356             
41357             this.iframe.style.height = h + 'px';
41358             if(this.doc){
41359                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41360             }
41361         }
41362         
41363     },
41364
41365     /**
41366      * Toggles the editor between standard and source edit mode.
41367      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41368      */
41369     toggleSourceEdit : function(sourceEditMode){
41370         
41371         this.sourceEditMode = sourceEditMode === true;
41372         
41373         if(this.sourceEditMode){
41374  
41375             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41376             
41377         }else{
41378             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41379             //this.iframe.className = '';
41380             this.deferFocus();
41381         }
41382         //this.setSize(this.owner.wrap.getSize());
41383         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41384     },
41385
41386     
41387   
41388
41389     /**
41390      * Protected method that will not generally be called directly. If you need/want
41391      * custom HTML cleanup, this is the method you should override.
41392      * @param {String} html The HTML to be cleaned
41393      * return {String} The cleaned HTML
41394      */
41395     cleanHtml : function(html){
41396         html = String(html);
41397         if(html.length > 5){
41398             if(Roo.isSafari){ // strip safari nonsense
41399                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41400             }
41401         }
41402         if(html == '&nbsp;'){
41403             html = '';
41404         }
41405         return html;
41406     },
41407
41408     /**
41409      * HTML Editor -> Textarea
41410      * Protected method that will not generally be called directly. Syncs the contents
41411      * of the editor iframe with the textarea.
41412      */
41413     syncValue : function(){
41414         if(this.initialized){
41415             var bd = (this.doc.body || this.doc.documentElement);
41416             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41417             var html = bd.innerHTML;
41418             if(Roo.isSafari){
41419                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41420                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41421                 if(m && m[1]){
41422                     html = '<div style="'+m[0]+'">' + html + '</div>';
41423                 }
41424             }
41425             html = this.cleanHtml(html);
41426             // fix up the special chars.. normaly like back quotes in word...
41427             // however we do not want to do this with chinese..
41428             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41429                 var cc = b.charCodeAt();
41430                 if (
41431                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41432                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41433                     (cc >= 0xf900 && cc < 0xfb00 )
41434                 ) {
41435                         return b;
41436                 }
41437                 return "&#"+cc+";" 
41438             });
41439             if(this.owner.fireEvent('beforesync', this, html) !== false){
41440                 this.el.dom.value = html;
41441                 this.owner.fireEvent('sync', this, html);
41442             }
41443         }
41444     },
41445
41446     /**
41447      * Protected method that will not generally be called directly. Pushes the value of the textarea
41448      * into the iframe editor.
41449      */
41450     pushValue : function(){
41451         if(this.initialized){
41452             var v = this.el.dom.value.trim();
41453             
41454 //            if(v.length < 1){
41455 //                v = '&#160;';
41456 //            }
41457             
41458             if(this.owner.fireEvent('beforepush', this, v) !== false){
41459                 var d = (this.doc.body || this.doc.documentElement);
41460                 d.innerHTML = v;
41461                 this.cleanUpPaste();
41462                 this.el.dom.value = d.innerHTML;
41463                 this.owner.fireEvent('push', this, v);
41464             }
41465         }
41466     },
41467
41468     // private
41469     deferFocus : function(){
41470         this.focus.defer(10, this);
41471     },
41472
41473     // doc'ed in Field
41474     focus : function(){
41475         if(this.win && !this.sourceEditMode){
41476             this.win.focus();
41477         }else{
41478             this.el.focus();
41479         }
41480     },
41481     
41482     assignDocWin: function()
41483     {
41484         var iframe = this.iframe;
41485         
41486          if(Roo.isIE){
41487             this.doc = iframe.contentWindow.document;
41488             this.win = iframe.contentWindow;
41489         } else {
41490 //            if (!Roo.get(this.frameId)) {
41491 //                return;
41492 //            }
41493 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41494 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41495             
41496             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41497                 return;
41498             }
41499             
41500             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41501             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41502         }
41503     },
41504     
41505     // private
41506     initEditor : function(){
41507         //console.log("INIT EDITOR");
41508         this.assignDocWin();
41509         
41510         
41511         
41512         this.doc.designMode="on";
41513         this.doc.open();
41514         this.doc.write(this.getDocMarkup());
41515         this.doc.close();
41516         
41517         var dbody = (this.doc.body || this.doc.documentElement);
41518         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41519         // this copies styles from the containing element into thsi one..
41520         // not sure why we need all of this..
41521         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41522         
41523         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41524         //ss['background-attachment'] = 'fixed'; // w3c
41525         dbody.bgProperties = 'fixed'; // ie
41526         //Roo.DomHelper.applyStyles(dbody, ss);
41527         Roo.EventManager.on(this.doc, {
41528             //'mousedown': this.onEditorEvent,
41529             'mouseup': this.onEditorEvent,
41530             'dblclick': this.onEditorEvent,
41531             'click': this.onEditorEvent,
41532             'keyup': this.onEditorEvent,
41533             buffer:100,
41534             scope: this
41535         });
41536         if(Roo.isGecko){
41537             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41538         }
41539         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41540             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41541         }
41542         this.initialized = true;
41543
41544         this.owner.fireEvent('initialize', this);
41545         this.pushValue();
41546     },
41547
41548     // private
41549     onDestroy : function(){
41550         
41551         
41552         
41553         if(this.rendered){
41554             
41555             //for (var i =0; i < this.toolbars.length;i++) {
41556             //    // fixme - ask toolbars for heights?
41557             //    this.toolbars[i].onDestroy();
41558            // }
41559             
41560             //this.wrap.dom.innerHTML = '';
41561             //this.wrap.remove();
41562         }
41563     },
41564
41565     // private
41566     onFirstFocus : function(){
41567         
41568         this.assignDocWin();
41569         
41570         
41571         this.activated = true;
41572          
41573     
41574         if(Roo.isGecko){ // prevent silly gecko errors
41575             this.win.focus();
41576             var s = this.win.getSelection();
41577             if(!s.focusNode || s.focusNode.nodeType != 3){
41578                 var r = s.getRangeAt(0);
41579                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41580                 r.collapse(true);
41581                 this.deferFocus();
41582             }
41583             try{
41584                 this.execCmd('useCSS', true);
41585                 this.execCmd('styleWithCSS', false);
41586             }catch(e){}
41587         }
41588         this.owner.fireEvent('activate', this);
41589     },
41590
41591     // private
41592     adjustFont: function(btn){
41593         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41594         //if(Roo.isSafari){ // safari
41595         //    adjust *= 2;
41596        // }
41597         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41598         if(Roo.isSafari){ // safari
41599             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41600             v =  (v < 10) ? 10 : v;
41601             v =  (v > 48) ? 48 : v;
41602             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41603             
41604         }
41605         
41606         
41607         v = Math.max(1, v+adjust);
41608         
41609         this.execCmd('FontSize', v  );
41610     },
41611
41612     onEditorEvent : function(e)
41613     {
41614         this.owner.fireEvent('editorevent', this, e);
41615       //  this.updateToolbar();
41616         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41617     },
41618
41619     insertTag : function(tg)
41620     {
41621         // could be a bit smarter... -> wrap the current selected tRoo..
41622         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41623             
41624             range = this.createRange(this.getSelection());
41625             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41626             wrappingNode.appendChild(range.extractContents());
41627             range.insertNode(wrappingNode);
41628
41629             return;
41630             
41631             
41632             
41633         }
41634         this.execCmd("formatblock",   tg);
41635         
41636     },
41637     
41638     insertText : function(txt)
41639     {
41640         
41641         
41642         var range = this.createRange();
41643         range.deleteContents();
41644                //alert(Sender.getAttribute('label'));
41645                
41646         range.insertNode(this.doc.createTextNode(txt));
41647     } ,
41648     
41649      
41650
41651     /**
41652      * Executes a Midas editor command on the editor document and performs necessary focus and
41653      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41654      * @param {String} cmd The Midas command
41655      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41656      */
41657     relayCmd : function(cmd, value){
41658         this.win.focus();
41659         this.execCmd(cmd, value);
41660         this.owner.fireEvent('editorevent', this);
41661         //this.updateToolbar();
41662         this.owner.deferFocus();
41663     },
41664
41665     /**
41666      * Executes a Midas editor command directly on the editor document.
41667      * For visual commands, you should use {@link #relayCmd} instead.
41668      * <b>This should only be called after the editor is initialized.</b>
41669      * @param {String} cmd The Midas command
41670      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41671      */
41672     execCmd : function(cmd, value){
41673         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41674         this.syncValue();
41675     },
41676  
41677  
41678    
41679     /**
41680      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41681      * to insert tRoo.
41682      * @param {String} text | dom node.. 
41683      */
41684     insertAtCursor : function(text)
41685     {
41686         
41687         
41688         
41689         if(!this.activated){
41690             return;
41691         }
41692         /*
41693         if(Roo.isIE){
41694             this.win.focus();
41695             var r = this.doc.selection.createRange();
41696             if(r){
41697                 r.collapse(true);
41698                 r.pasteHTML(text);
41699                 this.syncValue();
41700                 this.deferFocus();
41701             
41702             }
41703             return;
41704         }
41705         */
41706         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41707             this.win.focus();
41708             
41709             
41710             // from jquery ui (MIT licenced)
41711             var range, node;
41712             var win = this.win;
41713             
41714             if (win.getSelection && win.getSelection().getRangeAt) {
41715                 range = win.getSelection().getRangeAt(0);
41716                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41717                 range.insertNode(node);
41718             } else if (win.document.selection && win.document.selection.createRange) {
41719                 // no firefox support
41720                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41721                 win.document.selection.createRange().pasteHTML(txt);
41722             } else {
41723                 // no firefox support
41724                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41725                 this.execCmd('InsertHTML', txt);
41726             } 
41727             
41728             this.syncValue();
41729             
41730             this.deferFocus();
41731         }
41732     },
41733  // private
41734     mozKeyPress : function(e){
41735         if(e.ctrlKey){
41736             var c = e.getCharCode(), cmd;
41737           
41738             if(c > 0){
41739                 c = String.fromCharCode(c).toLowerCase();
41740                 switch(c){
41741                     case 'b':
41742                         cmd = 'bold';
41743                         break;
41744                     case 'i':
41745                         cmd = 'italic';
41746                         break;
41747                     
41748                     case 'u':
41749                         cmd = 'underline';
41750                         break;
41751                     
41752                     case 'v':
41753                         this.cleanUpPaste.defer(100, this);
41754                         return;
41755                         
41756                 }
41757                 if(cmd){
41758                     this.win.focus();
41759                     this.execCmd(cmd);
41760                     this.deferFocus();
41761                     e.preventDefault();
41762                 }
41763                 
41764             }
41765         }
41766     },
41767
41768     // private
41769     fixKeys : function(){ // load time branching for fastest keydown performance
41770         if(Roo.isIE){
41771             return function(e){
41772                 var k = e.getKey(), r;
41773                 if(k == e.TAB){
41774                     e.stopEvent();
41775                     r = this.doc.selection.createRange();
41776                     if(r){
41777                         r.collapse(true);
41778                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41779                         this.deferFocus();
41780                     }
41781                     return;
41782                 }
41783                 
41784                 if(k == e.ENTER){
41785                     r = this.doc.selection.createRange();
41786                     if(r){
41787                         var target = r.parentElement();
41788                         if(!target || target.tagName.toLowerCase() != 'li'){
41789                             e.stopEvent();
41790                             r.pasteHTML('<br />');
41791                             r.collapse(false);
41792                             r.select();
41793                         }
41794                     }
41795                 }
41796                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41797                     this.cleanUpPaste.defer(100, this);
41798                     return;
41799                 }
41800                 
41801                 
41802             };
41803         }else if(Roo.isOpera){
41804             return function(e){
41805                 var k = e.getKey();
41806                 if(k == e.TAB){
41807                     e.stopEvent();
41808                     this.win.focus();
41809                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41810                     this.deferFocus();
41811                 }
41812                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41813                     this.cleanUpPaste.defer(100, this);
41814                     return;
41815                 }
41816                 
41817             };
41818         }else if(Roo.isSafari){
41819             return function(e){
41820                 var k = e.getKey();
41821                 
41822                 if(k == e.TAB){
41823                     e.stopEvent();
41824                     this.execCmd('InsertText','\t');
41825                     this.deferFocus();
41826                     return;
41827                 }
41828                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41829                     this.cleanUpPaste.defer(100, this);
41830                     return;
41831                 }
41832                 
41833              };
41834         }
41835     }(),
41836     
41837     getAllAncestors: function()
41838     {
41839         var p = this.getSelectedNode();
41840         var a = [];
41841         if (!p) {
41842             a.push(p); // push blank onto stack..
41843             p = this.getParentElement();
41844         }
41845         
41846         
41847         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41848             a.push(p);
41849             p = p.parentNode;
41850         }
41851         a.push(this.doc.body);
41852         return a;
41853     },
41854     lastSel : false,
41855     lastSelNode : false,
41856     
41857     
41858     getSelection : function() 
41859     {
41860         this.assignDocWin();
41861         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41862     },
41863     
41864     getSelectedNode: function() 
41865     {
41866         // this may only work on Gecko!!!
41867         
41868         // should we cache this!!!!
41869         
41870         
41871         
41872          
41873         var range = this.createRange(this.getSelection()).cloneRange();
41874         
41875         if (Roo.isIE) {
41876             var parent = range.parentElement();
41877             while (true) {
41878                 var testRange = range.duplicate();
41879                 testRange.moveToElementText(parent);
41880                 if (testRange.inRange(range)) {
41881                     break;
41882                 }
41883                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41884                     break;
41885                 }
41886                 parent = parent.parentElement;
41887             }
41888             return parent;
41889         }
41890         
41891         // is ancestor a text element.
41892         var ac =  range.commonAncestorContainer;
41893         if (ac.nodeType == 3) {
41894             ac = ac.parentNode;
41895         }
41896         
41897         var ar = ac.childNodes;
41898          
41899         var nodes = [];
41900         var other_nodes = [];
41901         var has_other_nodes = false;
41902         for (var i=0;i<ar.length;i++) {
41903             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41904                 continue;
41905             }
41906             // fullly contained node.
41907             
41908             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41909                 nodes.push(ar[i]);
41910                 continue;
41911             }
41912             
41913             // probably selected..
41914             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41915                 other_nodes.push(ar[i]);
41916                 continue;
41917             }
41918             // outer..
41919             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41920                 continue;
41921             }
41922             
41923             
41924             has_other_nodes = true;
41925         }
41926         if (!nodes.length && other_nodes.length) {
41927             nodes= other_nodes;
41928         }
41929         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41930             return false;
41931         }
41932         
41933         return nodes[0];
41934     },
41935     createRange: function(sel)
41936     {
41937         // this has strange effects when using with 
41938         // top toolbar - not sure if it's a great idea.
41939         //this.editor.contentWindow.focus();
41940         if (typeof sel != "undefined") {
41941             try {
41942                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41943             } catch(e) {
41944                 return this.doc.createRange();
41945             }
41946         } else {
41947             return this.doc.createRange();
41948         }
41949     },
41950     getParentElement: function()
41951     {
41952         
41953         this.assignDocWin();
41954         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41955         
41956         var range = this.createRange(sel);
41957          
41958         try {
41959             var p = range.commonAncestorContainer;
41960             while (p.nodeType == 3) { // text node
41961                 p = p.parentNode;
41962             }
41963             return p;
41964         } catch (e) {
41965             return null;
41966         }
41967     
41968     },
41969     /***
41970      *
41971      * Range intersection.. the hard stuff...
41972      *  '-1' = before
41973      *  '0' = hits..
41974      *  '1' = after.
41975      *         [ -- selected range --- ]
41976      *   [fail]                        [fail]
41977      *
41978      *    basically..
41979      *      if end is before start or  hits it. fail.
41980      *      if start is after end or hits it fail.
41981      *
41982      *   if either hits (but other is outside. - then it's not 
41983      *   
41984      *    
41985      **/
41986     
41987     
41988     // @see http://www.thismuchiknow.co.uk/?p=64.
41989     rangeIntersectsNode : function(range, node)
41990     {
41991         var nodeRange = node.ownerDocument.createRange();
41992         try {
41993             nodeRange.selectNode(node);
41994         } catch (e) {
41995             nodeRange.selectNodeContents(node);
41996         }
41997     
41998         var rangeStartRange = range.cloneRange();
41999         rangeStartRange.collapse(true);
42000     
42001         var rangeEndRange = range.cloneRange();
42002         rangeEndRange.collapse(false);
42003     
42004         var nodeStartRange = nodeRange.cloneRange();
42005         nodeStartRange.collapse(true);
42006     
42007         var nodeEndRange = nodeRange.cloneRange();
42008         nodeEndRange.collapse(false);
42009     
42010         return rangeStartRange.compareBoundaryPoints(
42011                  Range.START_TO_START, nodeEndRange) == -1 &&
42012                rangeEndRange.compareBoundaryPoints(
42013                  Range.START_TO_START, nodeStartRange) == 1;
42014         
42015          
42016     },
42017     rangeCompareNode : function(range, node)
42018     {
42019         var nodeRange = node.ownerDocument.createRange();
42020         try {
42021             nodeRange.selectNode(node);
42022         } catch (e) {
42023             nodeRange.selectNodeContents(node);
42024         }
42025         
42026         
42027         range.collapse(true);
42028     
42029         nodeRange.collapse(true);
42030      
42031         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42032         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42033          
42034         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42035         
42036         var nodeIsBefore   =  ss == 1;
42037         var nodeIsAfter    = ee == -1;
42038         
42039         if (nodeIsBefore && nodeIsAfter)
42040             return 0; // outer
42041         if (!nodeIsBefore && nodeIsAfter)
42042             return 1; //right trailed.
42043         
42044         if (nodeIsBefore && !nodeIsAfter)
42045             return 2;  // left trailed.
42046         // fully contined.
42047         return 3;
42048     },
42049
42050     // private? - in a new class?
42051     cleanUpPaste :  function()
42052     {
42053         // cleans up the whole document..
42054         Roo.log('cleanuppaste');
42055         
42056         this.cleanUpChildren(this.doc.body);
42057         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42058         if (clean != this.doc.body.innerHTML) {
42059             this.doc.body.innerHTML = clean;
42060         }
42061         
42062     },
42063     
42064     cleanWordChars : function(input) {// change the chars to hex code
42065         var he = Roo.HtmlEditorCore;
42066         
42067         var output = input;
42068         Roo.each(he.swapCodes, function(sw) { 
42069             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42070             
42071             output = output.replace(swapper, sw[1]);
42072         });
42073         
42074         return output;
42075     },
42076     
42077     
42078     cleanUpChildren : function (n)
42079     {
42080         if (!n.childNodes.length) {
42081             return;
42082         }
42083         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42084            this.cleanUpChild(n.childNodes[i]);
42085         }
42086     },
42087     
42088     
42089         
42090     
42091     cleanUpChild : function (node)
42092     {
42093         var ed = this;
42094         //console.log(node);
42095         if (node.nodeName == "#text") {
42096             // clean up silly Windows -- stuff?
42097             return; 
42098         }
42099         if (node.nodeName == "#comment") {
42100             node.parentNode.removeChild(node);
42101             // clean up silly Windows -- stuff?
42102             return; 
42103         }
42104         var lcname = node.tagName.toLowerCase();
42105         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42106         // whitelist of tags..
42107         
42108         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42109             // remove node.
42110             node.parentNode.removeChild(node);
42111             return;
42112             
42113         }
42114         
42115         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42116         
42117         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42118         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42119         
42120         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42121         //    remove_keep_children = true;
42122         //}
42123         
42124         if (remove_keep_children) {
42125             this.cleanUpChildren(node);
42126             // inserts everything just before this node...
42127             while (node.childNodes.length) {
42128                 var cn = node.childNodes[0];
42129                 node.removeChild(cn);
42130                 node.parentNode.insertBefore(cn, node);
42131             }
42132             node.parentNode.removeChild(node);
42133             return;
42134         }
42135         
42136         if (!node.attributes || !node.attributes.length) {
42137             this.cleanUpChildren(node);
42138             return;
42139         }
42140         
42141         function cleanAttr(n,v)
42142         {
42143             
42144             if (v.match(/^\./) || v.match(/^\//)) {
42145                 return;
42146             }
42147             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42148                 return;
42149             }
42150             if (v.match(/^#/)) {
42151                 return;
42152             }
42153 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42154             node.removeAttribute(n);
42155             
42156         }
42157         
42158         var cwhite = this.cwhite;
42159         var cblack = this.cblack;
42160             
42161         function cleanStyle(n,v)
42162         {
42163             if (v.match(/expression/)) { //XSS?? should we even bother..
42164                 node.removeAttribute(n);
42165                 return;
42166             }
42167             
42168             var parts = v.split(/;/);
42169             var clean = [];
42170             
42171             Roo.each(parts, function(p) {
42172                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42173                 if (!p.length) {
42174                     return true;
42175                 }
42176                 var l = p.split(':').shift().replace(/\s+/g,'');
42177                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42178                 
42179                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42180 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42181                     //node.removeAttribute(n);
42182                     return true;
42183                 }
42184                 //Roo.log()
42185                 // only allow 'c whitelisted system attributes'
42186                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42187 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42188                     //node.removeAttribute(n);
42189                     return true;
42190                 }
42191                 
42192                 
42193                  
42194                 
42195                 clean.push(p);
42196                 return true;
42197             });
42198             if (clean.length) { 
42199                 node.setAttribute(n, clean.join(';'));
42200             } else {
42201                 node.removeAttribute(n);
42202             }
42203             
42204         }
42205         
42206         
42207         for (var i = node.attributes.length-1; i > -1 ; i--) {
42208             var a = node.attributes[i];
42209             //console.log(a);
42210             
42211             if (a.name.toLowerCase().substr(0,2)=='on')  {
42212                 node.removeAttribute(a.name);
42213                 continue;
42214             }
42215             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42216                 node.removeAttribute(a.name);
42217                 continue;
42218             }
42219             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42220                 cleanAttr(a.name,a.value); // fixme..
42221                 continue;
42222             }
42223             if (a.name == 'style') {
42224                 cleanStyle(a.name,a.value);
42225                 continue;
42226             }
42227             /// clean up MS crap..
42228             // tecnically this should be a list of valid class'es..
42229             
42230             
42231             if (a.name == 'class') {
42232                 if (a.value.match(/^Mso/)) {
42233                     node.className = '';
42234                 }
42235                 
42236                 if (a.value.match(/body/)) {
42237                     node.className = '';
42238                 }
42239                 continue;
42240             }
42241             
42242             // style cleanup!?
42243             // class cleanup?
42244             
42245         }
42246         
42247         
42248         this.cleanUpChildren(node);
42249         
42250         
42251     },
42252     /**
42253      * Clean up MS wordisms...
42254      */
42255     cleanWord : function(node)
42256     {
42257         var _t = this;
42258         var cleanWordChildren = function()
42259         {
42260             if (!node.childNodes.length) {
42261                 return;
42262             }
42263             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42264                _t.cleanWord(node.childNodes[i]);
42265             }
42266         }
42267         
42268         
42269         if (!node) {
42270             this.cleanWord(this.doc.body);
42271             return;
42272         }
42273         if (node.nodeName == "#text") {
42274             // clean up silly Windows -- stuff?
42275             return; 
42276         }
42277         if (node.nodeName == "#comment") {
42278             node.parentNode.removeChild(node);
42279             // clean up silly Windows -- stuff?
42280             return; 
42281         }
42282         
42283         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42284             node.parentNode.removeChild(node);
42285             return;
42286         }
42287         
42288         // remove - but keep children..
42289         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42290             while (node.childNodes.length) {
42291                 var cn = node.childNodes[0];
42292                 node.removeChild(cn);
42293                 node.parentNode.insertBefore(cn, node);
42294             }
42295             node.parentNode.removeChild(node);
42296             cleanWordChildren();
42297             return;
42298         }
42299         // clean styles
42300         if (node.className.length) {
42301             
42302             var cn = node.className.split(/\W+/);
42303             var cna = [];
42304             Roo.each(cn, function(cls) {
42305                 if (cls.match(/Mso[a-zA-Z]+/)) {
42306                     return;
42307                 }
42308                 cna.push(cls);
42309             });
42310             node.className = cna.length ? cna.join(' ') : '';
42311             if (!cna.length) {
42312                 node.removeAttribute("class");
42313             }
42314         }
42315         
42316         if (node.hasAttribute("lang")) {
42317             node.removeAttribute("lang");
42318         }
42319         
42320         if (node.hasAttribute("style")) {
42321             
42322             var styles = node.getAttribute("style").split(";");
42323             var nstyle = [];
42324             Roo.each(styles, function(s) {
42325                 if (!s.match(/:/)) {
42326                     return;
42327                 }
42328                 var kv = s.split(":");
42329                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42330                     return;
42331                 }
42332                 // what ever is left... we allow.
42333                 nstyle.push(s);
42334             });
42335             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42336             if (!nstyle.length) {
42337                 node.removeAttribute('style');
42338             }
42339         }
42340         
42341         cleanWordChildren();
42342         
42343         
42344     },
42345     domToHTML : function(currentElement, depth, nopadtext) {
42346         
42347         depth = depth || 0;
42348         nopadtext = nopadtext || false;
42349     
42350         if (!currentElement) {
42351             return this.domToHTML(this.doc.body);
42352         }
42353         
42354         //Roo.log(currentElement);
42355         var j;
42356         var allText = false;
42357         var nodeName = currentElement.nodeName;
42358         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42359         
42360         if  (nodeName == '#text') {
42361             
42362             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42363         }
42364         
42365         
42366         var ret = '';
42367         if (nodeName != 'BODY') {
42368              
42369             var i = 0;
42370             // Prints the node tagName, such as <A>, <IMG>, etc
42371             if (tagName) {
42372                 var attr = [];
42373                 for(i = 0; i < currentElement.attributes.length;i++) {
42374                     // quoting?
42375                     var aname = currentElement.attributes.item(i).name;
42376                     if (!currentElement.attributes.item(i).value.length) {
42377                         continue;
42378                     }
42379                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42380                 }
42381                 
42382                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42383             } 
42384             else {
42385                 
42386                 // eack
42387             }
42388         } else {
42389             tagName = false;
42390         }
42391         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42392             return ret;
42393         }
42394         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42395             nopadtext = true;
42396         }
42397         
42398         
42399         // Traverse the tree
42400         i = 0;
42401         var currentElementChild = currentElement.childNodes.item(i);
42402         var allText = true;
42403         var innerHTML  = '';
42404         lastnode = '';
42405         while (currentElementChild) {
42406             // Formatting code (indent the tree so it looks nice on the screen)
42407             var nopad = nopadtext;
42408             if (lastnode == 'SPAN') {
42409                 nopad  = true;
42410             }
42411             // text
42412             if  (currentElementChild.nodeName == '#text') {
42413                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42414                 toadd = nopadtext ? toadd : toadd.trim();
42415                 if (!nopad && toadd.length > 80) {
42416                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42417                 }
42418                 innerHTML  += toadd;
42419                 
42420                 i++;
42421                 currentElementChild = currentElement.childNodes.item(i);
42422                 lastNode = '';
42423                 continue;
42424             }
42425             allText = false;
42426             
42427             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42428                 
42429             // Recursively traverse the tree structure of the child node
42430             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42431             lastnode = currentElementChild.nodeName;
42432             i++;
42433             currentElementChild=currentElement.childNodes.item(i);
42434         }
42435         
42436         ret += innerHTML;
42437         
42438         if (!allText) {
42439                 // The remaining code is mostly for formatting the tree
42440             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42441         }
42442         
42443         
42444         if (tagName) {
42445             ret+= "</"+tagName+">";
42446         }
42447         return ret;
42448         
42449     },
42450         
42451     applyBlacklists : function()
42452     {
42453         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42454         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42455         
42456         this.white = [];
42457         this.black = [];
42458         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42459             if (b.indexOf(tag) > -1) {
42460                 return;
42461             }
42462             this.white.push(tag);
42463             
42464         }, this);
42465         
42466         Roo.each(w, function(tag) {
42467             if (b.indexOf(tag) > -1) {
42468                 return;
42469             }
42470             if (this.white.indexOf(tag) > -1) {
42471                 return;
42472             }
42473             this.white.push(tag);
42474             
42475         }, this);
42476         
42477         
42478         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42479             if (w.indexOf(tag) > -1) {
42480                 return;
42481             }
42482             this.black.push(tag);
42483             
42484         }, this);
42485         
42486         Roo.each(b, function(tag) {
42487             if (w.indexOf(tag) > -1) {
42488                 return;
42489             }
42490             if (this.black.indexOf(tag) > -1) {
42491                 return;
42492             }
42493             this.black.push(tag);
42494             
42495         }, this);
42496         
42497         
42498         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42499         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42500         
42501         this.cwhite = [];
42502         this.cblack = [];
42503         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42504             if (b.indexOf(tag) > -1) {
42505                 return;
42506             }
42507             this.cwhite.push(tag);
42508             
42509         }, this);
42510         
42511         Roo.each(w, function(tag) {
42512             if (b.indexOf(tag) > -1) {
42513                 return;
42514             }
42515             if (this.cwhite.indexOf(tag) > -1) {
42516                 return;
42517             }
42518             this.cwhite.push(tag);
42519             
42520         }, this);
42521         
42522         
42523         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42524             if (w.indexOf(tag) > -1) {
42525                 return;
42526             }
42527             this.cblack.push(tag);
42528             
42529         }, this);
42530         
42531         Roo.each(b, function(tag) {
42532             if (w.indexOf(tag) > -1) {
42533                 return;
42534             }
42535             if (this.cblack.indexOf(tag) > -1) {
42536                 return;
42537             }
42538             this.cblack.push(tag);
42539             
42540         }, this);
42541     },
42542     
42543     setStylesheets : function(stylesheets)
42544     {
42545         if(typeof(stylesheets) == 'string'){
42546             Roo.get(this.iframe.contentDocument.head).createChild({
42547                 tag : 'link',
42548                 rel : 'stylesheet',
42549                 type : 'text/css',
42550                 href : stylesheets
42551             });
42552             
42553             return;
42554         }
42555         var _this = this;
42556      
42557         Roo.each(stylesheets, function(s) {
42558             if(!s.length){
42559                 return;
42560             }
42561             
42562             Roo.get(_this.iframe.contentDocument.head).createChild({
42563                 tag : 'link',
42564                 rel : 'stylesheet',
42565                 type : 'text/css',
42566                 href : s
42567             });
42568         });
42569
42570         
42571     },
42572     
42573     removeStylesheets : function()
42574     {
42575         var _this = this;
42576         
42577         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42578             s.remove();
42579         });
42580     }
42581     
42582     // hide stuff that is not compatible
42583     /**
42584      * @event blur
42585      * @hide
42586      */
42587     /**
42588      * @event change
42589      * @hide
42590      */
42591     /**
42592      * @event focus
42593      * @hide
42594      */
42595     /**
42596      * @event specialkey
42597      * @hide
42598      */
42599     /**
42600      * @cfg {String} fieldClass @hide
42601      */
42602     /**
42603      * @cfg {String} focusClass @hide
42604      */
42605     /**
42606      * @cfg {String} autoCreate @hide
42607      */
42608     /**
42609      * @cfg {String} inputType @hide
42610      */
42611     /**
42612      * @cfg {String} invalidClass @hide
42613      */
42614     /**
42615      * @cfg {String} invalidText @hide
42616      */
42617     /**
42618      * @cfg {String} msgFx @hide
42619      */
42620     /**
42621      * @cfg {String} validateOnBlur @hide
42622      */
42623 });
42624
42625 Roo.HtmlEditorCore.white = [
42626         'area', 'br', 'img', 'input', 'hr', 'wbr',
42627         
42628        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42629        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42630        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42631        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42632        'table',   'ul',         'xmp', 
42633        
42634        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42635       'thead',   'tr', 
42636      
42637       'dir', 'menu', 'ol', 'ul', 'dl',
42638        
42639       'embed',  'object'
42640 ];
42641
42642
42643 Roo.HtmlEditorCore.black = [
42644     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42645         'applet', // 
42646         'base',   'basefont', 'bgsound', 'blink',  'body', 
42647         'frame',  'frameset', 'head',    'html',   'ilayer', 
42648         'iframe', 'layer',  'link',     'meta',    'object',   
42649         'script', 'style' ,'title',  'xml' // clean later..
42650 ];
42651 Roo.HtmlEditorCore.clean = [
42652     'script', 'style', 'title', 'xml'
42653 ];
42654 Roo.HtmlEditorCore.remove = [
42655     'font'
42656 ];
42657 // attributes..
42658
42659 Roo.HtmlEditorCore.ablack = [
42660     'on'
42661 ];
42662     
42663 Roo.HtmlEditorCore.aclean = [ 
42664     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42665 ];
42666
42667 // protocols..
42668 Roo.HtmlEditorCore.pwhite= [
42669         'http',  'https',  'mailto'
42670 ];
42671
42672 // white listed style attributes.
42673 Roo.HtmlEditorCore.cwhite= [
42674       //  'text-align', /// default is to allow most things..
42675       
42676          
42677 //        'font-size'//??
42678 ];
42679
42680 // black listed style attributes.
42681 Roo.HtmlEditorCore.cblack= [
42682       //  'font-size' -- this can be set by the project 
42683 ];
42684
42685
42686 Roo.HtmlEditorCore.swapCodes   =[ 
42687     [    8211, "--" ], 
42688     [    8212, "--" ], 
42689     [    8216,  "'" ],  
42690     [    8217, "'" ],  
42691     [    8220, '"' ],  
42692     [    8221, '"' ],  
42693     [    8226, "*" ],  
42694     [    8230, "..." ]
42695 ]; 
42696
42697     //<script type="text/javascript">
42698
42699 /*
42700  * Ext JS Library 1.1.1
42701  * Copyright(c) 2006-2007, Ext JS, LLC.
42702  * Licence LGPL
42703  * 
42704  */
42705  
42706  
42707 Roo.form.HtmlEditor = function(config){
42708     
42709     
42710     
42711     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42712     
42713     if (!this.toolbars) {
42714         this.toolbars = [];
42715     }
42716     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42717     
42718     
42719 };
42720
42721 /**
42722  * @class Roo.form.HtmlEditor
42723  * @extends Roo.form.Field
42724  * Provides a lightweight HTML Editor component.
42725  *
42726  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42727  * 
42728  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42729  * supported by this editor.</b><br/><br/>
42730  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42731  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42732  */
42733 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42734     /**
42735      * @cfg {Boolean} clearUp
42736      */
42737     clearUp : true,
42738       /**
42739      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42740      */
42741     toolbars : false,
42742    
42743      /**
42744      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42745      *                        Roo.resizable.
42746      */
42747     resizable : false,
42748      /**
42749      * @cfg {Number} height (in pixels)
42750      */   
42751     height: 300,
42752    /**
42753      * @cfg {Number} width (in pixels)
42754      */   
42755     width: 500,
42756     
42757     /**
42758      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42759      * 
42760      */
42761     stylesheets: false,
42762     
42763     
42764      /**
42765      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42766      * 
42767      */
42768     cblack: false,
42769     /**
42770      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42771      * 
42772      */
42773     cwhite: false,
42774     
42775      /**
42776      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42777      * 
42778      */
42779     black: false,
42780     /**
42781      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42782      * 
42783      */
42784     white: false,
42785     
42786     // id of frame..
42787     frameId: false,
42788     
42789     // private properties
42790     validationEvent : false,
42791     deferHeight: true,
42792     initialized : false,
42793     activated : false,
42794     
42795     onFocus : Roo.emptyFn,
42796     iframePad:3,
42797     hideMode:'offsets',
42798     
42799     actionMode : 'container', // defaults to hiding it...
42800     
42801     defaultAutoCreate : { // modified by initCompnoent..
42802         tag: "textarea",
42803         style:"width:500px;height:300px;",
42804         autocomplete: "new-password"
42805     },
42806
42807     // private
42808     initComponent : function(){
42809         this.addEvents({
42810             /**
42811              * @event initialize
42812              * Fires when the editor is fully initialized (including the iframe)
42813              * @param {HtmlEditor} this
42814              */
42815             initialize: true,
42816             /**
42817              * @event activate
42818              * Fires when the editor is first receives the focus. Any insertion must wait
42819              * until after this event.
42820              * @param {HtmlEditor} this
42821              */
42822             activate: true,
42823              /**
42824              * @event beforesync
42825              * Fires before the textarea is updated with content from the editor iframe. Return false
42826              * to cancel the sync.
42827              * @param {HtmlEditor} this
42828              * @param {String} html
42829              */
42830             beforesync: true,
42831              /**
42832              * @event beforepush
42833              * Fires before the iframe editor is updated with content from the textarea. Return false
42834              * to cancel the push.
42835              * @param {HtmlEditor} this
42836              * @param {String} html
42837              */
42838             beforepush: true,
42839              /**
42840              * @event sync
42841              * Fires when the textarea is updated with content from the editor iframe.
42842              * @param {HtmlEditor} this
42843              * @param {String} html
42844              */
42845             sync: true,
42846              /**
42847              * @event push
42848              * Fires when the iframe editor is updated with content from the textarea.
42849              * @param {HtmlEditor} this
42850              * @param {String} html
42851              */
42852             push: true,
42853              /**
42854              * @event editmodechange
42855              * Fires when the editor switches edit modes
42856              * @param {HtmlEditor} this
42857              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42858              */
42859             editmodechange: true,
42860             /**
42861              * @event editorevent
42862              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42863              * @param {HtmlEditor} this
42864              */
42865             editorevent: true,
42866             /**
42867              * @event firstfocus
42868              * Fires when on first focus - needed by toolbars..
42869              * @param {HtmlEditor} this
42870              */
42871             firstfocus: true,
42872             /**
42873              * @event autosave
42874              * Auto save the htmlEditor value as a file into Events
42875              * @param {HtmlEditor} this
42876              */
42877             autosave: true,
42878             /**
42879              * @event savedpreview
42880              * preview the saved version of htmlEditor
42881              * @param {HtmlEditor} this
42882              */
42883             savedpreview: true,
42884             
42885             /**
42886             * @event stylesheetsclick
42887             * Fires when press the Sytlesheets button
42888             * @param {Roo.HtmlEditorCore} this
42889             */
42890             stylesheetsclick: true
42891         });
42892         this.defaultAutoCreate =  {
42893             tag: "textarea",
42894             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42895             autocomplete: "new-password"
42896         };
42897     },
42898
42899     /**
42900      * Protected method that will not generally be called directly. It
42901      * is called when the editor creates its toolbar. Override this method if you need to
42902      * add custom toolbar buttons.
42903      * @param {HtmlEditor} editor
42904      */
42905     createToolbar : function(editor){
42906         Roo.log("create toolbars");
42907         if (!editor.toolbars || !editor.toolbars.length) {
42908             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42909         }
42910         
42911         for (var i =0 ; i < editor.toolbars.length;i++) {
42912             editor.toolbars[i] = Roo.factory(
42913                     typeof(editor.toolbars[i]) == 'string' ?
42914                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42915                 Roo.form.HtmlEditor);
42916             editor.toolbars[i].init(editor);
42917         }
42918          
42919         
42920     },
42921
42922      
42923     // private
42924     onRender : function(ct, position)
42925     {
42926         var _t = this;
42927         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42928         
42929         this.wrap = this.el.wrap({
42930             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42931         });
42932         
42933         this.editorcore.onRender(ct, position);
42934          
42935         if (this.resizable) {
42936             this.resizeEl = new Roo.Resizable(this.wrap, {
42937                 pinned : true,
42938                 wrap: true,
42939                 dynamic : true,
42940                 minHeight : this.height,
42941                 height: this.height,
42942                 handles : this.resizable,
42943                 width: this.width,
42944                 listeners : {
42945                     resize : function(r, w, h) {
42946                         _t.onResize(w,h); // -something
42947                     }
42948                 }
42949             });
42950             
42951         }
42952         this.createToolbar(this);
42953        
42954         
42955         if(!this.width){
42956             this.setSize(this.wrap.getSize());
42957         }
42958         if (this.resizeEl) {
42959             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42960             // should trigger onReize..
42961         }
42962         
42963         this.keyNav = new Roo.KeyNav(this.el, {
42964             
42965             "tab" : function(e){
42966                 e.preventDefault();
42967                 
42968                 var value = this.getValue();
42969                 
42970                 var start = this.el.dom.selectionStart;
42971                 var end = this.el.dom.selectionEnd;
42972                 
42973                 if(!e.shiftKey){
42974                     
42975                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
42976                     this.el.dom.setSelectionRange(end + 1, end + 1);
42977                     return;
42978                 }
42979                 
42980                 var f = value.substring(0, start).split("\t");
42981                 
42982                 if(f.pop().length != 0){
42983                     return;
42984                 }
42985                 
42986                 this.setValue(f.join("\t") + value.substring(end));
42987                 this.el.dom.setSelectionRange(start - 1, start - 1);
42988                 
42989             },
42990             
42991             "home" : function(e){
42992                 e.preventDefault();
42993                 
42994                 var curr = this.el.dom.selectionStart;
42995                 var lines = this.getValue().split("\n");
42996                 
42997                 if(!lines.length){
42998                     return;
42999                 }
43000                 
43001                 if(e.ctrlKey){
43002                     this.el.dom.setSelectionRange(0, 0);
43003                     return;
43004                 }
43005                 
43006                 var pos = 0;
43007                 
43008                 for (var i = 0; i < lines.length;i++) {
43009                     pos += lines[i].length;
43010                     
43011                     if(i != 0){
43012                         pos += 1;
43013                     }
43014                     
43015                     if(pos < curr){
43016                         continue;
43017                     }
43018                     
43019                     pos -= lines[i].length;
43020                     
43021                     break;
43022                 }
43023                 
43024                 if(!e.shiftKey){
43025                     this.el.dom.setSelectionRange(pos, pos);
43026                     return;
43027                 }
43028                 
43029                 this.el.dom.selectionStart = pos;
43030                 this.el.dom.selectionEnd = curr;
43031             },
43032             
43033             "end" : function(e){
43034                 e.preventDefault();
43035                 
43036                 var curr = this.el.dom.selectionStart;
43037                 var lines = this.getValue().split("\n");
43038                 
43039                 if(!lines.length){
43040                     return;
43041                 }
43042                 
43043                 if(e.ctrlKey){
43044                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43045                     return;
43046                 }
43047                 
43048                 var pos = 0;
43049                 
43050                 for (var i = 0; i < lines.length;i++) {
43051                     
43052                     pos += lines[i].length;
43053                     
43054                     if(i != 0){
43055                         pos += 1;
43056                     }
43057                     
43058                     if(pos < curr){
43059                         continue;
43060                     }
43061                     
43062                     break;
43063                 }
43064                 
43065                 if(!e.shiftKey){
43066                     this.el.dom.setSelectionRange(pos, pos);
43067                     return;
43068                 }
43069                 
43070                 this.el.dom.selectionStart = curr;
43071                 this.el.dom.selectionEnd = pos;
43072             },
43073
43074             scope : this,
43075
43076             doRelay : function(foo, bar, hname){
43077                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43078             },
43079
43080             forceKeyDown: true
43081         });
43082         
43083 //        if(this.autosave && this.w){
43084 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43085 //        }
43086     },
43087
43088     // private
43089     onResize : function(w, h)
43090     {
43091         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43092         var ew = false;
43093         var eh = false;
43094         
43095         if(this.el ){
43096             if(typeof w == 'number'){
43097                 var aw = w - this.wrap.getFrameWidth('lr');
43098                 this.el.setWidth(this.adjustWidth('textarea', aw));
43099                 ew = aw;
43100             }
43101             if(typeof h == 'number'){
43102                 var tbh = 0;
43103                 for (var i =0; i < this.toolbars.length;i++) {
43104                     // fixme - ask toolbars for heights?
43105                     tbh += this.toolbars[i].tb.el.getHeight();
43106                     if (this.toolbars[i].footer) {
43107                         tbh += this.toolbars[i].footer.el.getHeight();
43108                     }
43109                 }
43110                 
43111                 
43112                 
43113                 
43114                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43115                 ah -= 5; // knock a few pixes off for look..
43116 //                Roo.log(ah);
43117                 this.el.setHeight(this.adjustWidth('textarea', ah));
43118                 var eh = ah;
43119             }
43120         }
43121         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43122         this.editorcore.onResize(ew,eh);
43123         
43124     },
43125
43126     /**
43127      * Toggles the editor between standard and source edit mode.
43128      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43129      */
43130     toggleSourceEdit : function(sourceEditMode)
43131     {
43132         this.editorcore.toggleSourceEdit(sourceEditMode);
43133         
43134         if(this.editorcore.sourceEditMode){
43135             Roo.log('editor - showing textarea');
43136             
43137 //            Roo.log('in');
43138 //            Roo.log(this.syncValue());
43139             this.editorcore.syncValue();
43140             this.el.removeClass('x-hidden');
43141             this.el.dom.removeAttribute('tabIndex');
43142             this.el.focus();
43143             
43144             for (var i = 0; i < this.toolbars.length; i++) {
43145                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43146                     this.toolbars[i].tb.hide();
43147                     this.toolbars[i].footer.hide();
43148                 }
43149             }
43150             
43151         }else{
43152             Roo.log('editor - hiding textarea');
43153 //            Roo.log('out')
43154 //            Roo.log(this.pushValue()); 
43155             this.editorcore.pushValue();
43156             
43157             this.el.addClass('x-hidden');
43158             this.el.dom.setAttribute('tabIndex', -1);
43159             
43160             for (var i = 0; i < this.toolbars.length; i++) {
43161                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43162                     this.toolbars[i].tb.show();
43163                     this.toolbars[i].footer.show();
43164                 }
43165             }
43166             
43167             //this.deferFocus();
43168         }
43169         
43170         this.setSize(this.wrap.getSize());
43171         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43172         
43173         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43174     },
43175  
43176     // private (for BoxComponent)
43177     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43178
43179     // private (for BoxComponent)
43180     getResizeEl : function(){
43181         return this.wrap;
43182     },
43183
43184     // private (for BoxComponent)
43185     getPositionEl : function(){
43186         return this.wrap;
43187     },
43188
43189     // private
43190     initEvents : function(){
43191         this.originalValue = this.getValue();
43192     },
43193
43194     /**
43195      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43196      * @method
43197      */
43198     markInvalid : Roo.emptyFn,
43199     /**
43200      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43201      * @method
43202      */
43203     clearInvalid : Roo.emptyFn,
43204
43205     setValue : function(v){
43206         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43207         this.editorcore.pushValue();
43208     },
43209
43210      
43211     // private
43212     deferFocus : function(){
43213         this.focus.defer(10, this);
43214     },
43215
43216     // doc'ed in Field
43217     focus : function(){
43218         this.editorcore.focus();
43219         
43220     },
43221       
43222
43223     // private
43224     onDestroy : function(){
43225         
43226         
43227         
43228         if(this.rendered){
43229             
43230             for (var i =0; i < this.toolbars.length;i++) {
43231                 // fixme - ask toolbars for heights?
43232                 this.toolbars[i].onDestroy();
43233             }
43234             
43235             this.wrap.dom.innerHTML = '';
43236             this.wrap.remove();
43237         }
43238     },
43239
43240     // private
43241     onFirstFocus : function(){
43242         //Roo.log("onFirstFocus");
43243         this.editorcore.onFirstFocus();
43244          for (var i =0; i < this.toolbars.length;i++) {
43245             this.toolbars[i].onFirstFocus();
43246         }
43247         
43248     },
43249     
43250     // private
43251     syncValue : function()
43252     {
43253         this.editorcore.syncValue();
43254     },
43255     
43256     pushValue : function()
43257     {
43258         this.editorcore.pushValue();
43259     },
43260     
43261     setStylesheets : function(stylesheets)
43262     {
43263         this.editorcore.setStylesheets(stylesheets);
43264     },
43265     
43266     removeStylesheets : function()
43267     {
43268         this.editorcore.removeStylesheets();
43269     }
43270      
43271     
43272     // hide stuff that is not compatible
43273     /**
43274      * @event blur
43275      * @hide
43276      */
43277     /**
43278      * @event change
43279      * @hide
43280      */
43281     /**
43282      * @event focus
43283      * @hide
43284      */
43285     /**
43286      * @event specialkey
43287      * @hide
43288      */
43289     /**
43290      * @cfg {String} fieldClass @hide
43291      */
43292     /**
43293      * @cfg {String} focusClass @hide
43294      */
43295     /**
43296      * @cfg {String} autoCreate @hide
43297      */
43298     /**
43299      * @cfg {String} inputType @hide
43300      */
43301     /**
43302      * @cfg {String} invalidClass @hide
43303      */
43304     /**
43305      * @cfg {String} invalidText @hide
43306      */
43307     /**
43308      * @cfg {String} msgFx @hide
43309      */
43310     /**
43311      * @cfg {String} validateOnBlur @hide
43312      */
43313 });
43314  
43315     // <script type="text/javascript">
43316 /*
43317  * Based on
43318  * Ext JS Library 1.1.1
43319  * Copyright(c) 2006-2007, Ext JS, LLC.
43320  *  
43321  
43322  */
43323
43324 /**
43325  * @class Roo.form.HtmlEditorToolbar1
43326  * Basic Toolbar
43327  * 
43328  * Usage:
43329  *
43330  new Roo.form.HtmlEditor({
43331     ....
43332     toolbars : [
43333         new Roo.form.HtmlEditorToolbar1({
43334             disable : { fonts: 1 , format: 1, ..., ... , ...],
43335             btns : [ .... ]
43336         })
43337     }
43338      
43339  * 
43340  * @cfg {Object} disable List of elements to disable..
43341  * @cfg {Array} btns List of additional buttons.
43342  * 
43343  * 
43344  * NEEDS Extra CSS? 
43345  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43346  */
43347  
43348 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43349 {
43350     
43351     Roo.apply(this, config);
43352     
43353     // default disabled, based on 'good practice'..
43354     this.disable = this.disable || {};
43355     Roo.applyIf(this.disable, {
43356         fontSize : true,
43357         colors : true,
43358         specialElements : true
43359     });
43360     
43361     
43362     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43363     // dont call parent... till later.
43364 }
43365
43366 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43367     
43368     tb: false,
43369     
43370     rendered: false,
43371     
43372     editor : false,
43373     editorcore : false,
43374     /**
43375      * @cfg {Object} disable  List of toolbar elements to disable
43376          
43377      */
43378     disable : false,
43379     
43380     
43381      /**
43382      * @cfg {String} createLinkText The default text for the create link prompt
43383      */
43384     createLinkText : 'Please enter the URL for the link:',
43385     /**
43386      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43387      */
43388     defaultLinkValue : 'http:/'+'/',
43389    
43390     
43391       /**
43392      * @cfg {Array} fontFamilies An array of available font families
43393      */
43394     fontFamilies : [
43395         'Arial',
43396         'Courier New',
43397         'Tahoma',
43398         'Times New Roman',
43399         'Verdana'
43400     ],
43401     
43402     specialChars : [
43403            "&#169;",
43404           "&#174;",     
43405           "&#8482;",    
43406           "&#163;" ,    
43407          // "&#8212;",    
43408           "&#8230;",    
43409           "&#247;" ,    
43410         //  "&#225;" ,     ?? a acute?
43411            "&#8364;"    , //Euro
43412        //   "&#8220;"    ,
43413         //  "&#8221;"    ,
43414         //  "&#8226;"    ,
43415           "&#176;"  //   , // degrees
43416
43417          // "&#233;"     , // e ecute
43418          // "&#250;"     , // u ecute?
43419     ],
43420     
43421     specialElements : [
43422         {
43423             text: "Insert Table",
43424             xtype: 'MenuItem',
43425             xns : Roo.Menu,
43426             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43427                 
43428         },
43429         {    
43430             text: "Insert Image",
43431             xtype: 'MenuItem',
43432             xns : Roo.Menu,
43433             ihtml : '<img src="about:blank"/>'
43434             
43435         }
43436         
43437          
43438     ],
43439     
43440     
43441     inputElements : [ 
43442             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43443             "input:submit", "input:button", "select", "textarea", "label" ],
43444     formats : [
43445         ["p"] ,  
43446         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43447         ["pre"],[ "code"], 
43448         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43449         ['div'],['span']
43450     ],
43451     
43452     cleanStyles : [
43453         "font-size"
43454     ],
43455      /**
43456      * @cfg {String} defaultFont default font to use.
43457      */
43458     defaultFont: 'tahoma',
43459    
43460     fontSelect : false,
43461     
43462     
43463     formatCombo : false,
43464     
43465     init : function(editor)
43466     {
43467         this.editor = editor;
43468         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43469         var editorcore = this.editorcore;
43470         
43471         var _t = this;
43472         
43473         var fid = editorcore.frameId;
43474         var etb = this;
43475         function btn(id, toggle, handler){
43476             var xid = fid + '-'+ id ;
43477             return {
43478                 id : xid,
43479                 cmd : id,
43480                 cls : 'x-btn-icon x-edit-'+id,
43481                 enableToggle:toggle !== false,
43482                 scope: _t, // was editor...
43483                 handler:handler||_t.relayBtnCmd,
43484                 clickEvent:'mousedown',
43485                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43486                 tabIndex:-1
43487             };
43488         }
43489         
43490         
43491         
43492         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43493         this.tb = tb;
43494          // stop form submits
43495         tb.el.on('click', function(e){
43496             e.preventDefault(); // what does this do?
43497         });
43498
43499         if(!this.disable.font) { // && !Roo.isSafari){
43500             /* why no safari for fonts 
43501             editor.fontSelect = tb.el.createChild({
43502                 tag:'select',
43503                 tabIndex: -1,
43504                 cls:'x-font-select',
43505                 html: this.createFontOptions()
43506             });
43507             
43508             editor.fontSelect.on('change', function(){
43509                 var font = editor.fontSelect.dom.value;
43510                 editor.relayCmd('fontname', font);
43511                 editor.deferFocus();
43512             }, editor);
43513             
43514             tb.add(
43515                 editor.fontSelect.dom,
43516                 '-'
43517             );
43518             */
43519             
43520         };
43521         if(!this.disable.formats){
43522             this.formatCombo = new Roo.form.ComboBox({
43523                 store: new Roo.data.SimpleStore({
43524                     id : 'tag',
43525                     fields: ['tag'],
43526                     data : this.formats // from states.js
43527                 }),
43528                 blockFocus : true,
43529                 name : '',
43530                 //autoCreate : {tag: "div",  size: "20"},
43531                 displayField:'tag',
43532                 typeAhead: false,
43533                 mode: 'local',
43534                 editable : false,
43535                 triggerAction: 'all',
43536                 emptyText:'Add tag',
43537                 selectOnFocus:true,
43538                 width:135,
43539                 listeners : {
43540                     'select': function(c, r, i) {
43541                         editorcore.insertTag(r.get('tag'));
43542                         editor.focus();
43543                     }
43544                 }
43545
43546             });
43547             tb.addField(this.formatCombo);
43548             
43549         }
43550         
43551         if(!this.disable.format){
43552             tb.add(
43553                 btn('bold'),
43554                 btn('italic'),
43555                 btn('underline')
43556             );
43557         };
43558         if(!this.disable.fontSize){
43559             tb.add(
43560                 '-',
43561                 
43562                 
43563                 btn('increasefontsize', false, editorcore.adjustFont),
43564                 btn('decreasefontsize', false, editorcore.adjustFont)
43565             );
43566         };
43567         
43568         
43569         if(!this.disable.colors){
43570             tb.add(
43571                 '-', {
43572                     id:editorcore.frameId +'-forecolor',
43573                     cls:'x-btn-icon x-edit-forecolor',
43574                     clickEvent:'mousedown',
43575                     tooltip: this.buttonTips['forecolor'] || undefined,
43576                     tabIndex:-1,
43577                     menu : new Roo.menu.ColorMenu({
43578                         allowReselect: true,
43579                         focus: Roo.emptyFn,
43580                         value:'000000',
43581                         plain:true,
43582                         selectHandler: function(cp, color){
43583                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43584                             editor.deferFocus();
43585                         },
43586                         scope: editorcore,
43587                         clickEvent:'mousedown'
43588                     })
43589                 }, {
43590                     id:editorcore.frameId +'backcolor',
43591                     cls:'x-btn-icon x-edit-backcolor',
43592                     clickEvent:'mousedown',
43593                     tooltip: this.buttonTips['backcolor'] || undefined,
43594                     tabIndex:-1,
43595                     menu : new Roo.menu.ColorMenu({
43596                         focus: Roo.emptyFn,
43597                         value:'FFFFFF',
43598                         plain:true,
43599                         allowReselect: true,
43600                         selectHandler: function(cp, color){
43601                             if(Roo.isGecko){
43602                                 editorcore.execCmd('useCSS', false);
43603                                 editorcore.execCmd('hilitecolor', color);
43604                                 editorcore.execCmd('useCSS', true);
43605                                 editor.deferFocus();
43606                             }else{
43607                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43608                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43609                                 editor.deferFocus();
43610                             }
43611                         },
43612                         scope:editorcore,
43613                         clickEvent:'mousedown'
43614                     })
43615                 }
43616             );
43617         };
43618         // now add all the items...
43619         
43620
43621         if(!this.disable.alignments){
43622             tb.add(
43623                 '-',
43624                 btn('justifyleft'),
43625                 btn('justifycenter'),
43626                 btn('justifyright')
43627             );
43628         };
43629
43630         //if(!Roo.isSafari){
43631             if(!this.disable.links){
43632                 tb.add(
43633                     '-',
43634                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43635                 );
43636             };
43637
43638             if(!this.disable.lists){
43639                 tb.add(
43640                     '-',
43641                     btn('insertorderedlist'),
43642                     btn('insertunorderedlist')
43643                 );
43644             }
43645             if(!this.disable.sourceEdit){
43646                 tb.add(
43647                     '-',
43648                     btn('sourceedit', true, function(btn){
43649                         this.toggleSourceEdit(btn.pressed);
43650                     })
43651                 );
43652             }
43653         //}
43654         
43655         var smenu = { };
43656         // special menu.. - needs to be tidied up..
43657         if (!this.disable.special) {
43658             smenu = {
43659                 text: "&#169;",
43660                 cls: 'x-edit-none',
43661                 
43662                 menu : {
43663                     items : []
43664                 }
43665             };
43666             for (var i =0; i < this.specialChars.length; i++) {
43667                 smenu.menu.items.push({
43668                     
43669                     html: this.specialChars[i],
43670                     handler: function(a,b) {
43671                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43672                         //editor.insertAtCursor(a.html);
43673                         
43674                     },
43675                     tabIndex:-1
43676                 });
43677             }
43678             
43679             
43680             tb.add(smenu);
43681             
43682             
43683         }
43684         
43685         var cmenu = { };
43686         if (!this.disable.cleanStyles) {
43687             cmenu = {
43688                 cls: 'x-btn-icon x-btn-clear',
43689                 
43690                 menu : {
43691                     items : []
43692                 }
43693             };
43694             for (var i =0; i < this.cleanStyles.length; i++) {
43695                 cmenu.menu.items.push({
43696                     actiontype : this.cleanStyles[i],
43697                     html: 'Remove ' + this.cleanStyles[i],
43698                     handler: function(a,b) {
43699 //                        Roo.log(a);
43700 //                        Roo.log(b);
43701                         var c = Roo.get(editorcore.doc.body);
43702                         c.select('[style]').each(function(s) {
43703                             s.dom.style.removeProperty(a.actiontype);
43704                         });
43705                         editorcore.syncValue();
43706                     },
43707                     tabIndex:-1
43708                 });
43709             }
43710             cmenu.menu.items.push({
43711                 actiontype : 'word',
43712                 html: 'Remove MS Word Formating',
43713                 handler: function(a,b) {
43714                     editorcore.cleanWord();
43715                     editorcore.syncValue();
43716                 },
43717                 tabIndex:-1
43718             });
43719             
43720             cmenu.menu.items.push({
43721                 actiontype : 'all',
43722                 html: 'Remove All Styles',
43723                 handler: function(a,b) {
43724                     
43725                     var c = Roo.get(editorcore.doc.body);
43726                     c.select('[style]').each(function(s) {
43727                         s.dom.removeAttribute('style');
43728                     });
43729                     editorcore.syncValue();
43730                 },
43731                 tabIndex:-1
43732             });
43733              cmenu.menu.items.push({
43734                 actiontype : 'word',
43735                 html: 'Tidy HTML Source',
43736                 handler: function(a,b) {
43737                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43738                     editorcore.syncValue();
43739                 },
43740                 tabIndex:-1
43741             });
43742             
43743             
43744             tb.add(cmenu);
43745         }
43746          
43747         if (!this.disable.specialElements) {
43748             var semenu = {
43749                 text: "Other;",
43750                 cls: 'x-edit-none',
43751                 menu : {
43752                     items : []
43753                 }
43754             };
43755             for (var i =0; i < this.specialElements.length; i++) {
43756                 semenu.menu.items.push(
43757                     Roo.apply({ 
43758                         handler: function(a,b) {
43759                             editor.insertAtCursor(this.ihtml);
43760                         }
43761                     }, this.specialElements[i])
43762                 );
43763                     
43764             }
43765             
43766             tb.add(semenu);
43767             
43768             
43769         }
43770          
43771         
43772         if (this.btns) {
43773             for(var i =0; i< this.btns.length;i++) {
43774                 var b = Roo.factory(this.btns[i],Roo.form);
43775                 b.cls =  'x-edit-none';
43776                 
43777                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43778                     b.cls += ' x-init-enable';
43779                 }
43780                 
43781                 b.scope = editorcore;
43782                 tb.add(b);
43783             }
43784         
43785         }
43786         
43787         
43788         
43789         // disable everything...
43790         
43791         this.tb.items.each(function(item){
43792             
43793            if(
43794                 item.id != editorcore.frameId+ '-sourceedit' && 
43795                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43796             ){
43797                 
43798                 item.disable();
43799             }
43800         });
43801         this.rendered = true;
43802         
43803         // the all the btns;
43804         editor.on('editorevent', this.updateToolbar, this);
43805         // other toolbars need to implement this..
43806         //editor.on('editmodechange', this.updateToolbar, this);
43807     },
43808     
43809     
43810     relayBtnCmd : function(btn) {
43811         this.editorcore.relayCmd(btn.cmd);
43812     },
43813     // private used internally
43814     createLink : function(){
43815         Roo.log("create link?");
43816         var url = prompt(this.createLinkText, this.defaultLinkValue);
43817         if(url && url != 'http:/'+'/'){
43818             this.editorcore.relayCmd('createlink', url);
43819         }
43820     },
43821
43822     
43823     /**
43824      * Protected method that will not generally be called directly. It triggers
43825      * a toolbar update by reading the markup state of the current selection in the editor.
43826      */
43827     updateToolbar: function(){
43828
43829         if(!this.editorcore.activated){
43830             this.editor.onFirstFocus();
43831             return;
43832         }
43833
43834         var btns = this.tb.items.map, 
43835             doc = this.editorcore.doc,
43836             frameId = this.editorcore.frameId;
43837
43838         if(!this.disable.font && !Roo.isSafari){
43839             /*
43840             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43841             if(name != this.fontSelect.dom.value){
43842                 this.fontSelect.dom.value = name;
43843             }
43844             */
43845         }
43846         if(!this.disable.format){
43847             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43848             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43849             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43850         }
43851         if(!this.disable.alignments){
43852             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43853             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43854             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43855         }
43856         if(!Roo.isSafari && !this.disable.lists){
43857             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43858             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43859         }
43860         
43861         var ans = this.editorcore.getAllAncestors();
43862         if (this.formatCombo) {
43863             
43864             
43865             var store = this.formatCombo.store;
43866             this.formatCombo.setValue("");
43867             for (var i =0; i < ans.length;i++) {
43868                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43869                     // select it..
43870                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43871                     break;
43872                 }
43873             }
43874         }
43875         
43876         
43877         
43878         // hides menus... - so this cant be on a menu...
43879         Roo.menu.MenuMgr.hideAll();
43880
43881         //this.editorsyncValue();
43882     },
43883    
43884     
43885     createFontOptions : function(){
43886         var buf = [], fs = this.fontFamilies, ff, lc;
43887         
43888         
43889         
43890         for(var i = 0, len = fs.length; i< len; i++){
43891             ff = fs[i];
43892             lc = ff.toLowerCase();
43893             buf.push(
43894                 '<option value="',lc,'" style="font-family:',ff,';"',
43895                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43896                     ff,
43897                 '</option>'
43898             );
43899         }
43900         return buf.join('');
43901     },
43902     
43903     toggleSourceEdit : function(sourceEditMode){
43904         
43905         Roo.log("toolbar toogle");
43906         if(sourceEditMode === undefined){
43907             sourceEditMode = !this.sourceEditMode;
43908         }
43909         this.sourceEditMode = sourceEditMode === true;
43910         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43911         // just toggle the button?
43912         if(btn.pressed !== this.sourceEditMode){
43913             btn.toggle(this.sourceEditMode);
43914             return;
43915         }
43916         
43917         if(sourceEditMode){
43918             Roo.log("disabling buttons");
43919             this.tb.items.each(function(item){
43920                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43921                     item.disable();
43922                 }
43923             });
43924           
43925         }else{
43926             Roo.log("enabling buttons");
43927             if(this.editorcore.initialized){
43928                 this.tb.items.each(function(item){
43929                     item.enable();
43930                 });
43931             }
43932             
43933         }
43934         Roo.log("calling toggole on editor");
43935         // tell the editor that it's been pressed..
43936         this.editor.toggleSourceEdit(sourceEditMode);
43937        
43938     },
43939      /**
43940      * Object collection of toolbar tooltips for the buttons in the editor. The key
43941      * is the command id associated with that button and the value is a valid QuickTips object.
43942      * For example:
43943 <pre><code>
43944 {
43945     bold : {
43946         title: 'Bold (Ctrl+B)',
43947         text: 'Make the selected text bold.',
43948         cls: 'x-html-editor-tip'
43949     },
43950     italic : {
43951         title: 'Italic (Ctrl+I)',
43952         text: 'Make the selected text italic.',
43953         cls: 'x-html-editor-tip'
43954     },
43955     ...
43956 </code></pre>
43957     * @type Object
43958      */
43959     buttonTips : {
43960         bold : {
43961             title: 'Bold (Ctrl+B)',
43962             text: 'Make the selected text bold.',
43963             cls: 'x-html-editor-tip'
43964         },
43965         italic : {
43966             title: 'Italic (Ctrl+I)',
43967             text: 'Make the selected text italic.',
43968             cls: 'x-html-editor-tip'
43969         },
43970         underline : {
43971             title: 'Underline (Ctrl+U)',
43972             text: 'Underline the selected text.',
43973             cls: 'x-html-editor-tip'
43974         },
43975         increasefontsize : {
43976             title: 'Grow Text',
43977             text: 'Increase the font size.',
43978             cls: 'x-html-editor-tip'
43979         },
43980         decreasefontsize : {
43981             title: 'Shrink Text',
43982             text: 'Decrease the font size.',
43983             cls: 'x-html-editor-tip'
43984         },
43985         backcolor : {
43986             title: 'Text Highlight Color',
43987             text: 'Change the background color of the selected text.',
43988             cls: 'x-html-editor-tip'
43989         },
43990         forecolor : {
43991             title: 'Font Color',
43992             text: 'Change the color of the selected text.',
43993             cls: 'x-html-editor-tip'
43994         },
43995         justifyleft : {
43996             title: 'Align Text Left',
43997             text: 'Align text to the left.',
43998             cls: 'x-html-editor-tip'
43999         },
44000         justifycenter : {
44001             title: 'Center Text',
44002             text: 'Center text in the editor.',
44003             cls: 'x-html-editor-tip'
44004         },
44005         justifyright : {
44006             title: 'Align Text Right',
44007             text: 'Align text to the right.',
44008             cls: 'x-html-editor-tip'
44009         },
44010         insertunorderedlist : {
44011             title: 'Bullet List',
44012             text: 'Start a bulleted list.',
44013             cls: 'x-html-editor-tip'
44014         },
44015         insertorderedlist : {
44016             title: 'Numbered List',
44017             text: 'Start a numbered list.',
44018             cls: 'x-html-editor-tip'
44019         },
44020         createlink : {
44021             title: 'Hyperlink',
44022             text: 'Make the selected text a hyperlink.',
44023             cls: 'x-html-editor-tip'
44024         },
44025         sourceedit : {
44026             title: 'Source Edit',
44027             text: 'Switch to source editing mode.',
44028             cls: 'x-html-editor-tip'
44029         }
44030     },
44031     // private
44032     onDestroy : function(){
44033         if(this.rendered){
44034             
44035             this.tb.items.each(function(item){
44036                 if(item.menu){
44037                     item.menu.removeAll();
44038                     if(item.menu.el){
44039                         item.menu.el.destroy();
44040                     }
44041                 }
44042                 item.destroy();
44043             });
44044              
44045         }
44046     },
44047     onFirstFocus: function() {
44048         this.tb.items.each(function(item){
44049            item.enable();
44050         });
44051     }
44052 });
44053
44054
44055
44056
44057 // <script type="text/javascript">
44058 /*
44059  * Based on
44060  * Ext JS Library 1.1.1
44061  * Copyright(c) 2006-2007, Ext JS, LLC.
44062  *  
44063  
44064  */
44065
44066  
44067 /**
44068  * @class Roo.form.HtmlEditor.ToolbarContext
44069  * Context Toolbar
44070  * 
44071  * Usage:
44072  *
44073  new Roo.form.HtmlEditor({
44074     ....
44075     toolbars : [
44076         { xtype: 'ToolbarStandard', styles : {} }
44077         { xtype: 'ToolbarContext', disable : {} }
44078     ]
44079 })
44080
44081      
44082  * 
44083  * @config : {Object} disable List of elements to disable.. (not done yet.)
44084  * @config : {Object} styles  Map of styles available.
44085  * 
44086  */
44087
44088 Roo.form.HtmlEditor.ToolbarContext = function(config)
44089 {
44090     
44091     Roo.apply(this, config);
44092     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44093     // dont call parent... till later.
44094     this.styles = this.styles || {};
44095 }
44096
44097  
44098
44099 Roo.form.HtmlEditor.ToolbarContext.types = {
44100     'IMG' : {
44101         width : {
44102             title: "Width",
44103             width: 40
44104         },
44105         height:  {
44106             title: "Height",
44107             width: 40
44108         },
44109         align: {
44110             title: "Align",
44111             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44112             width : 80
44113             
44114         },
44115         border: {
44116             title: "Border",
44117             width: 40
44118         },
44119         alt: {
44120             title: "Alt",
44121             width: 120
44122         },
44123         src : {
44124             title: "Src",
44125             width: 220
44126         }
44127         
44128     },
44129     'A' : {
44130         name : {
44131             title: "Name",
44132             width: 50
44133         },
44134         target:  {
44135             title: "Target",
44136             width: 120
44137         },
44138         href:  {
44139             title: "Href",
44140             width: 220
44141         } // border?
44142         
44143     },
44144     'TABLE' : {
44145         rows : {
44146             title: "Rows",
44147             width: 20
44148         },
44149         cols : {
44150             title: "Cols",
44151             width: 20
44152         },
44153         width : {
44154             title: "Width",
44155             width: 40
44156         },
44157         height : {
44158             title: "Height",
44159             width: 40
44160         },
44161         border : {
44162             title: "Border",
44163             width: 20
44164         }
44165     },
44166     'TD' : {
44167         width : {
44168             title: "Width",
44169             width: 40
44170         },
44171         height : {
44172             title: "Height",
44173             width: 40
44174         },   
44175         align: {
44176             title: "Align",
44177             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44178             width: 80
44179         },
44180         valign: {
44181             title: "Valign",
44182             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44183             width: 80
44184         },
44185         colspan: {
44186             title: "Colspan",
44187             width: 20
44188             
44189         },
44190          'font-family'  : {
44191             title : "Font",
44192             style : 'fontFamily',
44193             displayField: 'display',
44194             optname : 'font-family',
44195             width: 140
44196         }
44197     },
44198     'INPUT' : {
44199         name : {
44200             title: "name",
44201             width: 120
44202         },
44203         value : {
44204             title: "Value",
44205             width: 120
44206         },
44207         width : {
44208             title: "Width",
44209             width: 40
44210         }
44211     },
44212     'LABEL' : {
44213         'for' : {
44214             title: "For",
44215             width: 120
44216         }
44217     },
44218     'TEXTAREA' : {
44219           name : {
44220             title: "name",
44221             width: 120
44222         },
44223         rows : {
44224             title: "Rows",
44225             width: 20
44226         },
44227         cols : {
44228             title: "Cols",
44229             width: 20
44230         }
44231     },
44232     'SELECT' : {
44233         name : {
44234             title: "name",
44235             width: 120
44236         },
44237         selectoptions : {
44238             title: "Options",
44239             width: 200
44240         }
44241     },
44242     
44243     // should we really allow this??
44244     // should this just be 
44245     'BODY' : {
44246         title : {
44247             title: "Title",
44248             width: 200,
44249             disabled : true
44250         }
44251     },
44252     'SPAN' : {
44253         'font-family'  : {
44254             title : "Font",
44255             style : 'fontFamily',
44256             displayField: 'display',
44257             optname : 'font-family',
44258             width: 140
44259         }
44260     },
44261     'DIV' : {
44262         'font-family'  : {
44263             title : "Font",
44264             style : 'fontFamily',
44265             displayField: 'display',
44266             optname : 'font-family',
44267             width: 140
44268         }
44269     },
44270      'P' : {
44271         'font-family'  : {
44272             title : "Font",
44273             style : 'fontFamily',
44274             displayField: 'display',
44275             optname : 'font-family',
44276             width: 140
44277         }
44278     },
44279     
44280     '*' : {
44281         // empty..
44282     }
44283
44284 };
44285
44286 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44287 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44288
44289 Roo.form.HtmlEditor.ToolbarContext.options = {
44290         'font-family'  : [ 
44291                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44292                 [ 'Courier New', 'Courier New'],
44293                 [ 'Tahoma', 'Tahoma'],
44294                 [ 'Times New Roman,serif', 'Times'],
44295                 [ 'Verdana','Verdana' ]
44296         ]
44297 };
44298
44299 // fixme - these need to be configurable..
44300  
44301
44302 Roo.form.HtmlEditor.ToolbarContext.types
44303
44304
44305 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44306     
44307     tb: false,
44308     
44309     rendered: false,
44310     
44311     editor : false,
44312     editorcore : false,
44313     /**
44314      * @cfg {Object} disable  List of toolbar elements to disable
44315          
44316      */
44317     disable : false,
44318     /**
44319      * @cfg {Object} styles List of styles 
44320      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44321      *
44322      * These must be defined in the page, so they get rendered correctly..
44323      * .headline { }
44324      * TD.underline { }
44325      * 
44326      */
44327     styles : false,
44328     
44329     options: false,
44330     
44331     toolbars : false,
44332     
44333     init : function(editor)
44334     {
44335         this.editor = editor;
44336         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44337         var editorcore = this.editorcore;
44338         
44339         var fid = editorcore.frameId;
44340         var etb = this;
44341         function btn(id, toggle, handler){
44342             var xid = fid + '-'+ id ;
44343             return {
44344                 id : xid,
44345                 cmd : id,
44346                 cls : 'x-btn-icon x-edit-'+id,
44347                 enableToggle:toggle !== false,
44348                 scope: editorcore, // was editor...
44349                 handler:handler||editorcore.relayBtnCmd,
44350                 clickEvent:'mousedown',
44351                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44352                 tabIndex:-1
44353             };
44354         }
44355         // create a new element.
44356         var wdiv = editor.wrap.createChild({
44357                 tag: 'div'
44358             }, editor.wrap.dom.firstChild.nextSibling, true);
44359         
44360         // can we do this more than once??
44361         
44362          // stop form submits
44363       
44364  
44365         // disable everything...
44366         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44367         this.toolbars = {};
44368            
44369         for (var i in  ty) {
44370           
44371             this.toolbars[i] = this.buildToolbar(ty[i],i);
44372         }
44373         this.tb = this.toolbars.BODY;
44374         this.tb.el.show();
44375         this.buildFooter();
44376         this.footer.show();
44377         editor.on('hide', function( ) { this.footer.hide() }, this);
44378         editor.on('show', function( ) { this.footer.show() }, this);
44379         
44380          
44381         this.rendered = true;
44382         
44383         // the all the btns;
44384         editor.on('editorevent', this.updateToolbar, this);
44385         // other toolbars need to implement this..
44386         //editor.on('editmodechange', this.updateToolbar, this);
44387     },
44388     
44389     
44390     
44391     /**
44392      * Protected method that will not generally be called directly. It triggers
44393      * a toolbar update by reading the markup state of the current selection in the editor.
44394      *
44395      * Note you can force an update by calling on('editorevent', scope, false)
44396      */
44397     updateToolbar: function(editor,ev,sel){
44398
44399         //Roo.log(ev);
44400         // capture mouse up - this is handy for selecting images..
44401         // perhaps should go somewhere else...
44402         if(!this.editorcore.activated){
44403              this.editor.onFirstFocus();
44404             return;
44405         }
44406         
44407         
44408         
44409         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44410         // selectNode - might want to handle IE?
44411         if (ev &&
44412             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44413             ev.target && ev.target.tagName == 'IMG') {
44414             // they have click on an image...
44415             // let's see if we can change the selection...
44416             sel = ev.target;
44417          
44418               var nodeRange = sel.ownerDocument.createRange();
44419             try {
44420                 nodeRange.selectNode(sel);
44421             } catch (e) {
44422                 nodeRange.selectNodeContents(sel);
44423             }
44424             //nodeRange.collapse(true);
44425             var s = this.editorcore.win.getSelection();
44426             s.removeAllRanges();
44427             s.addRange(nodeRange);
44428         }  
44429         
44430       
44431         var updateFooter = sel ? false : true;
44432         
44433         
44434         var ans = this.editorcore.getAllAncestors();
44435         
44436         // pick
44437         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44438         
44439         if (!sel) { 
44440             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44441             sel = sel ? sel : this.editorcore.doc.body;
44442             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44443             
44444         }
44445         // pick a menu that exists..
44446         var tn = sel.tagName.toUpperCase();
44447         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44448         
44449         tn = sel.tagName.toUpperCase();
44450         
44451         var lastSel = this.tb.selectedNode
44452         
44453         this.tb.selectedNode = sel;
44454         
44455         // if current menu does not match..
44456         
44457         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44458                 
44459             this.tb.el.hide();
44460             ///console.log("show: " + tn);
44461             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44462             this.tb.el.show();
44463             // update name
44464             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44465             
44466             
44467             // update attributes
44468             if (this.tb.fields) {
44469                 this.tb.fields.each(function(e) {
44470                     if (e.stylename) {
44471                         e.setValue(sel.style[e.stylename]);
44472                         return;
44473                     } 
44474                    e.setValue(sel.getAttribute(e.attrname));
44475                 });
44476             }
44477             
44478             var hasStyles = false;
44479             for(var i in this.styles) {
44480                 hasStyles = true;
44481                 break;
44482             }
44483             
44484             // update styles
44485             if (hasStyles) { 
44486                 var st = this.tb.fields.item(0);
44487                 
44488                 st.store.removeAll();
44489                
44490                 
44491                 var cn = sel.className.split(/\s+/);
44492                 
44493                 var avs = [];
44494                 if (this.styles['*']) {
44495                     
44496                     Roo.each(this.styles['*'], function(v) {
44497                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44498                     });
44499                 }
44500                 if (this.styles[tn]) { 
44501                     Roo.each(this.styles[tn], function(v) {
44502                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44503                     });
44504                 }
44505                 
44506                 st.store.loadData(avs);
44507                 st.collapse();
44508                 st.setValue(cn);
44509             }
44510             // flag our selected Node.
44511             this.tb.selectedNode = sel;
44512            
44513            
44514             Roo.menu.MenuMgr.hideAll();
44515
44516         }
44517         
44518         if (!updateFooter) {
44519             //this.footDisp.dom.innerHTML = ''; 
44520             return;
44521         }
44522         // update the footer
44523         //
44524         var html = '';
44525         
44526         this.footerEls = ans.reverse();
44527         Roo.each(this.footerEls, function(a,i) {
44528             if (!a) { return; }
44529             html += html.length ? ' &gt; '  :  '';
44530             
44531             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44532             
44533         });
44534        
44535         // 
44536         var sz = this.footDisp.up('td').getSize();
44537         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44538         this.footDisp.dom.style.marginLeft = '5px';
44539         
44540         this.footDisp.dom.style.overflow = 'hidden';
44541         
44542         this.footDisp.dom.innerHTML = html;
44543             
44544         //this.editorsyncValue();
44545     },
44546      
44547     
44548    
44549        
44550     // private
44551     onDestroy : function(){
44552         if(this.rendered){
44553             
44554             this.tb.items.each(function(item){
44555                 if(item.menu){
44556                     item.menu.removeAll();
44557                     if(item.menu.el){
44558                         item.menu.el.destroy();
44559                     }
44560                 }
44561                 item.destroy();
44562             });
44563              
44564         }
44565     },
44566     onFirstFocus: function() {
44567         // need to do this for all the toolbars..
44568         this.tb.items.each(function(item){
44569            item.enable();
44570         });
44571     },
44572     buildToolbar: function(tlist, nm)
44573     {
44574         var editor = this.editor;
44575         var editorcore = this.editorcore;
44576          // create a new element.
44577         var wdiv = editor.wrap.createChild({
44578                 tag: 'div'
44579             }, editor.wrap.dom.firstChild.nextSibling, true);
44580         
44581        
44582         var tb = new Roo.Toolbar(wdiv);
44583         // add the name..
44584         
44585         tb.add(nm+ ":&nbsp;");
44586         
44587         var styles = [];
44588         for(var i in this.styles) {
44589             styles.push(i);
44590         }
44591         
44592         // styles...
44593         if (styles && styles.length) {
44594             
44595             // this needs a multi-select checkbox...
44596             tb.addField( new Roo.form.ComboBox({
44597                 store: new Roo.data.SimpleStore({
44598                     id : 'val',
44599                     fields: ['val', 'selected'],
44600                     data : [] 
44601                 }),
44602                 name : '-roo-edit-className',
44603                 attrname : 'className',
44604                 displayField: 'val',
44605                 typeAhead: false,
44606                 mode: 'local',
44607                 editable : false,
44608                 triggerAction: 'all',
44609                 emptyText:'Select Style',
44610                 selectOnFocus:true,
44611                 width: 130,
44612                 listeners : {
44613                     'select': function(c, r, i) {
44614                         // initial support only for on class per el..
44615                         tb.selectedNode.className =  r ? r.get('val') : '';
44616                         editorcore.syncValue();
44617                     }
44618                 }
44619     
44620             }));
44621         }
44622         
44623         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44624         var tbops = tbc.options;
44625         
44626         for (var i in tlist) {
44627             
44628             var item = tlist[i];
44629             tb.add(item.title + ":&nbsp;");
44630             
44631             
44632             //optname == used so you can configure the options available..
44633             var opts = item.opts ? item.opts : false;
44634             if (item.optname) {
44635                 opts = tbops[item.optname];
44636            
44637             }
44638             
44639             if (opts) {
44640                 // opts == pulldown..
44641                 tb.addField( new Roo.form.ComboBox({
44642                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44643                         id : 'val',
44644                         fields: ['val', 'display'],
44645                         data : opts  
44646                     }),
44647                     name : '-roo-edit-' + i,
44648                     attrname : i,
44649                     stylename : item.style ? item.style : false,
44650                     displayField: item.displayField ? item.displayField : 'val',
44651                     valueField :  'val',
44652                     typeAhead: false,
44653                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44654                     editable : false,
44655                     triggerAction: 'all',
44656                     emptyText:'Select',
44657                     selectOnFocus:true,
44658                     width: item.width ? item.width  : 130,
44659                     listeners : {
44660                         'select': function(c, r, i) {
44661                             if (c.stylename) {
44662                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44663                                 return;
44664                             }
44665                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44666                         }
44667                     }
44668
44669                 }));
44670                 continue;
44671                     
44672                  
44673                 
44674                 tb.addField( new Roo.form.TextField({
44675                     name: i,
44676                     width: 100,
44677                     //allowBlank:false,
44678                     value: ''
44679                 }));
44680                 continue;
44681             }
44682             tb.addField( new Roo.form.TextField({
44683                 name: '-roo-edit-' + i,
44684                 attrname : i,
44685                 
44686                 width: item.width,
44687                 //allowBlank:true,
44688                 value: '',
44689                 listeners: {
44690                     'change' : function(f, nv, ov) {
44691                         tb.selectedNode.setAttribute(f.attrname, nv);
44692                     }
44693                 }
44694             }));
44695              
44696         }
44697         
44698         var _this = this;
44699         
44700         if(nm == 'BODY'){
44701             tb.addSeparator();
44702         
44703             tb.addButton( {
44704                 text: 'Stylesheets',
44705
44706                 listeners : {
44707                     click : function ()
44708                     {
44709                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44710                     }
44711                 }
44712             });
44713         }
44714         
44715         tb.addFill();
44716         tb.addButton( {
44717             text: 'Remove Tag',
44718     
44719             listeners : {
44720                 click : function ()
44721                 {
44722                     // remove
44723                     // undo does not work.
44724                      
44725                     var sn = tb.selectedNode;
44726                     
44727                     var pn = sn.parentNode;
44728                     
44729                     var stn =  sn.childNodes[0];
44730                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44731                     while (sn.childNodes.length) {
44732                         var node = sn.childNodes[0];
44733                         sn.removeChild(node);
44734                         //Roo.log(node);
44735                         pn.insertBefore(node, sn);
44736                         
44737                     }
44738                     pn.removeChild(sn);
44739                     var range = editorcore.createRange();
44740         
44741                     range.setStart(stn,0);
44742                     range.setEnd(en,0); //????
44743                     //range.selectNode(sel);
44744                     
44745                     
44746                     var selection = editorcore.getSelection();
44747                     selection.removeAllRanges();
44748                     selection.addRange(range);
44749                     
44750                     
44751                     
44752                     //_this.updateToolbar(null, null, pn);
44753                     _this.updateToolbar(null, null, null);
44754                     _this.footDisp.dom.innerHTML = ''; 
44755                 }
44756             }
44757             
44758                     
44759                 
44760             
44761         });
44762         
44763         
44764         tb.el.on('click', function(e){
44765             e.preventDefault(); // what does this do?
44766         });
44767         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44768         tb.el.hide();
44769         tb.name = nm;
44770         // dont need to disable them... as they will get hidden
44771         return tb;
44772          
44773         
44774     },
44775     buildFooter : function()
44776     {
44777         
44778         var fel = this.editor.wrap.createChild();
44779         this.footer = new Roo.Toolbar(fel);
44780         // toolbar has scrolly on left / right?
44781         var footDisp= new Roo.Toolbar.Fill();
44782         var _t = this;
44783         this.footer.add(
44784             {
44785                 text : '&lt;',
44786                 xtype: 'Button',
44787                 handler : function() {
44788                     _t.footDisp.scrollTo('left',0,true)
44789                 }
44790             }
44791         );
44792         this.footer.add( footDisp );
44793         this.footer.add( 
44794             {
44795                 text : '&gt;',
44796                 xtype: 'Button',
44797                 handler : function() {
44798                     // no animation..
44799                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44800                 }
44801             }
44802         );
44803         var fel = Roo.get(footDisp.el);
44804         fel.addClass('x-editor-context');
44805         this.footDispWrap = fel; 
44806         this.footDispWrap.overflow  = 'hidden';
44807         
44808         this.footDisp = fel.createChild();
44809         this.footDispWrap.on('click', this.onContextClick, this)
44810         
44811         
44812     },
44813     onContextClick : function (ev,dom)
44814     {
44815         ev.preventDefault();
44816         var  cn = dom.className;
44817         //Roo.log(cn);
44818         if (!cn.match(/x-ed-loc-/)) {
44819             return;
44820         }
44821         var n = cn.split('-').pop();
44822         var ans = this.footerEls;
44823         var sel = ans[n];
44824         
44825          // pick
44826         var range = this.editorcore.createRange();
44827         
44828         range.selectNodeContents(sel);
44829         //range.selectNode(sel);
44830         
44831         
44832         var selection = this.editorcore.getSelection();
44833         selection.removeAllRanges();
44834         selection.addRange(range);
44835         
44836         
44837         
44838         this.updateToolbar(null, null, sel);
44839         
44840         
44841     }
44842     
44843     
44844     
44845     
44846     
44847 });
44848
44849
44850
44851
44852
44853 /*
44854  * Based on:
44855  * Ext JS Library 1.1.1
44856  * Copyright(c) 2006-2007, Ext JS, LLC.
44857  *
44858  * Originally Released Under LGPL - original licence link has changed is not relivant.
44859  *
44860  * Fork - LGPL
44861  * <script type="text/javascript">
44862  */
44863  
44864 /**
44865  * @class Roo.form.BasicForm
44866  * @extends Roo.util.Observable
44867  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44868  * @constructor
44869  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44870  * @param {Object} config Configuration options
44871  */
44872 Roo.form.BasicForm = function(el, config){
44873     this.allItems = [];
44874     this.childForms = [];
44875     Roo.apply(this, config);
44876     /*
44877      * The Roo.form.Field items in this form.
44878      * @type MixedCollection
44879      */
44880      
44881      
44882     this.items = new Roo.util.MixedCollection(false, function(o){
44883         return o.id || (o.id = Roo.id());
44884     });
44885     this.addEvents({
44886         /**
44887          * @event beforeaction
44888          * Fires before any action is performed. Return false to cancel the action.
44889          * @param {Form} this
44890          * @param {Action} action The action to be performed
44891          */
44892         beforeaction: true,
44893         /**
44894          * @event actionfailed
44895          * Fires when an action fails.
44896          * @param {Form} this
44897          * @param {Action} action The action that failed
44898          */
44899         actionfailed : true,
44900         /**
44901          * @event actioncomplete
44902          * Fires when an action is completed.
44903          * @param {Form} this
44904          * @param {Action} action The action that completed
44905          */
44906         actioncomplete : true
44907     });
44908     if(el){
44909         this.initEl(el);
44910     }
44911     Roo.form.BasicForm.superclass.constructor.call(this);
44912 };
44913
44914 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44915     /**
44916      * @cfg {String} method
44917      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44918      */
44919     /**
44920      * @cfg {DataReader} reader
44921      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44922      * This is optional as there is built-in support for processing JSON.
44923      */
44924     /**
44925      * @cfg {DataReader} errorReader
44926      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44927      * This is completely optional as there is built-in support for processing JSON.
44928      */
44929     /**
44930      * @cfg {String} url
44931      * The URL to use for form actions if one isn't supplied in the action options.
44932      */
44933     /**
44934      * @cfg {Boolean} fileUpload
44935      * Set to true if this form is a file upload.
44936      */
44937      
44938     /**
44939      * @cfg {Object} baseParams
44940      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44941      */
44942      /**
44943      
44944     /**
44945      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44946      */
44947     timeout: 30,
44948
44949     // private
44950     activeAction : null,
44951
44952     /**
44953      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44954      * or setValues() data instead of when the form was first created.
44955      */
44956     trackResetOnLoad : false,
44957     
44958     
44959     /**
44960      * childForms - used for multi-tab forms
44961      * @type {Array}
44962      */
44963     childForms : false,
44964     
44965     /**
44966      * allItems - full list of fields.
44967      * @type {Array}
44968      */
44969     allItems : false,
44970     
44971     /**
44972      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44973      * element by passing it or its id or mask the form itself by passing in true.
44974      * @type Mixed
44975      */
44976     waitMsgTarget : false,
44977
44978     // private
44979     initEl : function(el){
44980         this.el = Roo.get(el);
44981         this.id = this.el.id || Roo.id();
44982         this.el.on('submit', this.onSubmit, this);
44983         this.el.addClass('x-form');
44984     },
44985
44986     // private
44987     onSubmit : function(e){
44988         e.stopEvent();
44989     },
44990
44991     /**
44992      * Returns true if client-side validation on the form is successful.
44993      * @return Boolean
44994      */
44995     isValid : function(){
44996         var valid = true;
44997         this.items.each(function(f){
44998            if(!f.validate()){
44999                valid = false;
45000            }
45001         });
45002         return valid;
45003     },
45004
45005     /**
45006      * Returns true if any fields in this form have changed since their original load.
45007      * @return Boolean
45008      */
45009     isDirty : function(){
45010         var dirty = false;
45011         this.items.each(function(f){
45012            if(f.isDirty()){
45013                dirty = true;
45014                return false;
45015            }
45016         });
45017         return dirty;
45018     },
45019
45020     /**
45021      * Performs a predefined action (submit or load) or custom actions you define on this form.
45022      * @param {String} actionName The name of the action type
45023      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45024      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45025      * accept other config options):
45026      * <pre>
45027 Property          Type             Description
45028 ----------------  ---------------  ----------------------------------------------------------------------------------
45029 url               String           The url for the action (defaults to the form's url)
45030 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45031 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45032 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45033                                    validate the form on the client (defaults to false)
45034      * </pre>
45035      * @return {BasicForm} this
45036      */
45037     doAction : function(action, options){
45038         if(typeof action == 'string'){
45039             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45040         }
45041         if(this.fireEvent('beforeaction', this, action) !== false){
45042             this.beforeAction(action);
45043             action.run.defer(100, action);
45044         }
45045         return this;
45046     },
45047
45048     /**
45049      * Shortcut to do a submit action.
45050      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45051      * @return {BasicForm} this
45052      */
45053     submit : function(options){
45054         this.doAction('submit', options);
45055         return this;
45056     },
45057
45058     /**
45059      * Shortcut to do a load action.
45060      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45061      * @return {BasicForm} this
45062      */
45063     load : function(options){
45064         this.doAction('load', options);
45065         return this;
45066     },
45067
45068     /**
45069      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45070      * @param {Record} record The record to edit
45071      * @return {BasicForm} this
45072      */
45073     updateRecord : function(record){
45074         record.beginEdit();
45075         var fs = record.fields;
45076         fs.each(function(f){
45077             var field = this.findField(f.name);
45078             if(field){
45079                 record.set(f.name, field.getValue());
45080             }
45081         }, this);
45082         record.endEdit();
45083         return this;
45084     },
45085
45086     /**
45087      * Loads an Roo.data.Record into this form.
45088      * @param {Record} record The record to load
45089      * @return {BasicForm} this
45090      */
45091     loadRecord : function(record){
45092         this.setValues(record.data);
45093         return this;
45094     },
45095
45096     // private
45097     beforeAction : function(action){
45098         var o = action.options;
45099         
45100        
45101         if(this.waitMsgTarget === true){
45102             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45103         }else if(this.waitMsgTarget){
45104             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45105             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45106         }else {
45107             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45108         }
45109          
45110     },
45111
45112     // private
45113     afterAction : function(action, success){
45114         this.activeAction = null;
45115         var o = action.options;
45116         
45117         if(this.waitMsgTarget === true){
45118             this.el.unmask();
45119         }else if(this.waitMsgTarget){
45120             this.waitMsgTarget.unmask();
45121         }else{
45122             Roo.MessageBox.updateProgress(1);
45123             Roo.MessageBox.hide();
45124         }
45125          
45126         if(success){
45127             if(o.reset){
45128                 this.reset();
45129             }
45130             Roo.callback(o.success, o.scope, [this, action]);
45131             this.fireEvent('actioncomplete', this, action);
45132             
45133         }else{
45134             
45135             // failure condition..
45136             // we have a scenario where updates need confirming.
45137             // eg. if a locking scenario exists..
45138             // we look for { errors : { needs_confirm : true }} in the response.
45139             if (
45140                 (typeof(action.result) != 'undefined')  &&
45141                 (typeof(action.result.errors) != 'undefined')  &&
45142                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45143            ){
45144                 var _t = this;
45145                 Roo.MessageBox.confirm(
45146                     "Change requires confirmation",
45147                     action.result.errorMsg,
45148                     function(r) {
45149                         if (r != 'yes') {
45150                             return;
45151                         }
45152                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45153                     }
45154                     
45155                 );
45156                 
45157                 
45158                 
45159                 return;
45160             }
45161             
45162             Roo.callback(o.failure, o.scope, [this, action]);
45163             // show an error message if no failed handler is set..
45164             if (!this.hasListener('actionfailed')) {
45165                 Roo.MessageBox.alert("Error",
45166                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45167                         action.result.errorMsg :
45168                         "Saving Failed, please check your entries or try again"
45169                 );
45170             }
45171             
45172             this.fireEvent('actionfailed', this, action);
45173         }
45174         
45175     },
45176
45177     /**
45178      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45179      * @param {String} id The value to search for
45180      * @return Field
45181      */
45182     findField : function(id){
45183         var field = this.items.get(id);
45184         if(!field){
45185             this.items.each(function(f){
45186                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45187                     field = f;
45188                     return false;
45189                 }
45190             });
45191         }
45192         return field || null;
45193     },
45194
45195     /**
45196      * Add a secondary form to this one, 
45197      * Used to provide tabbed forms. One form is primary, with hidden values 
45198      * which mirror the elements from the other forms.
45199      * 
45200      * @param {Roo.form.Form} form to add.
45201      * 
45202      */
45203     addForm : function(form)
45204     {
45205        
45206         if (this.childForms.indexOf(form) > -1) {
45207             // already added..
45208             return;
45209         }
45210         this.childForms.push(form);
45211         var n = '';
45212         Roo.each(form.allItems, function (fe) {
45213             
45214             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45215             if (this.findField(n)) { // already added..
45216                 return;
45217             }
45218             var add = new Roo.form.Hidden({
45219                 name : n
45220             });
45221             add.render(this.el);
45222             
45223             this.add( add );
45224         }, this);
45225         
45226     },
45227     /**
45228      * Mark fields in this form invalid in bulk.
45229      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45230      * @return {BasicForm} this
45231      */
45232     markInvalid : function(errors){
45233         if(errors instanceof Array){
45234             for(var i = 0, len = errors.length; i < len; i++){
45235                 var fieldError = errors[i];
45236                 var f = this.findField(fieldError.id);
45237                 if(f){
45238                     f.markInvalid(fieldError.msg);
45239                 }
45240             }
45241         }else{
45242             var field, id;
45243             for(id in errors){
45244                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45245                     field.markInvalid(errors[id]);
45246                 }
45247             }
45248         }
45249         Roo.each(this.childForms || [], function (f) {
45250             f.markInvalid(errors);
45251         });
45252         
45253         return this;
45254     },
45255
45256     /**
45257      * Set values for fields in this form in bulk.
45258      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45259      * @return {BasicForm} this
45260      */
45261     setValues : function(values){
45262         if(values instanceof Array){ // array of objects
45263             for(var i = 0, len = values.length; i < len; i++){
45264                 var v = values[i];
45265                 var f = this.findField(v.id);
45266                 if(f){
45267                     f.setValue(v.value);
45268                     if(this.trackResetOnLoad){
45269                         f.originalValue = f.getValue();
45270                     }
45271                 }
45272             }
45273         }else{ // object hash
45274             var field, id;
45275             for(id in values){
45276                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45277                     
45278                     if (field.setFromData && 
45279                         field.valueField && 
45280                         field.displayField &&
45281                         // combos' with local stores can 
45282                         // be queried via setValue()
45283                         // to set their value..
45284                         (field.store && !field.store.isLocal)
45285                         ) {
45286                         // it's a combo
45287                         var sd = { };
45288                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45289                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45290                         field.setFromData(sd);
45291                         
45292                     } else {
45293                         field.setValue(values[id]);
45294                     }
45295                     
45296                     
45297                     if(this.trackResetOnLoad){
45298                         field.originalValue = field.getValue();
45299                     }
45300                 }
45301             }
45302         }
45303          
45304         Roo.each(this.childForms || [], function (f) {
45305             f.setValues(values);
45306         });
45307                 
45308         return this;
45309     },
45310
45311     /**
45312      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45313      * they are returned as an array.
45314      * @param {Boolean} asString
45315      * @return {Object}
45316      */
45317     getValues : function(asString){
45318         if (this.childForms) {
45319             // copy values from the child forms
45320             Roo.each(this.childForms, function (f) {
45321                 this.setValues(f.getValues());
45322             }, this);
45323         }
45324         
45325         
45326         
45327         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45328         if(asString === true){
45329             return fs;
45330         }
45331         return Roo.urlDecode(fs);
45332     },
45333     
45334     /**
45335      * Returns the fields in this form as an object with key/value pairs. 
45336      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45337      * @return {Object}
45338      */
45339     getFieldValues : function(with_hidden)
45340     {
45341         if (this.childForms) {
45342             // copy values from the child forms
45343             // should this call getFieldValues - probably not as we do not currently copy
45344             // hidden fields when we generate..
45345             Roo.each(this.childForms, function (f) {
45346                 this.setValues(f.getValues());
45347             }, this);
45348         }
45349         
45350         var ret = {};
45351         this.items.each(function(f){
45352             if (!f.getName()) {
45353                 return;
45354             }
45355             var v = f.getValue();
45356             if (f.inputType =='radio') {
45357                 if (typeof(ret[f.getName()]) == 'undefined') {
45358                     ret[f.getName()] = ''; // empty..
45359                 }
45360                 
45361                 if (!f.el.dom.checked) {
45362                     return;
45363                     
45364                 }
45365                 v = f.el.dom.value;
45366                 
45367             }
45368             
45369             // not sure if this supported any more..
45370             if ((typeof(v) == 'object') && f.getRawValue) {
45371                 v = f.getRawValue() ; // dates..
45372             }
45373             // combo boxes where name != hiddenName...
45374             if (f.name != f.getName()) {
45375                 ret[f.name] = f.getRawValue();
45376             }
45377             ret[f.getName()] = v;
45378         });
45379         
45380         return ret;
45381     },
45382
45383     /**
45384      * Clears all invalid messages in this form.
45385      * @return {BasicForm} this
45386      */
45387     clearInvalid : function(){
45388         this.items.each(function(f){
45389            f.clearInvalid();
45390         });
45391         
45392         Roo.each(this.childForms || [], function (f) {
45393             f.clearInvalid();
45394         });
45395         
45396         
45397         return this;
45398     },
45399
45400     /**
45401      * Resets this form.
45402      * @return {BasicForm} this
45403      */
45404     reset : function(){
45405         this.items.each(function(f){
45406             f.reset();
45407         });
45408         
45409         Roo.each(this.childForms || [], function (f) {
45410             f.reset();
45411         });
45412        
45413         
45414         return this;
45415     },
45416
45417     /**
45418      * Add Roo.form components to this form.
45419      * @param {Field} field1
45420      * @param {Field} field2 (optional)
45421      * @param {Field} etc (optional)
45422      * @return {BasicForm} this
45423      */
45424     add : function(){
45425         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45426         return this;
45427     },
45428
45429
45430     /**
45431      * Removes a field from the items collection (does NOT remove its markup).
45432      * @param {Field} field
45433      * @return {BasicForm} this
45434      */
45435     remove : function(field){
45436         this.items.remove(field);
45437         return this;
45438     },
45439
45440     /**
45441      * Looks at the fields in this form, checks them for an id attribute,
45442      * and calls applyTo on the existing dom element with that id.
45443      * @return {BasicForm} this
45444      */
45445     render : function(){
45446         this.items.each(function(f){
45447             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45448                 f.applyTo(f.id);
45449             }
45450         });
45451         return this;
45452     },
45453
45454     /**
45455      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45456      * @param {Object} values
45457      * @return {BasicForm} this
45458      */
45459     applyToFields : function(o){
45460         this.items.each(function(f){
45461            Roo.apply(f, o);
45462         });
45463         return this;
45464     },
45465
45466     /**
45467      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45468      * @param {Object} values
45469      * @return {BasicForm} this
45470      */
45471     applyIfToFields : function(o){
45472         this.items.each(function(f){
45473            Roo.applyIf(f, o);
45474         });
45475         return this;
45476     }
45477 });
45478
45479 // back compat
45480 Roo.BasicForm = Roo.form.BasicForm;/*
45481  * Based on:
45482  * Ext JS Library 1.1.1
45483  * Copyright(c) 2006-2007, Ext JS, LLC.
45484  *
45485  * Originally Released Under LGPL - original licence link has changed is not relivant.
45486  *
45487  * Fork - LGPL
45488  * <script type="text/javascript">
45489  */
45490
45491 /**
45492  * @class Roo.form.Form
45493  * @extends Roo.form.BasicForm
45494  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45495  * @constructor
45496  * @param {Object} config Configuration options
45497  */
45498 Roo.form.Form = function(config){
45499     var xitems =  [];
45500     if (config.items) {
45501         xitems = config.items;
45502         delete config.items;
45503     }
45504    
45505     
45506     Roo.form.Form.superclass.constructor.call(this, null, config);
45507     this.url = this.url || this.action;
45508     if(!this.root){
45509         this.root = new Roo.form.Layout(Roo.applyIf({
45510             id: Roo.id()
45511         }, config));
45512     }
45513     this.active = this.root;
45514     /**
45515      * Array of all the buttons that have been added to this form via {@link addButton}
45516      * @type Array
45517      */
45518     this.buttons = [];
45519     this.allItems = [];
45520     this.addEvents({
45521         /**
45522          * @event clientvalidation
45523          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45524          * @param {Form} this
45525          * @param {Boolean} valid true if the form has passed client-side validation
45526          */
45527         clientvalidation: true,
45528         /**
45529          * @event rendered
45530          * Fires when the form is rendered
45531          * @param {Roo.form.Form} form
45532          */
45533         rendered : true
45534     });
45535     
45536     if (this.progressUrl) {
45537             // push a hidden field onto the list of fields..
45538             this.addxtype( {
45539                     xns: Roo.form, 
45540                     xtype : 'Hidden', 
45541                     name : 'UPLOAD_IDENTIFIER' 
45542             });
45543         }
45544         
45545     
45546     Roo.each(xitems, this.addxtype, this);
45547     
45548     
45549     
45550 };
45551
45552 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45553     /**
45554      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45555      */
45556     /**
45557      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45558      */
45559     /**
45560      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45561      */
45562     buttonAlign:'center',
45563
45564     /**
45565      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45566      */
45567     minButtonWidth:75,
45568
45569     /**
45570      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45571      * This property cascades to child containers if not set.
45572      */
45573     labelAlign:'left',
45574
45575     /**
45576      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45577      * fires a looping event with that state. This is required to bind buttons to the valid
45578      * state using the config value formBind:true on the button.
45579      */
45580     monitorValid : false,
45581
45582     /**
45583      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45584      */
45585     monitorPoll : 200,
45586     
45587     /**
45588      * @cfg {String} progressUrl - Url to return progress data 
45589      */
45590     
45591     progressUrl : false,
45592   
45593     /**
45594      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45595      * fields are added and the column is closed. If no fields are passed the column remains open
45596      * until end() is called.
45597      * @param {Object} config The config to pass to the column
45598      * @param {Field} field1 (optional)
45599      * @param {Field} field2 (optional)
45600      * @param {Field} etc (optional)
45601      * @return Column The column container object
45602      */
45603     column : function(c){
45604         var col = new Roo.form.Column(c);
45605         this.start(col);
45606         if(arguments.length > 1){ // duplicate code required because of Opera
45607             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45608             this.end();
45609         }
45610         return col;
45611     },
45612
45613     /**
45614      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45615      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45616      * until end() is called.
45617      * @param {Object} config The config to pass to the fieldset
45618      * @param {Field} field1 (optional)
45619      * @param {Field} field2 (optional)
45620      * @param {Field} etc (optional)
45621      * @return FieldSet The fieldset container object
45622      */
45623     fieldset : function(c){
45624         var fs = new Roo.form.FieldSet(c);
45625         this.start(fs);
45626         if(arguments.length > 1){ // duplicate code required because of Opera
45627             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45628             this.end();
45629         }
45630         return fs;
45631     },
45632
45633     /**
45634      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45635      * fields are added and the container is closed. If no fields are passed the container remains open
45636      * until end() is called.
45637      * @param {Object} config The config to pass to the Layout
45638      * @param {Field} field1 (optional)
45639      * @param {Field} field2 (optional)
45640      * @param {Field} etc (optional)
45641      * @return Layout The container object
45642      */
45643     container : function(c){
45644         var l = new Roo.form.Layout(c);
45645         this.start(l);
45646         if(arguments.length > 1){ // duplicate code required because of Opera
45647             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45648             this.end();
45649         }
45650         return l;
45651     },
45652
45653     /**
45654      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45655      * @param {Object} container A Roo.form.Layout or subclass of Layout
45656      * @return {Form} this
45657      */
45658     start : function(c){
45659         // cascade label info
45660         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45661         this.active.stack.push(c);
45662         c.ownerCt = this.active;
45663         this.active = c;
45664         return this;
45665     },
45666
45667     /**
45668      * Closes the current open container
45669      * @return {Form} this
45670      */
45671     end : function(){
45672         if(this.active == this.root){
45673             return this;
45674         }
45675         this.active = this.active.ownerCt;
45676         return this;
45677     },
45678
45679     /**
45680      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45681      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45682      * as the label of the field.
45683      * @param {Field} field1
45684      * @param {Field} field2 (optional)
45685      * @param {Field} etc. (optional)
45686      * @return {Form} this
45687      */
45688     add : function(){
45689         this.active.stack.push.apply(this.active.stack, arguments);
45690         this.allItems.push.apply(this.allItems,arguments);
45691         var r = [];
45692         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45693             if(a[i].isFormField){
45694                 r.push(a[i]);
45695             }
45696         }
45697         if(r.length > 0){
45698             Roo.form.Form.superclass.add.apply(this, r);
45699         }
45700         return this;
45701     },
45702     
45703
45704     
45705     
45706     
45707      /**
45708      * Find any element that has been added to a form, using it's ID or name
45709      * This can include framesets, columns etc. along with regular fields..
45710      * @param {String} id - id or name to find.
45711      
45712      * @return {Element} e - or false if nothing found.
45713      */
45714     findbyId : function(id)
45715     {
45716         var ret = false;
45717         if (!id) {
45718             return ret;
45719         }
45720         Roo.each(this.allItems, function(f){
45721             if (f.id == id || f.name == id ){
45722                 ret = f;
45723                 return false;
45724             }
45725         });
45726         return ret;
45727     },
45728
45729     
45730     
45731     /**
45732      * Render this form into the passed container. This should only be called once!
45733      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45734      * @return {Form} this
45735      */
45736     render : function(ct)
45737     {
45738         
45739         
45740         
45741         ct = Roo.get(ct);
45742         var o = this.autoCreate || {
45743             tag: 'form',
45744             method : this.method || 'POST',
45745             id : this.id || Roo.id()
45746         };
45747         this.initEl(ct.createChild(o));
45748
45749         this.root.render(this.el);
45750         
45751        
45752              
45753         this.items.each(function(f){
45754             f.render('x-form-el-'+f.id);
45755         });
45756
45757         if(this.buttons.length > 0){
45758             // tables are required to maintain order and for correct IE layout
45759             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45760                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45761                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45762             }}, null, true);
45763             var tr = tb.getElementsByTagName('tr')[0];
45764             for(var i = 0, len = this.buttons.length; i < len; i++) {
45765                 var b = this.buttons[i];
45766                 var td = document.createElement('td');
45767                 td.className = 'x-form-btn-td';
45768                 b.render(tr.appendChild(td));
45769             }
45770         }
45771         if(this.monitorValid){ // initialize after render
45772             this.startMonitoring();
45773         }
45774         this.fireEvent('rendered', this);
45775         return this;
45776     },
45777
45778     /**
45779      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45780      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45781      * object or a valid Roo.DomHelper element config
45782      * @param {Function} handler The function called when the button is clicked
45783      * @param {Object} scope (optional) The scope of the handler function
45784      * @return {Roo.Button}
45785      */
45786     addButton : function(config, handler, scope){
45787         var bc = {
45788             handler: handler,
45789             scope: scope,
45790             minWidth: this.minButtonWidth,
45791             hideParent:true
45792         };
45793         if(typeof config == "string"){
45794             bc.text = config;
45795         }else{
45796             Roo.apply(bc, config);
45797         }
45798         var btn = new Roo.Button(null, bc);
45799         this.buttons.push(btn);
45800         return btn;
45801     },
45802
45803      /**
45804      * Adds a series of form elements (using the xtype property as the factory method.
45805      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45806      * @param {Object} config 
45807      */
45808     
45809     addxtype : function()
45810     {
45811         var ar = Array.prototype.slice.call(arguments, 0);
45812         var ret = false;
45813         for(var i = 0; i < ar.length; i++) {
45814             if (!ar[i]) {
45815                 continue; // skip -- if this happends something invalid got sent, we 
45816                 // should ignore it, as basically that interface element will not show up
45817                 // and that should be pretty obvious!!
45818             }
45819             
45820             if (Roo.form[ar[i].xtype]) {
45821                 ar[i].form = this;
45822                 var fe = Roo.factory(ar[i], Roo.form);
45823                 if (!ret) {
45824                     ret = fe;
45825                 }
45826                 fe.form = this;
45827                 if (fe.store) {
45828                     fe.store.form = this;
45829                 }
45830                 if (fe.isLayout) {  
45831                          
45832                     this.start(fe);
45833                     this.allItems.push(fe);
45834                     if (fe.items && fe.addxtype) {
45835                         fe.addxtype.apply(fe, fe.items);
45836                         delete fe.items;
45837                     }
45838                      this.end();
45839                     continue;
45840                 }
45841                 
45842                 
45843                  
45844                 this.add(fe);
45845               //  console.log('adding ' + ar[i].xtype);
45846             }
45847             if (ar[i].xtype == 'Button') {  
45848                 //console.log('adding button');
45849                 //console.log(ar[i]);
45850                 this.addButton(ar[i]);
45851                 this.allItems.push(fe);
45852                 continue;
45853             }
45854             
45855             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45856                 alert('end is not supported on xtype any more, use items');
45857             //    this.end();
45858             //    //console.log('adding end');
45859             }
45860             
45861         }
45862         return ret;
45863     },
45864     
45865     /**
45866      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45867      * option "monitorValid"
45868      */
45869     startMonitoring : function(){
45870         if(!this.bound){
45871             this.bound = true;
45872             Roo.TaskMgr.start({
45873                 run : this.bindHandler,
45874                 interval : this.monitorPoll || 200,
45875                 scope: this
45876             });
45877         }
45878     },
45879
45880     /**
45881      * Stops monitoring of the valid state of this form
45882      */
45883     stopMonitoring : function(){
45884         this.bound = false;
45885     },
45886
45887     // private
45888     bindHandler : function(){
45889         if(!this.bound){
45890             return false; // stops binding
45891         }
45892         var valid = true;
45893         this.items.each(function(f){
45894             if(!f.isValid(true)){
45895                 valid = false;
45896                 return false;
45897             }
45898         });
45899         for(var i = 0, len = this.buttons.length; i < len; i++){
45900             var btn = this.buttons[i];
45901             if(btn.formBind === true && btn.disabled === valid){
45902                 btn.setDisabled(!valid);
45903             }
45904         }
45905         this.fireEvent('clientvalidation', this, valid);
45906     }
45907     
45908     
45909     
45910     
45911     
45912     
45913     
45914     
45915 });
45916
45917
45918 // back compat
45919 Roo.Form = Roo.form.Form;
45920 /*
45921  * Based on:
45922  * Ext JS Library 1.1.1
45923  * Copyright(c) 2006-2007, Ext JS, LLC.
45924  *
45925  * Originally Released Under LGPL - original licence link has changed is not relivant.
45926  *
45927  * Fork - LGPL
45928  * <script type="text/javascript">
45929  */
45930
45931 // as we use this in bootstrap.
45932 Roo.namespace('Roo.form');
45933  /**
45934  * @class Roo.form.Action
45935  * Internal Class used to handle form actions
45936  * @constructor
45937  * @param {Roo.form.BasicForm} el The form element or its id
45938  * @param {Object} config Configuration options
45939  */
45940
45941  
45942  
45943 // define the action interface
45944 Roo.form.Action = function(form, options){
45945     this.form = form;
45946     this.options = options || {};
45947 };
45948 /**
45949  * Client Validation Failed
45950  * @const 
45951  */
45952 Roo.form.Action.CLIENT_INVALID = 'client';
45953 /**
45954  * Server Validation Failed
45955  * @const 
45956  */
45957 Roo.form.Action.SERVER_INVALID = 'server';
45958  /**
45959  * Connect to Server Failed
45960  * @const 
45961  */
45962 Roo.form.Action.CONNECT_FAILURE = 'connect';
45963 /**
45964  * Reading Data from Server Failed
45965  * @const 
45966  */
45967 Roo.form.Action.LOAD_FAILURE = 'load';
45968
45969 Roo.form.Action.prototype = {
45970     type : 'default',
45971     failureType : undefined,
45972     response : undefined,
45973     result : undefined,
45974
45975     // interface method
45976     run : function(options){
45977
45978     },
45979
45980     // interface method
45981     success : function(response){
45982
45983     },
45984
45985     // interface method
45986     handleResponse : function(response){
45987
45988     },
45989
45990     // default connection failure
45991     failure : function(response){
45992         
45993         this.response = response;
45994         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45995         this.form.afterAction(this, false);
45996     },
45997
45998     processResponse : function(response){
45999         this.response = response;
46000         if(!response.responseText){
46001             return true;
46002         }
46003         this.result = this.handleResponse(response);
46004         return this.result;
46005     },
46006
46007     // utility functions used internally
46008     getUrl : function(appendParams){
46009         var url = this.options.url || this.form.url || this.form.el.dom.action;
46010         if(appendParams){
46011             var p = this.getParams();
46012             if(p){
46013                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46014             }
46015         }
46016         return url;
46017     },
46018
46019     getMethod : function(){
46020         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46021     },
46022
46023     getParams : function(){
46024         var bp = this.form.baseParams;
46025         var p = this.options.params;
46026         if(p){
46027             if(typeof p == "object"){
46028                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46029             }else if(typeof p == 'string' && bp){
46030                 p += '&' + Roo.urlEncode(bp);
46031             }
46032         }else if(bp){
46033             p = Roo.urlEncode(bp);
46034         }
46035         return p;
46036     },
46037
46038     createCallback : function(){
46039         return {
46040             success: this.success,
46041             failure: this.failure,
46042             scope: this,
46043             timeout: (this.form.timeout*1000),
46044             upload: this.form.fileUpload ? this.success : undefined
46045         };
46046     }
46047 };
46048
46049 Roo.form.Action.Submit = function(form, options){
46050     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46051 };
46052
46053 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46054     type : 'submit',
46055
46056     haveProgress : false,
46057     uploadComplete : false,
46058     
46059     // uploadProgress indicator.
46060     uploadProgress : function()
46061     {
46062         if (!this.form.progressUrl) {
46063             return;
46064         }
46065         
46066         if (!this.haveProgress) {
46067             Roo.MessageBox.progress("Uploading", "Uploading");
46068         }
46069         if (this.uploadComplete) {
46070            Roo.MessageBox.hide();
46071            return;
46072         }
46073         
46074         this.haveProgress = true;
46075    
46076         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46077         
46078         var c = new Roo.data.Connection();
46079         c.request({
46080             url : this.form.progressUrl,
46081             params: {
46082                 id : uid
46083             },
46084             method: 'GET',
46085             success : function(req){
46086                //console.log(data);
46087                 var rdata = false;
46088                 var edata;
46089                 try  {
46090                    rdata = Roo.decode(req.responseText)
46091                 } catch (e) {
46092                     Roo.log("Invalid data from server..");
46093                     Roo.log(edata);
46094                     return;
46095                 }
46096                 if (!rdata || !rdata.success) {
46097                     Roo.log(rdata);
46098                     Roo.MessageBox.alert(Roo.encode(rdata));
46099                     return;
46100                 }
46101                 var data = rdata.data;
46102                 
46103                 if (this.uploadComplete) {
46104                    Roo.MessageBox.hide();
46105                    return;
46106                 }
46107                    
46108                 if (data){
46109                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46110                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46111                     );
46112                 }
46113                 this.uploadProgress.defer(2000,this);
46114             },
46115        
46116             failure: function(data) {
46117                 Roo.log('progress url failed ');
46118                 Roo.log(data);
46119             },
46120             scope : this
46121         });
46122            
46123     },
46124     
46125     
46126     run : function()
46127     {
46128         // run get Values on the form, so it syncs any secondary forms.
46129         this.form.getValues();
46130         
46131         var o = this.options;
46132         var method = this.getMethod();
46133         var isPost = method == 'POST';
46134         if(o.clientValidation === false || this.form.isValid()){
46135             
46136             if (this.form.progressUrl) {
46137                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46138                     (new Date() * 1) + '' + Math.random());
46139                     
46140             } 
46141             
46142             
46143             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46144                 form:this.form.el.dom,
46145                 url:this.getUrl(!isPost),
46146                 method: method,
46147                 params:isPost ? this.getParams() : null,
46148                 isUpload: this.form.fileUpload
46149             }));
46150             
46151             this.uploadProgress();
46152
46153         }else if (o.clientValidation !== false){ // client validation failed
46154             this.failureType = Roo.form.Action.CLIENT_INVALID;
46155             this.form.afterAction(this, false);
46156         }
46157     },
46158
46159     success : function(response)
46160     {
46161         this.uploadComplete= true;
46162         if (this.haveProgress) {
46163             Roo.MessageBox.hide();
46164         }
46165         
46166         
46167         var result = this.processResponse(response);
46168         if(result === true || result.success){
46169             this.form.afterAction(this, true);
46170             return;
46171         }
46172         if(result.errors){
46173             this.form.markInvalid(result.errors);
46174             this.failureType = Roo.form.Action.SERVER_INVALID;
46175         }
46176         this.form.afterAction(this, false);
46177     },
46178     failure : function(response)
46179     {
46180         this.uploadComplete= true;
46181         if (this.haveProgress) {
46182             Roo.MessageBox.hide();
46183         }
46184         
46185         this.response = response;
46186         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46187         this.form.afterAction(this, false);
46188     },
46189     
46190     handleResponse : function(response){
46191         if(this.form.errorReader){
46192             var rs = this.form.errorReader.read(response);
46193             var errors = [];
46194             if(rs.records){
46195                 for(var i = 0, len = rs.records.length; i < len; i++) {
46196                     var r = rs.records[i];
46197                     errors[i] = r.data;
46198                 }
46199             }
46200             if(errors.length < 1){
46201                 errors = null;
46202             }
46203             return {
46204                 success : rs.success,
46205                 errors : errors
46206             };
46207         }
46208         var ret = false;
46209         try {
46210             ret = Roo.decode(response.responseText);
46211         } catch (e) {
46212             ret = {
46213                 success: false,
46214                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46215                 errors : []
46216             };
46217         }
46218         return ret;
46219         
46220     }
46221 });
46222
46223
46224 Roo.form.Action.Load = function(form, options){
46225     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46226     this.reader = this.form.reader;
46227 };
46228
46229 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46230     type : 'load',
46231
46232     run : function(){
46233         
46234         Roo.Ajax.request(Roo.apply(
46235                 this.createCallback(), {
46236                     method:this.getMethod(),
46237                     url:this.getUrl(false),
46238                     params:this.getParams()
46239         }));
46240     },
46241
46242     success : function(response){
46243         
46244         var result = this.processResponse(response);
46245         if(result === true || !result.success || !result.data){
46246             this.failureType = Roo.form.Action.LOAD_FAILURE;
46247             this.form.afterAction(this, false);
46248             return;
46249         }
46250         this.form.clearInvalid();
46251         this.form.setValues(result.data);
46252         this.form.afterAction(this, true);
46253     },
46254
46255     handleResponse : function(response){
46256         if(this.form.reader){
46257             var rs = this.form.reader.read(response);
46258             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46259             return {
46260                 success : rs.success,
46261                 data : data
46262             };
46263         }
46264         return Roo.decode(response.responseText);
46265     }
46266 });
46267
46268 Roo.form.Action.ACTION_TYPES = {
46269     'load' : Roo.form.Action.Load,
46270     'submit' : Roo.form.Action.Submit
46271 };/*
46272  * Based on:
46273  * Ext JS Library 1.1.1
46274  * Copyright(c) 2006-2007, Ext JS, LLC.
46275  *
46276  * Originally Released Under LGPL - original licence link has changed is not relivant.
46277  *
46278  * Fork - LGPL
46279  * <script type="text/javascript">
46280  */
46281  
46282 /**
46283  * @class Roo.form.Layout
46284  * @extends Roo.Component
46285  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46286  * @constructor
46287  * @param {Object} config Configuration options
46288  */
46289 Roo.form.Layout = function(config){
46290     var xitems = [];
46291     if (config.items) {
46292         xitems = config.items;
46293         delete config.items;
46294     }
46295     Roo.form.Layout.superclass.constructor.call(this, config);
46296     this.stack = [];
46297     Roo.each(xitems, this.addxtype, this);
46298      
46299 };
46300
46301 Roo.extend(Roo.form.Layout, Roo.Component, {
46302     /**
46303      * @cfg {String/Object} autoCreate
46304      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46305      */
46306     /**
46307      * @cfg {String/Object/Function} style
46308      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46309      * a function which returns such a specification.
46310      */
46311     /**
46312      * @cfg {String} labelAlign
46313      * Valid values are "left," "top" and "right" (defaults to "left")
46314      */
46315     /**
46316      * @cfg {Number} labelWidth
46317      * Fixed width in pixels of all field labels (defaults to undefined)
46318      */
46319     /**
46320      * @cfg {Boolean} clear
46321      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46322      */
46323     clear : true,
46324     /**
46325      * @cfg {String} labelSeparator
46326      * The separator to use after field labels (defaults to ':')
46327      */
46328     labelSeparator : ':',
46329     /**
46330      * @cfg {Boolean} hideLabels
46331      * True to suppress the display of field labels in this layout (defaults to false)
46332      */
46333     hideLabels : false,
46334
46335     // private
46336     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46337     
46338     isLayout : true,
46339     
46340     // private
46341     onRender : function(ct, position){
46342         if(this.el){ // from markup
46343             this.el = Roo.get(this.el);
46344         }else {  // generate
46345             var cfg = this.getAutoCreate();
46346             this.el = ct.createChild(cfg, position);
46347         }
46348         if(this.style){
46349             this.el.applyStyles(this.style);
46350         }
46351         if(this.labelAlign){
46352             this.el.addClass('x-form-label-'+this.labelAlign);
46353         }
46354         if(this.hideLabels){
46355             this.labelStyle = "display:none";
46356             this.elementStyle = "padding-left:0;";
46357         }else{
46358             if(typeof this.labelWidth == 'number'){
46359                 this.labelStyle = "width:"+this.labelWidth+"px;";
46360                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46361             }
46362             if(this.labelAlign == 'top'){
46363                 this.labelStyle = "width:auto;";
46364                 this.elementStyle = "padding-left:0;";
46365             }
46366         }
46367         var stack = this.stack;
46368         var slen = stack.length;
46369         if(slen > 0){
46370             if(!this.fieldTpl){
46371                 var t = new Roo.Template(
46372                     '<div class="x-form-item {5}">',
46373                         '<label for="{0}" style="{2}">{1}{4}</label>',
46374                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46375                         '</div>',
46376                     '</div><div class="x-form-clear-left"></div>'
46377                 );
46378                 t.disableFormats = true;
46379                 t.compile();
46380                 Roo.form.Layout.prototype.fieldTpl = t;
46381             }
46382             for(var i = 0; i < slen; i++) {
46383                 if(stack[i].isFormField){
46384                     this.renderField(stack[i]);
46385                 }else{
46386                     this.renderComponent(stack[i]);
46387                 }
46388             }
46389         }
46390         if(this.clear){
46391             this.el.createChild({cls:'x-form-clear'});
46392         }
46393     },
46394
46395     // private
46396     renderField : function(f){
46397         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46398                f.id, //0
46399                f.fieldLabel, //1
46400                f.labelStyle||this.labelStyle||'', //2
46401                this.elementStyle||'', //3
46402                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46403                f.itemCls||this.itemCls||''  //5
46404        ], true).getPrevSibling());
46405     },
46406
46407     // private
46408     renderComponent : function(c){
46409         c.render(c.isLayout ? this.el : this.el.createChild());    
46410     },
46411     /**
46412      * Adds a object form elements (using the xtype property as the factory method.)
46413      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46414      * @param {Object} config 
46415      */
46416     addxtype : function(o)
46417     {
46418         // create the lement.
46419         o.form = this.form;
46420         var fe = Roo.factory(o, Roo.form);
46421         this.form.allItems.push(fe);
46422         this.stack.push(fe);
46423         
46424         if (fe.isFormField) {
46425             this.form.items.add(fe);
46426         }
46427          
46428         return fe;
46429     }
46430 });
46431
46432 /**
46433  * @class Roo.form.Column
46434  * @extends Roo.form.Layout
46435  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46436  * @constructor
46437  * @param {Object} config Configuration options
46438  */
46439 Roo.form.Column = function(config){
46440     Roo.form.Column.superclass.constructor.call(this, config);
46441 };
46442
46443 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46444     /**
46445      * @cfg {Number/String} width
46446      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46447      */
46448     /**
46449      * @cfg {String/Object} autoCreate
46450      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46451      */
46452
46453     // private
46454     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46455
46456     // private
46457     onRender : function(ct, position){
46458         Roo.form.Column.superclass.onRender.call(this, ct, position);
46459         if(this.width){
46460             this.el.setWidth(this.width);
46461         }
46462     }
46463 });
46464
46465
46466 /**
46467  * @class Roo.form.Row
46468  * @extends Roo.form.Layout
46469  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46470  * @constructor
46471  * @param {Object} config Configuration options
46472  */
46473
46474  
46475 Roo.form.Row = function(config){
46476     Roo.form.Row.superclass.constructor.call(this, config);
46477 };
46478  
46479 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46480       /**
46481      * @cfg {Number/String} width
46482      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46483      */
46484     /**
46485      * @cfg {Number/String} height
46486      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46487      */
46488     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46489     
46490     padWidth : 20,
46491     // private
46492     onRender : function(ct, position){
46493         //console.log('row render');
46494         if(!this.rowTpl){
46495             var t = new Roo.Template(
46496                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46497                     '<label for="{0}" style="{2}">{1}{4}</label>',
46498                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46499                     '</div>',
46500                 '</div>'
46501             );
46502             t.disableFormats = true;
46503             t.compile();
46504             Roo.form.Layout.prototype.rowTpl = t;
46505         }
46506         this.fieldTpl = this.rowTpl;
46507         
46508         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46509         var labelWidth = 100;
46510         
46511         if ((this.labelAlign != 'top')) {
46512             if (typeof this.labelWidth == 'number') {
46513                 labelWidth = this.labelWidth
46514             }
46515             this.padWidth =  20 + labelWidth;
46516             
46517         }
46518         
46519         Roo.form.Column.superclass.onRender.call(this, ct, position);
46520         if(this.width){
46521             this.el.setWidth(this.width);
46522         }
46523         if(this.height){
46524             this.el.setHeight(this.height);
46525         }
46526     },
46527     
46528     // private
46529     renderField : function(f){
46530         f.fieldEl = this.fieldTpl.append(this.el, [
46531                f.id, f.fieldLabel,
46532                f.labelStyle||this.labelStyle||'',
46533                this.elementStyle||'',
46534                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46535                f.itemCls||this.itemCls||'',
46536                f.width ? f.width + this.padWidth : 160 + this.padWidth
46537        ],true);
46538     }
46539 });
46540  
46541
46542 /**
46543  * @class Roo.form.FieldSet
46544  * @extends Roo.form.Layout
46545  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46546  * @constructor
46547  * @param {Object} config Configuration options
46548  */
46549 Roo.form.FieldSet = function(config){
46550     Roo.form.FieldSet.superclass.constructor.call(this, config);
46551 };
46552
46553 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46554     /**
46555      * @cfg {String} legend
46556      * The text to display as the legend for the FieldSet (defaults to '')
46557      */
46558     /**
46559      * @cfg {String/Object} autoCreate
46560      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46561      */
46562
46563     // private
46564     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46565
46566     // private
46567     onRender : function(ct, position){
46568         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46569         if(this.legend){
46570             this.setLegend(this.legend);
46571         }
46572     },
46573
46574     // private
46575     setLegend : function(text){
46576         if(this.rendered){
46577             this.el.child('legend').update(text);
46578         }
46579     }
46580 });/*
46581  * Based on:
46582  * Ext JS Library 1.1.1
46583  * Copyright(c) 2006-2007, Ext JS, LLC.
46584  *
46585  * Originally Released Under LGPL - original licence link has changed is not relivant.
46586  *
46587  * Fork - LGPL
46588  * <script type="text/javascript">
46589  */
46590 /**
46591  * @class Roo.form.VTypes
46592  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46593  * @singleton
46594  */
46595 Roo.form.VTypes = function(){
46596     // closure these in so they are only created once.
46597     var alpha = /^[a-zA-Z_]+$/;
46598     var alphanum = /^[a-zA-Z0-9_]+$/;
46599     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46600     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46601
46602     // All these messages and functions are configurable
46603     return {
46604         /**
46605          * The function used to validate email addresses
46606          * @param {String} value The email address
46607          */
46608         'email' : function(v){
46609             return email.test(v);
46610         },
46611         /**
46612          * The error text to display when the email validation function returns false
46613          * @type String
46614          */
46615         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46616         /**
46617          * The keystroke filter mask to be applied on email input
46618          * @type RegExp
46619          */
46620         'emailMask' : /[a-z0-9_\.\-@]/i,
46621
46622         /**
46623          * The function used to validate URLs
46624          * @param {String} value The URL
46625          */
46626         'url' : function(v){
46627             return url.test(v);
46628         },
46629         /**
46630          * The error text to display when the url validation function returns false
46631          * @type String
46632          */
46633         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46634         
46635         /**
46636          * The function used to validate alpha values
46637          * @param {String} value The value
46638          */
46639         'alpha' : function(v){
46640             return alpha.test(v);
46641         },
46642         /**
46643          * The error text to display when the alpha validation function returns false
46644          * @type String
46645          */
46646         'alphaText' : 'This field should only contain letters and _',
46647         /**
46648          * The keystroke filter mask to be applied on alpha input
46649          * @type RegExp
46650          */
46651         'alphaMask' : /[a-z_]/i,
46652
46653         /**
46654          * The function used to validate alphanumeric values
46655          * @param {String} value The value
46656          */
46657         'alphanum' : function(v){
46658             return alphanum.test(v);
46659         },
46660         /**
46661          * The error text to display when the alphanumeric validation function returns false
46662          * @type String
46663          */
46664         'alphanumText' : 'This field should only contain letters, numbers and _',
46665         /**
46666          * The keystroke filter mask to be applied on alphanumeric input
46667          * @type RegExp
46668          */
46669         'alphanumMask' : /[a-z0-9_]/i
46670     };
46671 }();//<script type="text/javascript">
46672
46673 /**
46674  * @class Roo.form.FCKeditor
46675  * @extends Roo.form.TextArea
46676  * Wrapper around the FCKEditor http://www.fckeditor.net
46677  * @constructor
46678  * Creates a new FCKeditor
46679  * @param {Object} config Configuration options
46680  */
46681 Roo.form.FCKeditor = function(config){
46682     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46683     this.addEvents({
46684          /**
46685          * @event editorinit
46686          * Fired when the editor is initialized - you can add extra handlers here..
46687          * @param {FCKeditor} this
46688          * @param {Object} the FCK object.
46689          */
46690         editorinit : true
46691     });
46692     
46693     
46694 };
46695 Roo.form.FCKeditor.editors = { };
46696 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46697 {
46698     //defaultAutoCreate : {
46699     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46700     //},
46701     // private
46702     /**
46703      * @cfg {Object} fck options - see fck manual for details.
46704      */
46705     fckconfig : false,
46706     
46707     /**
46708      * @cfg {Object} fck toolbar set (Basic or Default)
46709      */
46710     toolbarSet : 'Basic',
46711     /**
46712      * @cfg {Object} fck BasePath
46713      */ 
46714     basePath : '/fckeditor/',
46715     
46716     
46717     frame : false,
46718     
46719     value : '',
46720     
46721    
46722     onRender : function(ct, position)
46723     {
46724         if(!this.el){
46725             this.defaultAutoCreate = {
46726                 tag: "textarea",
46727                 style:"width:300px;height:60px;",
46728                 autocomplete: "new-password"
46729             };
46730         }
46731         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46732         /*
46733         if(this.grow){
46734             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46735             if(this.preventScrollbars){
46736                 this.el.setStyle("overflow", "hidden");
46737             }
46738             this.el.setHeight(this.growMin);
46739         }
46740         */
46741         //console.log('onrender' + this.getId() );
46742         Roo.form.FCKeditor.editors[this.getId()] = this;
46743          
46744
46745         this.replaceTextarea() ;
46746         
46747     },
46748     
46749     getEditor : function() {
46750         return this.fckEditor;
46751     },
46752     /**
46753      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46754      * @param {Mixed} value The value to set
46755      */
46756     
46757     
46758     setValue : function(value)
46759     {
46760         //console.log('setValue: ' + value);
46761         
46762         if(typeof(value) == 'undefined') { // not sure why this is happending...
46763             return;
46764         }
46765         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46766         
46767         //if(!this.el || !this.getEditor()) {
46768         //    this.value = value;
46769             //this.setValue.defer(100,this,[value]);    
46770         //    return;
46771         //} 
46772         
46773         if(!this.getEditor()) {
46774             return;
46775         }
46776         
46777         this.getEditor().SetData(value);
46778         
46779         //
46780
46781     },
46782
46783     /**
46784      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46785      * @return {Mixed} value The field value
46786      */
46787     getValue : function()
46788     {
46789         
46790         if (this.frame && this.frame.dom.style.display == 'none') {
46791             return Roo.form.FCKeditor.superclass.getValue.call(this);
46792         }
46793         
46794         if(!this.el || !this.getEditor()) {
46795            
46796            // this.getValue.defer(100,this); 
46797             return this.value;
46798         }
46799        
46800         
46801         var value=this.getEditor().GetData();
46802         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46803         return Roo.form.FCKeditor.superclass.getValue.call(this);
46804         
46805
46806     },
46807
46808     /**
46809      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46810      * @return {Mixed} value The field value
46811      */
46812     getRawValue : function()
46813     {
46814         if (this.frame && this.frame.dom.style.display == 'none') {
46815             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46816         }
46817         
46818         if(!this.el || !this.getEditor()) {
46819             //this.getRawValue.defer(100,this); 
46820             return this.value;
46821             return;
46822         }
46823         
46824         
46825         
46826         var value=this.getEditor().GetData();
46827         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46828         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46829          
46830     },
46831     
46832     setSize : function(w,h) {
46833         
46834         
46835         
46836         //if (this.frame && this.frame.dom.style.display == 'none') {
46837         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46838         //    return;
46839         //}
46840         //if(!this.el || !this.getEditor()) {
46841         //    this.setSize.defer(100,this, [w,h]); 
46842         //    return;
46843         //}
46844         
46845         
46846         
46847         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46848         
46849         this.frame.dom.setAttribute('width', w);
46850         this.frame.dom.setAttribute('height', h);
46851         this.frame.setSize(w,h);
46852         
46853     },
46854     
46855     toggleSourceEdit : function(value) {
46856         
46857       
46858          
46859         this.el.dom.style.display = value ? '' : 'none';
46860         this.frame.dom.style.display = value ?  'none' : '';
46861         
46862     },
46863     
46864     
46865     focus: function(tag)
46866     {
46867         if (this.frame.dom.style.display == 'none') {
46868             return Roo.form.FCKeditor.superclass.focus.call(this);
46869         }
46870         if(!this.el || !this.getEditor()) {
46871             this.focus.defer(100,this, [tag]); 
46872             return;
46873         }
46874         
46875         
46876         
46877         
46878         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46879         this.getEditor().Focus();
46880         if (tgs.length) {
46881             if (!this.getEditor().Selection.GetSelection()) {
46882                 this.focus.defer(100,this, [tag]); 
46883                 return;
46884             }
46885             
46886             
46887             var r = this.getEditor().EditorDocument.createRange();
46888             r.setStart(tgs[0],0);
46889             r.setEnd(tgs[0],0);
46890             this.getEditor().Selection.GetSelection().removeAllRanges();
46891             this.getEditor().Selection.GetSelection().addRange(r);
46892             this.getEditor().Focus();
46893         }
46894         
46895     },
46896     
46897     
46898     
46899     replaceTextarea : function()
46900     {
46901         if ( document.getElementById( this.getId() + '___Frame' ) )
46902             return ;
46903         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46904         //{
46905             // We must check the elements firstly using the Id and then the name.
46906         var oTextarea = document.getElementById( this.getId() );
46907         
46908         var colElementsByName = document.getElementsByName( this.getId() ) ;
46909          
46910         oTextarea.style.display = 'none' ;
46911
46912         if ( oTextarea.tabIndex ) {            
46913             this.TabIndex = oTextarea.tabIndex ;
46914         }
46915         
46916         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46917         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46918         this.frame = Roo.get(this.getId() + '___Frame')
46919     },
46920     
46921     _getConfigHtml : function()
46922     {
46923         var sConfig = '' ;
46924
46925         for ( var o in this.fckconfig ) {
46926             sConfig += sConfig.length > 0  ? '&amp;' : '';
46927             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46928         }
46929
46930         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46931     },
46932     
46933     
46934     _getIFrameHtml : function()
46935     {
46936         var sFile = 'fckeditor.html' ;
46937         /* no idea what this is about..
46938         try
46939         {
46940             if ( (/fcksource=true/i).test( window.top.location.search ) )
46941                 sFile = 'fckeditor.original.html' ;
46942         }
46943         catch (e) { 
46944         */
46945
46946         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46947         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46948         
46949         
46950         var html = '<iframe id="' + this.getId() +
46951             '___Frame" src="' + sLink +
46952             '" width="' + this.width +
46953             '" height="' + this.height + '"' +
46954             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46955             ' frameborder="0" scrolling="no"></iframe>' ;
46956
46957         return html ;
46958     },
46959     
46960     _insertHtmlBefore : function( html, element )
46961     {
46962         if ( element.insertAdjacentHTML )       {
46963             // IE
46964             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46965         } else { // Gecko
46966             var oRange = document.createRange() ;
46967             oRange.setStartBefore( element ) ;
46968             var oFragment = oRange.createContextualFragment( html );
46969             element.parentNode.insertBefore( oFragment, element ) ;
46970         }
46971     }
46972     
46973     
46974   
46975     
46976     
46977     
46978     
46979
46980 });
46981
46982 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46983
46984 function FCKeditor_OnComplete(editorInstance){
46985     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46986     f.fckEditor = editorInstance;
46987     //console.log("loaded");
46988     f.fireEvent('editorinit', f, editorInstance);
46989
46990   
46991
46992  
46993
46994
46995
46996
46997
46998
46999
47000
47001
47002
47003
47004
47005
47006
47007
47008 //<script type="text/javascript">
47009 /**
47010  * @class Roo.form.GridField
47011  * @extends Roo.form.Field
47012  * Embed a grid (or editable grid into a form)
47013  * STATUS ALPHA
47014  * 
47015  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47016  * it needs 
47017  * xgrid.store = Roo.data.Store
47018  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47019  * xgrid.store.reader = Roo.data.JsonReader 
47020  * 
47021  * 
47022  * @constructor
47023  * Creates a new GridField
47024  * @param {Object} config Configuration options
47025  */
47026 Roo.form.GridField = function(config){
47027     Roo.form.GridField.superclass.constructor.call(this, config);
47028      
47029 };
47030
47031 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47032     /**
47033      * @cfg {Number} width  - used to restrict width of grid..
47034      */
47035     width : 100,
47036     /**
47037      * @cfg {Number} height - used to restrict height of grid..
47038      */
47039     height : 50,
47040      /**
47041      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47042          * 
47043          *}
47044      */
47045     xgrid : false, 
47046     /**
47047      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47048      * {tag: "input", type: "checkbox", autocomplete: "off"})
47049      */
47050    // defaultAutoCreate : { tag: 'div' },
47051     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47052     /**
47053      * @cfg {String} addTitle Text to include for adding a title.
47054      */
47055     addTitle : false,
47056     //
47057     onResize : function(){
47058         Roo.form.Field.superclass.onResize.apply(this, arguments);
47059     },
47060
47061     initEvents : function(){
47062         // Roo.form.Checkbox.superclass.initEvents.call(this);
47063         // has no events...
47064        
47065     },
47066
47067
47068     getResizeEl : function(){
47069         return this.wrap;
47070     },
47071
47072     getPositionEl : function(){
47073         return this.wrap;
47074     },
47075
47076     // private
47077     onRender : function(ct, position){
47078         
47079         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47080         var style = this.style;
47081         delete this.style;
47082         
47083         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47084         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47085         this.viewEl = this.wrap.createChild({ tag: 'div' });
47086         if (style) {
47087             this.viewEl.applyStyles(style);
47088         }
47089         if (this.width) {
47090             this.viewEl.setWidth(this.width);
47091         }
47092         if (this.height) {
47093             this.viewEl.setHeight(this.height);
47094         }
47095         //if(this.inputValue !== undefined){
47096         //this.setValue(this.value);
47097         
47098         
47099         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47100         
47101         
47102         this.grid.render();
47103         this.grid.getDataSource().on('remove', this.refreshValue, this);
47104         this.grid.getDataSource().on('update', this.refreshValue, this);
47105         this.grid.on('afteredit', this.refreshValue, this);
47106  
47107     },
47108      
47109     
47110     /**
47111      * Sets the value of the item. 
47112      * @param {String} either an object  or a string..
47113      */
47114     setValue : function(v){
47115         //this.value = v;
47116         v = v || []; // empty set..
47117         // this does not seem smart - it really only affects memoryproxy grids..
47118         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47119             var ds = this.grid.getDataSource();
47120             // assumes a json reader..
47121             var data = {}
47122             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47123             ds.loadData( data);
47124         }
47125         // clear selection so it does not get stale.
47126         if (this.grid.sm) { 
47127             this.grid.sm.clearSelections();
47128         }
47129         
47130         Roo.form.GridField.superclass.setValue.call(this, v);
47131         this.refreshValue();
47132         // should load data in the grid really....
47133     },
47134     
47135     // private
47136     refreshValue: function() {
47137          var val = [];
47138         this.grid.getDataSource().each(function(r) {
47139             val.push(r.data);
47140         });
47141         this.el.dom.value = Roo.encode(val);
47142     }
47143     
47144      
47145     
47146     
47147 });/*
47148  * Based on:
47149  * Ext JS Library 1.1.1
47150  * Copyright(c) 2006-2007, Ext JS, LLC.
47151  *
47152  * Originally Released Under LGPL - original licence link has changed is not relivant.
47153  *
47154  * Fork - LGPL
47155  * <script type="text/javascript">
47156  */
47157 /**
47158  * @class Roo.form.DisplayField
47159  * @extends Roo.form.Field
47160  * A generic Field to display non-editable data.
47161  * @constructor
47162  * Creates a new Display Field item.
47163  * @param {Object} config Configuration options
47164  */
47165 Roo.form.DisplayField = function(config){
47166     Roo.form.DisplayField.superclass.constructor.call(this, config);
47167     
47168 };
47169
47170 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47171     inputType:      'hidden',
47172     allowBlank:     true,
47173     readOnly:         true,
47174     
47175  
47176     /**
47177      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47178      */
47179     focusClass : undefined,
47180     /**
47181      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47182      */
47183     fieldClass: 'x-form-field',
47184     
47185      /**
47186      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47187      */
47188     valueRenderer: undefined,
47189     
47190     width: 100,
47191     /**
47192      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47193      * {tag: "input", type: "checkbox", autocomplete: "off"})
47194      */
47195      
47196  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47197
47198     onResize : function(){
47199         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47200         
47201     },
47202
47203     initEvents : function(){
47204         // Roo.form.Checkbox.superclass.initEvents.call(this);
47205         // has no events...
47206        
47207     },
47208
47209
47210     getResizeEl : function(){
47211         return this.wrap;
47212     },
47213
47214     getPositionEl : function(){
47215         return this.wrap;
47216     },
47217
47218     // private
47219     onRender : function(ct, position){
47220         
47221         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47222         //if(this.inputValue !== undefined){
47223         this.wrap = this.el.wrap();
47224         
47225         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47226         
47227         if (this.bodyStyle) {
47228             this.viewEl.applyStyles(this.bodyStyle);
47229         }
47230         //this.viewEl.setStyle('padding', '2px');
47231         
47232         this.setValue(this.value);
47233         
47234     },
47235 /*
47236     // private
47237     initValue : Roo.emptyFn,
47238
47239   */
47240
47241         // private
47242     onClick : function(){
47243         
47244     },
47245
47246     /**
47247      * Sets the checked state of the checkbox.
47248      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47249      */
47250     setValue : function(v){
47251         this.value = v;
47252         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47253         // this might be called before we have a dom element..
47254         if (!this.viewEl) {
47255             return;
47256         }
47257         this.viewEl.dom.innerHTML = html;
47258         Roo.form.DisplayField.superclass.setValue.call(this, v);
47259
47260     }
47261 });/*
47262  * 
47263  * Licence- LGPL
47264  * 
47265  */
47266
47267 /**
47268  * @class Roo.form.DayPicker
47269  * @extends Roo.form.Field
47270  * A Day picker show [M] [T] [W] ....
47271  * @constructor
47272  * Creates a new Day Picker
47273  * @param {Object} config Configuration options
47274  */
47275 Roo.form.DayPicker= function(config){
47276     Roo.form.DayPicker.superclass.constructor.call(this, config);
47277      
47278 };
47279
47280 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47281     /**
47282      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47283      */
47284     focusClass : undefined,
47285     /**
47286      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47287      */
47288     fieldClass: "x-form-field",
47289    
47290     /**
47291      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47292      * {tag: "input", type: "checkbox", autocomplete: "off"})
47293      */
47294     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47295     
47296    
47297     actionMode : 'viewEl', 
47298     //
47299     // private
47300  
47301     inputType : 'hidden',
47302     
47303      
47304     inputElement: false, // real input element?
47305     basedOn: false, // ????
47306     
47307     isFormField: true, // not sure where this is needed!!!!
47308
47309     onResize : function(){
47310         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47311         if(!this.boxLabel){
47312             this.el.alignTo(this.wrap, 'c-c');
47313         }
47314     },
47315
47316     initEvents : function(){
47317         Roo.form.Checkbox.superclass.initEvents.call(this);
47318         this.el.on("click", this.onClick,  this);
47319         this.el.on("change", this.onClick,  this);
47320     },
47321
47322
47323     getResizeEl : function(){
47324         return this.wrap;
47325     },
47326
47327     getPositionEl : function(){
47328         return this.wrap;
47329     },
47330
47331     
47332     // private
47333     onRender : function(ct, position){
47334         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47335        
47336         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47337         
47338         var r1 = '<table><tr>';
47339         var r2 = '<tr class="x-form-daypick-icons">';
47340         for (var i=0; i < 7; i++) {
47341             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47342             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47343         }
47344         
47345         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47346         viewEl.select('img').on('click', this.onClick, this);
47347         this.viewEl = viewEl;   
47348         
47349         
47350         // this will not work on Chrome!!!
47351         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47352         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47353         
47354         
47355           
47356
47357     },
47358
47359     // private
47360     initValue : Roo.emptyFn,
47361
47362     /**
47363      * Returns the checked state of the checkbox.
47364      * @return {Boolean} True if checked, else false
47365      */
47366     getValue : function(){
47367         return this.el.dom.value;
47368         
47369     },
47370
47371         // private
47372     onClick : function(e){ 
47373         //this.setChecked(!this.checked);
47374         Roo.get(e.target).toggleClass('x-menu-item-checked');
47375         this.refreshValue();
47376         //if(this.el.dom.checked != this.checked){
47377         //    this.setValue(this.el.dom.checked);
47378        // }
47379     },
47380     
47381     // private
47382     refreshValue : function()
47383     {
47384         var val = '';
47385         this.viewEl.select('img',true).each(function(e,i,n)  {
47386             val += e.is(".x-menu-item-checked") ? String(n) : '';
47387         });
47388         this.setValue(val, true);
47389     },
47390
47391     /**
47392      * Sets the checked state of the checkbox.
47393      * On is always based on a string comparison between inputValue and the param.
47394      * @param {Boolean/String} value - the value to set 
47395      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47396      */
47397     setValue : function(v,suppressEvent){
47398         if (!this.el.dom) {
47399             return;
47400         }
47401         var old = this.el.dom.value ;
47402         this.el.dom.value = v;
47403         if (suppressEvent) {
47404             return ;
47405         }
47406          
47407         // update display..
47408         this.viewEl.select('img',true).each(function(e,i,n)  {
47409             
47410             var on = e.is(".x-menu-item-checked");
47411             var newv = v.indexOf(String(n)) > -1;
47412             if (on != newv) {
47413                 e.toggleClass('x-menu-item-checked');
47414             }
47415             
47416         });
47417         
47418         
47419         this.fireEvent('change', this, v, old);
47420         
47421         
47422     },
47423    
47424     // handle setting of hidden value by some other method!!?!?
47425     setFromHidden: function()
47426     {
47427         if(!this.el){
47428             return;
47429         }
47430         //console.log("SET FROM HIDDEN");
47431         //alert('setFrom hidden');
47432         this.setValue(this.el.dom.value);
47433     },
47434     
47435     onDestroy : function()
47436     {
47437         if(this.viewEl){
47438             Roo.get(this.viewEl).remove();
47439         }
47440          
47441         Roo.form.DayPicker.superclass.onDestroy.call(this);
47442     }
47443
47444 });/*
47445  * RooJS Library 1.1.1
47446  * Copyright(c) 2008-2011  Alan Knowles
47447  *
47448  * License - LGPL
47449  */
47450  
47451
47452 /**
47453  * @class Roo.form.ComboCheck
47454  * @extends Roo.form.ComboBox
47455  * A combobox for multiple select items.
47456  *
47457  * FIXME - could do with a reset button..
47458  * 
47459  * @constructor
47460  * Create a new ComboCheck
47461  * @param {Object} config Configuration options
47462  */
47463 Roo.form.ComboCheck = function(config){
47464     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47465     // should verify some data...
47466     // like
47467     // hiddenName = required..
47468     // displayField = required
47469     // valudField == required
47470     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47471     var _t = this;
47472     Roo.each(req, function(e) {
47473         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47474             throw "Roo.form.ComboCheck : missing value for: " + e;
47475         }
47476     });
47477     
47478     
47479 };
47480
47481 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47482      
47483      
47484     editable : false,
47485      
47486     selectedClass: 'x-menu-item-checked', 
47487     
47488     // private
47489     onRender : function(ct, position){
47490         var _t = this;
47491         
47492         
47493         
47494         if(!this.tpl){
47495             var cls = 'x-combo-list';
47496
47497             
47498             this.tpl =  new Roo.Template({
47499                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47500                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47501                    '<span>{' + this.displayField + '}</span>' +
47502                     '</div>' 
47503                 
47504             });
47505         }
47506  
47507         
47508         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47509         this.view.singleSelect = false;
47510         this.view.multiSelect = true;
47511         this.view.toggleSelect = true;
47512         this.pageTb.add(new Roo.Toolbar.Fill(), {
47513             
47514             text: 'Done',
47515             handler: function()
47516             {
47517                 _t.collapse();
47518             }
47519         });
47520     },
47521     
47522     onViewOver : function(e, t){
47523         // do nothing...
47524         return;
47525         
47526     },
47527     
47528     onViewClick : function(doFocus,index){
47529         return;
47530         
47531     },
47532     select: function () {
47533         //Roo.log("SELECT CALLED");
47534     },
47535      
47536     selectByValue : function(xv, scrollIntoView){
47537         var ar = this.getValueArray();
47538         var sels = [];
47539         
47540         Roo.each(ar, function(v) {
47541             if(v === undefined || v === null){
47542                 return;
47543             }
47544             var r = this.findRecord(this.valueField, v);
47545             if(r){
47546                 sels.push(this.store.indexOf(r))
47547                 
47548             }
47549         },this);
47550         this.view.select(sels);
47551         return false;
47552     },
47553     
47554     
47555     
47556     onSelect : function(record, index){
47557        // Roo.log("onselect Called");
47558        // this is only called by the clear button now..
47559         this.view.clearSelections();
47560         this.setValue('[]');
47561         if (this.value != this.valueBefore) {
47562             this.fireEvent('change', this, this.value, this.valueBefore);
47563             this.valueBefore = this.value;
47564         }
47565     },
47566     getValueArray : function()
47567     {
47568         var ar = [] ;
47569         
47570         try {
47571             //Roo.log(this.value);
47572             if (typeof(this.value) == 'undefined') {
47573                 return [];
47574             }
47575             var ar = Roo.decode(this.value);
47576             return  ar instanceof Array ? ar : []; //?? valid?
47577             
47578         } catch(e) {
47579             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47580             return [];
47581         }
47582          
47583     },
47584     expand : function ()
47585     {
47586         
47587         Roo.form.ComboCheck.superclass.expand.call(this);
47588         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47589         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47590         
47591
47592     },
47593     
47594     collapse : function(){
47595         Roo.form.ComboCheck.superclass.collapse.call(this);
47596         var sl = this.view.getSelectedIndexes();
47597         var st = this.store;
47598         var nv = [];
47599         var tv = [];
47600         var r;
47601         Roo.each(sl, function(i) {
47602             r = st.getAt(i);
47603             nv.push(r.get(this.valueField));
47604         },this);
47605         this.setValue(Roo.encode(nv));
47606         if (this.value != this.valueBefore) {
47607
47608             this.fireEvent('change', this, this.value, this.valueBefore);
47609             this.valueBefore = this.value;
47610         }
47611         
47612     },
47613     
47614     setValue : function(v){
47615         // Roo.log(v);
47616         this.value = v;
47617         
47618         var vals = this.getValueArray();
47619         var tv = [];
47620         Roo.each(vals, function(k) {
47621             var r = this.findRecord(this.valueField, k);
47622             if(r){
47623                 tv.push(r.data[this.displayField]);
47624             }else if(this.valueNotFoundText !== undefined){
47625                 tv.push( this.valueNotFoundText );
47626             }
47627         },this);
47628        // Roo.log(tv);
47629         
47630         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47631         this.hiddenField.value = v;
47632         this.value = v;
47633     }
47634     
47635 });/*
47636  * Based on:
47637  * Ext JS Library 1.1.1
47638  * Copyright(c) 2006-2007, Ext JS, LLC.
47639  *
47640  * Originally Released Under LGPL - original licence link has changed is not relivant.
47641  *
47642  * Fork - LGPL
47643  * <script type="text/javascript">
47644  */
47645  
47646 /**
47647  * @class Roo.form.Signature
47648  * @extends Roo.form.Field
47649  * Signature field.  
47650  * @constructor
47651  * 
47652  * @param {Object} config Configuration options
47653  */
47654
47655 Roo.form.Signature = function(config){
47656     Roo.form.Signature.superclass.constructor.call(this, config);
47657     
47658     this.addEvents({// not in used??
47659          /**
47660          * @event confirm
47661          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47662              * @param {Roo.form.Signature} combo This combo box
47663              */
47664         'confirm' : true,
47665         /**
47666          * @event reset
47667          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47668              * @param {Roo.form.ComboBox} combo This combo box
47669              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47670              */
47671         'reset' : true
47672     });
47673 };
47674
47675 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47676     /**
47677      * @cfg {Object} labels Label to use when rendering a form.
47678      * defaults to 
47679      * labels : { 
47680      *      clear : "Clear",
47681      *      confirm : "Confirm"
47682      *  }
47683      */
47684     labels : { 
47685         clear : "Clear",
47686         confirm : "Confirm"
47687     },
47688     /**
47689      * @cfg {Number} width The signature panel width (defaults to 300)
47690      */
47691     width: 300,
47692     /**
47693      * @cfg {Number} height The signature panel height (defaults to 100)
47694      */
47695     height : 100,
47696     /**
47697      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47698      */
47699     allowBlank : false,
47700     
47701     //private
47702     // {Object} signPanel The signature SVG panel element (defaults to {})
47703     signPanel : {},
47704     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47705     isMouseDown : false,
47706     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47707     isConfirmed : false,
47708     // {String} signatureTmp SVG mapping string (defaults to empty string)
47709     signatureTmp : '',
47710     
47711     
47712     defaultAutoCreate : { // modified by initCompnoent..
47713         tag: "input",
47714         type:"hidden"
47715     },
47716
47717     // private
47718     onRender : function(ct, position){
47719         
47720         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47721         
47722         this.wrap = this.el.wrap({
47723             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47724         });
47725         
47726         this.createToolbar(this);
47727         this.signPanel = this.wrap.createChild({
47728                 tag: 'div',
47729                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47730             }, this.el
47731         );
47732             
47733         this.svgID = Roo.id();
47734         this.svgEl = this.signPanel.createChild({
47735               xmlns : 'http://www.w3.org/2000/svg',
47736               tag : 'svg',
47737               id : this.svgID + "-svg",
47738               width: this.width,
47739               height: this.height,
47740               viewBox: '0 0 '+this.width+' '+this.height,
47741               cn : [
47742                 {
47743                     tag: "rect",
47744                     id: this.svgID + "-svg-r",
47745                     width: this.width,
47746                     height: this.height,
47747                     fill: "#ffa"
47748                 },
47749                 {
47750                     tag: "line",
47751                     id: this.svgID + "-svg-l",
47752                     x1: "0", // start
47753                     y1: (this.height*0.8), // start set the line in 80% of height
47754                     x2: this.width, // end
47755                     y2: (this.height*0.8), // end set the line in 80% of height
47756                     'stroke': "#666",
47757                     'stroke-width': "1",
47758                     'stroke-dasharray': "3",
47759                     'shape-rendering': "crispEdges",
47760                     'pointer-events': "none"
47761                 },
47762                 {
47763                     tag: "path",
47764                     id: this.svgID + "-svg-p",
47765                     'stroke': "navy",
47766                     'stroke-width': "3",
47767                     'fill': "none",
47768                     'pointer-events': 'none'
47769                 }
47770               ]
47771         });
47772         this.createSVG();
47773         this.svgBox = this.svgEl.dom.getScreenCTM();
47774     },
47775     createSVG : function(){ 
47776         var svg = this.signPanel;
47777         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47778         var t = this;
47779
47780         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47781         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47782         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47783         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47784         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47785         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47786         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47787         
47788     },
47789     isTouchEvent : function(e){
47790         return e.type.match(/^touch/);
47791     },
47792     getCoords : function (e) {
47793         var pt    = this.svgEl.dom.createSVGPoint();
47794         pt.x = e.clientX; 
47795         pt.y = e.clientY;
47796         if (this.isTouchEvent(e)) {
47797             pt.x =  e.targetTouches[0].clientX 
47798             pt.y = e.targetTouches[0].clientY;
47799         }
47800         var a = this.svgEl.dom.getScreenCTM();
47801         var b = a.inverse();
47802         var mx = pt.matrixTransform(b);
47803         return mx.x + ',' + mx.y;
47804     },
47805     //mouse event headler 
47806     down : function (e) {
47807         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47808         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47809         
47810         this.isMouseDown = true;
47811         
47812         e.preventDefault();
47813     },
47814     move : function (e) {
47815         if (this.isMouseDown) {
47816             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47817             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47818         }
47819         
47820         e.preventDefault();
47821     },
47822     up : function (e) {
47823         this.isMouseDown = false;
47824         var sp = this.signatureTmp.split(' ');
47825         
47826         if(sp.length > 1){
47827             if(!sp[sp.length-2].match(/^L/)){
47828                 sp.pop();
47829                 sp.pop();
47830                 sp.push("");
47831                 this.signatureTmp = sp.join(" ");
47832             }
47833         }
47834         if(this.getValue() != this.signatureTmp){
47835             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47836             this.isConfirmed = false;
47837         }
47838         e.preventDefault();
47839     },
47840     
47841     /**
47842      * Protected method that will not generally be called directly. It
47843      * is called when the editor creates its toolbar. Override this method if you need to
47844      * add custom toolbar buttons.
47845      * @param {HtmlEditor} editor
47846      */
47847     createToolbar : function(editor){
47848          function btn(id, toggle, handler){
47849             var xid = fid + '-'+ id ;
47850             return {
47851                 id : xid,
47852                 cmd : id,
47853                 cls : 'x-btn-icon x-edit-'+id,
47854                 enableToggle:toggle !== false,
47855                 scope: editor, // was editor...
47856                 handler:handler||editor.relayBtnCmd,
47857                 clickEvent:'mousedown',
47858                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47859                 tabIndex:-1
47860             };
47861         }
47862         
47863         
47864         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47865         this.tb = tb;
47866         this.tb.add(
47867            {
47868                 cls : ' x-signature-btn x-signature-'+id,
47869                 scope: editor, // was editor...
47870                 handler: this.reset,
47871                 clickEvent:'mousedown',
47872                 text: this.labels.clear
47873             },
47874             {
47875                  xtype : 'Fill',
47876                  xns: Roo.Toolbar
47877             }, 
47878             {
47879                 cls : '  x-signature-btn x-signature-'+id,
47880                 scope: editor, // was editor...
47881                 handler: this.confirmHandler,
47882                 clickEvent:'mousedown',
47883                 text: this.labels.confirm
47884             }
47885         );
47886     
47887     },
47888     //public
47889     /**
47890      * when user is clicked confirm then show this image.....
47891      * 
47892      * @return {String} Image Data URI
47893      */
47894     getImageDataURI : function(){
47895         var svg = this.svgEl.dom.parentNode.innerHTML;
47896         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47897         return src; 
47898     },
47899     /**
47900      * 
47901      * @return {Boolean} this.isConfirmed
47902      */
47903     getConfirmed : function(){
47904         return this.isConfirmed;
47905     },
47906     /**
47907      * 
47908      * @return {Number} this.width
47909      */
47910     getWidth : function(){
47911         return this.width;
47912     },
47913     /**
47914      * 
47915      * @return {Number} this.height
47916      */
47917     getHeight : function(){
47918         return this.height;
47919     },
47920     // private
47921     getSignature : function(){
47922         return this.signatureTmp;
47923     },
47924     // private
47925     reset : function(){
47926         this.signatureTmp = '';
47927         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47928         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47929         this.isConfirmed = false;
47930         Roo.form.Signature.superclass.reset.call(this);
47931     },
47932     setSignature : function(s){
47933         this.signatureTmp = s;
47934         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47935         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47936         this.setValue(s);
47937         this.isConfirmed = false;
47938         Roo.form.Signature.superclass.reset.call(this);
47939     }, 
47940     test : function(){
47941 //        Roo.log(this.signPanel.dom.contentWindow.up())
47942     },
47943     //private
47944     setConfirmed : function(){
47945         
47946         
47947         
47948 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47949     },
47950     // private
47951     confirmHandler : function(){
47952         if(!this.getSignature()){
47953             return;
47954         }
47955         
47956         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47957         this.setValue(this.getSignature());
47958         this.isConfirmed = true;
47959         
47960         this.fireEvent('confirm', this);
47961     },
47962     // private
47963     // Subclasses should provide the validation implementation by overriding this
47964     validateValue : function(value){
47965         if(this.allowBlank){
47966             return true;
47967         }
47968         
47969         if(this.isConfirmed){
47970             return true;
47971         }
47972         return false;
47973     }
47974 });/*
47975  * Based on:
47976  * Ext JS Library 1.1.1
47977  * Copyright(c) 2006-2007, Ext JS, LLC.
47978  *
47979  * Originally Released Under LGPL - original licence link has changed is not relivant.
47980  *
47981  * Fork - LGPL
47982  * <script type="text/javascript">
47983  */
47984  
47985
47986 /**
47987  * @class Roo.form.ComboBox
47988  * @extends Roo.form.TriggerField
47989  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47990  * @constructor
47991  * Create a new ComboBox.
47992  * @param {Object} config Configuration options
47993  */
47994 Roo.form.Select = function(config){
47995     Roo.form.Select.superclass.constructor.call(this, config);
47996      
47997 };
47998
47999 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48000     /**
48001      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48002      */
48003     /**
48004      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48005      * rendering into an Roo.Editor, defaults to false)
48006      */
48007     /**
48008      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48009      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48010      */
48011     /**
48012      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48013      */
48014     /**
48015      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48016      * the dropdown list (defaults to undefined, with no header element)
48017      */
48018
48019      /**
48020      * @cfg {String/Roo.Template} tpl The template to use to render the output
48021      */
48022      
48023     // private
48024     defaultAutoCreate : {tag: "select"  },
48025     /**
48026      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48027      */
48028     listWidth: undefined,
48029     /**
48030      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48031      * mode = 'remote' or 'text' if mode = 'local')
48032      */
48033     displayField: undefined,
48034     /**
48035      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48036      * mode = 'remote' or 'value' if mode = 'local'). 
48037      * Note: use of a valueField requires the user make a selection
48038      * in order for a value to be mapped.
48039      */
48040     valueField: undefined,
48041     
48042     
48043     /**
48044      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48045      * field's data value (defaults to the underlying DOM element's name)
48046      */
48047     hiddenName: undefined,
48048     /**
48049      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48050      */
48051     listClass: '',
48052     /**
48053      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48054      */
48055     selectedClass: 'x-combo-selected',
48056     /**
48057      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48058      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48059      * which displays a downward arrow icon).
48060      */
48061     triggerClass : 'x-form-arrow-trigger',
48062     /**
48063      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48064      */
48065     shadow:'sides',
48066     /**
48067      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48068      * anchor positions (defaults to 'tl-bl')
48069      */
48070     listAlign: 'tl-bl?',
48071     /**
48072      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48073      */
48074     maxHeight: 300,
48075     /**
48076      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48077      * query specified by the allQuery config option (defaults to 'query')
48078      */
48079     triggerAction: 'query',
48080     /**
48081      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48082      * (defaults to 4, does not apply if editable = false)
48083      */
48084     minChars : 4,
48085     /**
48086      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48087      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48088      */
48089     typeAhead: false,
48090     /**
48091      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48092      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48093      */
48094     queryDelay: 500,
48095     /**
48096      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48097      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48098      */
48099     pageSize: 0,
48100     /**
48101      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48102      * when editable = true (defaults to false)
48103      */
48104     selectOnFocus:false,
48105     /**
48106      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48107      */
48108     queryParam: 'query',
48109     /**
48110      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48111      * when mode = 'remote' (defaults to 'Loading...')
48112      */
48113     loadingText: 'Loading...',
48114     /**
48115      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48116      */
48117     resizable: false,
48118     /**
48119      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48120      */
48121     handleHeight : 8,
48122     /**
48123      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48124      * traditional select (defaults to true)
48125      */
48126     editable: true,
48127     /**
48128      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48129      */
48130     allQuery: '',
48131     /**
48132      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48133      */
48134     mode: 'remote',
48135     /**
48136      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48137      * listWidth has a higher value)
48138      */
48139     minListWidth : 70,
48140     /**
48141      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48142      * allow the user to set arbitrary text into the field (defaults to false)
48143      */
48144     forceSelection:false,
48145     /**
48146      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48147      * if typeAhead = true (defaults to 250)
48148      */
48149     typeAheadDelay : 250,
48150     /**
48151      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48152      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48153      */
48154     valueNotFoundText : undefined,
48155     
48156     /**
48157      * @cfg {String} defaultValue The value displayed after loading the store.
48158      */
48159     defaultValue: '',
48160     
48161     /**
48162      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48163      */
48164     blockFocus : false,
48165     
48166     /**
48167      * @cfg {Boolean} disableClear Disable showing of clear button.
48168      */
48169     disableClear : false,
48170     /**
48171      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48172      */
48173     alwaysQuery : false,
48174     
48175     //private
48176     addicon : false,
48177     editicon: false,
48178     
48179     // element that contains real text value.. (when hidden is used..)
48180      
48181     // private
48182     onRender : function(ct, position){
48183         Roo.form.Field.prototype.onRender.call(this, ct, position);
48184         
48185         if(this.store){
48186             this.store.on('beforeload', this.onBeforeLoad, this);
48187             this.store.on('load', this.onLoad, this);
48188             this.store.on('loadexception', this.onLoadException, this);
48189             this.store.load({});
48190         }
48191         
48192         
48193         
48194     },
48195
48196     // private
48197     initEvents : function(){
48198         //Roo.form.ComboBox.superclass.initEvents.call(this);
48199  
48200     },
48201
48202     onDestroy : function(){
48203        
48204         if(this.store){
48205             this.store.un('beforeload', this.onBeforeLoad, this);
48206             this.store.un('load', this.onLoad, this);
48207             this.store.un('loadexception', this.onLoadException, this);
48208         }
48209         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48210     },
48211
48212     // private
48213     fireKey : function(e){
48214         if(e.isNavKeyPress() && !this.list.isVisible()){
48215             this.fireEvent("specialkey", this, e);
48216         }
48217     },
48218
48219     // private
48220     onResize: function(w, h){
48221         
48222         return; 
48223     
48224         
48225     },
48226
48227     /**
48228      * Allow or prevent the user from directly editing the field text.  If false is passed,
48229      * the user will only be able to select from the items defined in the dropdown list.  This method
48230      * is the runtime equivalent of setting the 'editable' config option at config time.
48231      * @param {Boolean} value True to allow the user to directly edit the field text
48232      */
48233     setEditable : function(value){
48234          
48235     },
48236
48237     // private
48238     onBeforeLoad : function(){
48239         
48240         Roo.log("Select before load");
48241         return;
48242     
48243         this.innerList.update(this.loadingText ?
48244                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48245         //this.restrictHeight();
48246         this.selectedIndex = -1;
48247     },
48248
48249     // private
48250     onLoad : function(){
48251
48252     
48253         var dom = this.el.dom;
48254         dom.innerHTML = '';
48255          var od = dom.ownerDocument;
48256          
48257         if (this.emptyText) {
48258             var op = od.createElement('option');
48259             op.setAttribute('value', '');
48260             op.innerHTML = String.format('{0}', this.emptyText);
48261             dom.appendChild(op);
48262         }
48263         if(this.store.getCount() > 0){
48264            
48265             var vf = this.valueField;
48266             var df = this.displayField;
48267             this.store.data.each(function(r) {
48268                 // which colmsn to use... testing - cdoe / title..
48269                 var op = od.createElement('option');
48270                 op.setAttribute('value', r.data[vf]);
48271                 op.innerHTML = String.format('{0}', r.data[df]);
48272                 dom.appendChild(op);
48273             });
48274             if (typeof(this.defaultValue != 'undefined')) {
48275                 this.setValue(this.defaultValue);
48276             }
48277             
48278              
48279         }else{
48280             //this.onEmptyResults();
48281         }
48282         //this.el.focus();
48283     },
48284     // private
48285     onLoadException : function()
48286     {
48287         dom.innerHTML = '';
48288             
48289         Roo.log("Select on load exception");
48290         return;
48291     
48292         this.collapse();
48293         Roo.log(this.store.reader.jsonData);
48294         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48295             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48296         }
48297         
48298         
48299     },
48300     // private
48301     onTypeAhead : function(){
48302          
48303     },
48304
48305     // private
48306     onSelect : function(record, index){
48307         Roo.log('on select?');
48308         return;
48309         if(this.fireEvent('beforeselect', this, record, index) !== false){
48310             this.setFromData(index > -1 ? record.data : false);
48311             this.collapse();
48312             this.fireEvent('select', this, record, index);
48313         }
48314     },
48315
48316     /**
48317      * Returns the currently selected field value or empty string if no value is set.
48318      * @return {String} value The selected value
48319      */
48320     getValue : function(){
48321         var dom = this.el.dom;
48322         this.value = dom.options[dom.selectedIndex].value;
48323         return this.value;
48324         
48325     },
48326
48327     /**
48328      * Clears any text/value currently set in the field
48329      */
48330     clearValue : function(){
48331         this.value = '';
48332         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48333         
48334     },
48335
48336     /**
48337      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48338      * will be displayed in the field.  If the value does not match the data value of an existing item,
48339      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48340      * Otherwise the field will be blank (although the value will still be set).
48341      * @param {String} value The value to match
48342      */
48343     setValue : function(v){
48344         var d = this.el.dom;
48345         for (var i =0; i < d.options.length;i++) {
48346             if (v == d.options[i].value) {
48347                 d.selectedIndex = i;
48348                 this.value = v;
48349                 return;
48350             }
48351         }
48352         this.clearValue();
48353     },
48354     /**
48355      * @property {Object} the last set data for the element
48356      */
48357     
48358     lastData : false,
48359     /**
48360      * Sets the value of the field based on a object which is related to the record format for the store.
48361      * @param {Object} value the value to set as. or false on reset?
48362      */
48363     setFromData : function(o){
48364         Roo.log('setfrom data?');
48365          
48366         
48367         
48368     },
48369     // private
48370     reset : function(){
48371         this.clearValue();
48372     },
48373     // private
48374     findRecord : function(prop, value){
48375         
48376         return false;
48377     
48378         var record;
48379         if(this.store.getCount() > 0){
48380             this.store.each(function(r){
48381                 if(r.data[prop] == value){
48382                     record = r;
48383                     return false;
48384                 }
48385                 return true;
48386             });
48387         }
48388         return record;
48389     },
48390     
48391     getName: function()
48392     {
48393         // returns hidden if it's set..
48394         if (!this.rendered) {return ''};
48395         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48396         
48397     },
48398      
48399
48400     
48401
48402     // private
48403     onEmptyResults : function(){
48404         Roo.log('empty results');
48405         //this.collapse();
48406     },
48407
48408     /**
48409      * Returns true if the dropdown list is expanded, else false.
48410      */
48411     isExpanded : function(){
48412         return false;
48413     },
48414
48415     /**
48416      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48417      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48418      * @param {String} value The data value of the item to select
48419      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48420      * selected item if it is not currently in view (defaults to true)
48421      * @return {Boolean} True if the value matched an item in the list, else false
48422      */
48423     selectByValue : function(v, scrollIntoView){
48424         Roo.log('select By Value');
48425         return false;
48426     
48427         if(v !== undefined && v !== null){
48428             var r = this.findRecord(this.valueField || this.displayField, v);
48429             if(r){
48430                 this.select(this.store.indexOf(r), scrollIntoView);
48431                 return true;
48432             }
48433         }
48434         return false;
48435     },
48436
48437     /**
48438      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48439      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48440      * @param {Number} index The zero-based index of the list item to select
48441      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48442      * selected item if it is not currently in view (defaults to true)
48443      */
48444     select : function(index, scrollIntoView){
48445         Roo.log('select ');
48446         return  ;
48447         
48448         this.selectedIndex = index;
48449         this.view.select(index);
48450         if(scrollIntoView !== false){
48451             var el = this.view.getNode(index);
48452             if(el){
48453                 this.innerList.scrollChildIntoView(el, false);
48454             }
48455         }
48456     },
48457
48458       
48459
48460     // private
48461     validateBlur : function(){
48462         
48463         return;
48464         
48465     },
48466
48467     // private
48468     initQuery : function(){
48469         this.doQuery(this.getRawValue());
48470     },
48471
48472     // private
48473     doForce : function(){
48474         if(this.el.dom.value.length > 0){
48475             this.el.dom.value =
48476                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48477              
48478         }
48479     },
48480
48481     /**
48482      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48483      * query allowing the query action to be canceled if needed.
48484      * @param {String} query The SQL query to execute
48485      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48486      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48487      * saved in the current store (defaults to false)
48488      */
48489     doQuery : function(q, forceAll){
48490         
48491         Roo.log('doQuery?');
48492         if(q === undefined || q === null){
48493             q = '';
48494         }
48495         var qe = {
48496             query: q,
48497             forceAll: forceAll,
48498             combo: this,
48499             cancel:false
48500         };
48501         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48502             return false;
48503         }
48504         q = qe.query;
48505         forceAll = qe.forceAll;
48506         if(forceAll === true || (q.length >= this.minChars)){
48507             if(this.lastQuery != q || this.alwaysQuery){
48508                 this.lastQuery = q;
48509                 if(this.mode == 'local'){
48510                     this.selectedIndex = -1;
48511                     if(forceAll){
48512                         this.store.clearFilter();
48513                     }else{
48514                         this.store.filter(this.displayField, q);
48515                     }
48516                     this.onLoad();
48517                 }else{
48518                     this.store.baseParams[this.queryParam] = q;
48519                     this.store.load({
48520                         params: this.getParams(q)
48521                     });
48522                     this.expand();
48523                 }
48524             }else{
48525                 this.selectedIndex = -1;
48526                 this.onLoad();   
48527             }
48528         }
48529     },
48530
48531     // private
48532     getParams : function(q){
48533         var p = {};
48534         //p[this.queryParam] = q;
48535         if(this.pageSize){
48536             p.start = 0;
48537             p.limit = this.pageSize;
48538         }
48539         return p;
48540     },
48541
48542     /**
48543      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48544      */
48545     collapse : function(){
48546         
48547     },
48548
48549     // private
48550     collapseIf : function(e){
48551         
48552     },
48553
48554     /**
48555      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48556      */
48557     expand : function(){
48558         
48559     } ,
48560
48561     // private
48562      
48563
48564     /** 
48565     * @cfg {Boolean} grow 
48566     * @hide 
48567     */
48568     /** 
48569     * @cfg {Number} growMin 
48570     * @hide 
48571     */
48572     /** 
48573     * @cfg {Number} growMax 
48574     * @hide 
48575     */
48576     /**
48577      * @hide
48578      * @method autoSize
48579      */
48580     
48581     setWidth : function()
48582     {
48583         
48584     },
48585     getResizeEl : function(){
48586         return this.el;
48587     }
48588 });//<script type="text/javasscript">
48589  
48590
48591 /**
48592  * @class Roo.DDView
48593  * A DnD enabled version of Roo.View.
48594  * @param {Element/String} container The Element in which to create the View.
48595  * @param {String} tpl The template string used to create the markup for each element of the View
48596  * @param {Object} config The configuration properties. These include all the config options of
48597  * {@link Roo.View} plus some specific to this class.<br>
48598  * <p>
48599  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48600  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48601  * <p>
48602  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48603 .x-view-drag-insert-above {
48604         border-top:1px dotted #3366cc;
48605 }
48606 .x-view-drag-insert-below {
48607         border-bottom:1px dotted #3366cc;
48608 }
48609 </code></pre>
48610  * 
48611  */
48612  
48613 Roo.DDView = function(container, tpl, config) {
48614     Roo.DDView.superclass.constructor.apply(this, arguments);
48615     this.getEl().setStyle("outline", "0px none");
48616     this.getEl().unselectable();
48617     if (this.dragGroup) {
48618                 this.setDraggable(this.dragGroup.split(","));
48619     }
48620     if (this.dropGroup) {
48621                 this.setDroppable(this.dropGroup.split(","));
48622     }
48623     if (this.deletable) {
48624         this.setDeletable();
48625     }
48626     this.isDirtyFlag = false;
48627         this.addEvents({
48628                 "drop" : true
48629         });
48630 };
48631
48632 Roo.extend(Roo.DDView, Roo.View, {
48633 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48634 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48635 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48636 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48637
48638         isFormField: true,
48639
48640         reset: Roo.emptyFn,
48641         
48642         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48643
48644         validate: function() {
48645                 return true;
48646         },
48647         
48648         destroy: function() {
48649                 this.purgeListeners();
48650                 this.getEl.removeAllListeners();
48651                 this.getEl().remove();
48652                 if (this.dragZone) {
48653                         if (this.dragZone.destroy) {
48654                                 this.dragZone.destroy();
48655                         }
48656                 }
48657                 if (this.dropZone) {
48658                         if (this.dropZone.destroy) {
48659                                 this.dropZone.destroy();
48660                         }
48661                 }
48662         },
48663
48664 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48665         getName: function() {
48666                 return this.name;
48667         },
48668
48669 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48670         setValue: function(v) {
48671                 if (!this.store) {
48672                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48673                 }
48674                 var data = {};
48675                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48676                 this.store.proxy = new Roo.data.MemoryProxy(data);
48677                 this.store.load();
48678         },
48679
48680 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48681         getValue: function() {
48682                 var result = '(';
48683                 this.store.each(function(rec) {
48684                         result += rec.id + ',';
48685                 });
48686                 return result.substr(0, result.length - 1) + ')';
48687         },
48688         
48689         getIds: function() {
48690                 var i = 0, result = new Array(this.store.getCount());
48691                 this.store.each(function(rec) {
48692                         result[i++] = rec.id;
48693                 });
48694                 return result;
48695         },
48696         
48697         isDirty: function() {
48698                 return this.isDirtyFlag;
48699         },
48700
48701 /**
48702  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48703  *      whole Element becomes the target, and this causes the drop gesture to append.
48704  */
48705     getTargetFromEvent : function(e) {
48706                 var target = e.getTarget();
48707                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48708                 target = target.parentNode;
48709                 }
48710                 if (!target) {
48711                         target = this.el.dom.lastChild || this.el.dom;
48712                 }
48713                 return target;
48714     },
48715
48716 /**
48717  *      Create the drag data which consists of an object which has the property "ddel" as
48718  *      the drag proxy element. 
48719  */
48720     getDragData : function(e) {
48721         var target = this.findItemFromChild(e.getTarget());
48722                 if(target) {
48723                         this.handleSelection(e);
48724                         var selNodes = this.getSelectedNodes();
48725             var dragData = {
48726                 source: this,
48727                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48728                 nodes: selNodes,
48729                 records: []
48730                         };
48731                         var selectedIndices = this.getSelectedIndexes();
48732                         for (var i = 0; i < selectedIndices.length; i++) {
48733                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48734                         }
48735                         if (selNodes.length == 1) {
48736                                 dragData.ddel = target.cloneNode(true); // the div element
48737                         } else {
48738                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48739                                 div.className = 'multi-proxy';
48740                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48741                                         div.appendChild(selNodes[i].cloneNode(true));
48742                                 }
48743                                 dragData.ddel = div;
48744                         }
48745             //console.log(dragData)
48746             //console.log(dragData.ddel.innerHTML)
48747                         return dragData;
48748                 }
48749         //console.log('nodragData')
48750                 return false;
48751     },
48752     
48753 /**     Specify to which ddGroup items in this DDView may be dragged. */
48754     setDraggable: function(ddGroup) {
48755         if (ddGroup instanceof Array) {
48756                 Roo.each(ddGroup, this.setDraggable, this);
48757                 return;
48758         }
48759         if (this.dragZone) {
48760                 this.dragZone.addToGroup(ddGroup);
48761         } else {
48762                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48763                                 containerScroll: true,
48764                                 ddGroup: ddGroup 
48765
48766                         });
48767 //                      Draggability implies selection. DragZone's mousedown selects the element.
48768                         if (!this.multiSelect) { this.singleSelect = true; }
48769
48770 //                      Wire the DragZone's handlers up to methods in *this*
48771                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48772                 }
48773     },
48774
48775 /**     Specify from which ddGroup this DDView accepts drops. */
48776     setDroppable: function(ddGroup) {
48777         if (ddGroup instanceof Array) {
48778                 Roo.each(ddGroup, this.setDroppable, this);
48779                 return;
48780         }
48781         if (this.dropZone) {
48782                 this.dropZone.addToGroup(ddGroup);
48783         } else {
48784                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48785                                 containerScroll: true,
48786                                 ddGroup: ddGroup
48787                         });
48788
48789 //                      Wire the DropZone's handlers up to methods in *this*
48790                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48791                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48792                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48793                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48794                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48795                 }
48796     },
48797
48798 /**     Decide whether to drop above or below a View node. */
48799     getDropPoint : function(e, n, dd){
48800         if (n == this.el.dom) { return "above"; }
48801                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48802                 var c = t + (b - t) / 2;
48803                 var y = Roo.lib.Event.getPageY(e);
48804                 if(y <= c) {
48805                         return "above";
48806                 }else{
48807                         return "below";
48808                 }
48809     },
48810
48811     onNodeEnter : function(n, dd, e, data){
48812                 return false;
48813     },
48814     
48815     onNodeOver : function(n, dd, e, data){
48816                 var pt = this.getDropPoint(e, n, dd);
48817                 // set the insert point style on the target node
48818                 var dragElClass = this.dropNotAllowed;
48819                 if (pt) {
48820                         var targetElClass;
48821                         if (pt == "above"){
48822                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48823                                 targetElClass = "x-view-drag-insert-above";
48824                         } else {
48825                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48826                                 targetElClass = "x-view-drag-insert-below";
48827                         }
48828                         if (this.lastInsertClass != targetElClass){
48829                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48830                                 this.lastInsertClass = targetElClass;
48831                         }
48832                 }
48833                 return dragElClass;
48834         },
48835
48836     onNodeOut : function(n, dd, e, data){
48837                 this.removeDropIndicators(n);
48838     },
48839
48840     onNodeDrop : function(n, dd, e, data){
48841         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48842                 return false;
48843         }
48844         var pt = this.getDropPoint(e, n, dd);
48845                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48846                 if (pt == "below") { insertAt++; }
48847                 for (var i = 0; i < data.records.length; i++) {
48848                         var r = data.records[i];
48849                         var dup = this.store.getById(r.id);
48850                         if (dup && (dd != this.dragZone)) {
48851                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48852                         } else {
48853                                 if (data.copy) {
48854                                         this.store.insert(insertAt++, r.copy());
48855                                 } else {
48856                                         data.source.isDirtyFlag = true;
48857                                         r.store.remove(r);
48858                                         this.store.insert(insertAt++, r);
48859                                 }
48860                                 this.isDirtyFlag = true;
48861                         }
48862                 }
48863                 this.dragZone.cachedTarget = null;
48864                 return true;
48865     },
48866
48867     removeDropIndicators : function(n){
48868                 if(n){
48869                         Roo.fly(n).removeClass([
48870                                 "x-view-drag-insert-above",
48871                                 "x-view-drag-insert-below"]);
48872                         this.lastInsertClass = "_noclass";
48873                 }
48874     },
48875
48876 /**
48877  *      Utility method. Add a delete option to the DDView's context menu.
48878  *      @param {String} imageUrl The URL of the "delete" icon image.
48879  */
48880         setDeletable: function(imageUrl) {
48881                 if (!this.singleSelect && !this.multiSelect) {
48882                         this.singleSelect = true;
48883                 }
48884                 var c = this.getContextMenu();
48885                 this.contextMenu.on("itemclick", function(item) {
48886                         switch (item.id) {
48887                                 case "delete":
48888                                         this.remove(this.getSelectedIndexes());
48889                                         break;
48890                         }
48891                 }, this);
48892                 this.contextMenu.add({
48893                         icon: imageUrl,
48894                         id: "delete",
48895                         text: 'Delete'
48896                 });
48897         },
48898         
48899 /**     Return the context menu for this DDView. */
48900         getContextMenu: function() {
48901                 if (!this.contextMenu) {
48902 //                      Create the View's context menu
48903                         this.contextMenu = new Roo.menu.Menu({
48904                                 id: this.id + "-contextmenu"
48905                         });
48906                         this.el.on("contextmenu", this.showContextMenu, this);
48907                 }
48908                 return this.contextMenu;
48909         },
48910         
48911         disableContextMenu: function() {
48912                 if (this.contextMenu) {
48913                         this.el.un("contextmenu", this.showContextMenu, this);
48914                 }
48915         },
48916
48917         showContextMenu: function(e, item) {
48918         item = this.findItemFromChild(e.getTarget());
48919                 if (item) {
48920                         e.stopEvent();
48921                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48922                         this.contextMenu.showAt(e.getXY());
48923             }
48924     },
48925
48926 /**
48927  *      Remove {@link Roo.data.Record}s at the specified indices.
48928  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48929  */
48930     remove: function(selectedIndices) {
48931                 selectedIndices = [].concat(selectedIndices);
48932                 for (var i = 0; i < selectedIndices.length; i++) {
48933                         var rec = this.store.getAt(selectedIndices[i]);
48934                         this.store.remove(rec);
48935                 }
48936     },
48937
48938 /**
48939  *      Double click fires the event, but also, if this is draggable, and there is only one other
48940  *      related DropZone, it transfers the selected node.
48941  */
48942     onDblClick : function(e){
48943         var item = this.findItemFromChild(e.getTarget());
48944         if(item){
48945             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48946                 return false;
48947             }
48948             if (this.dragGroup) {
48949                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48950                     while (targets.indexOf(this.dropZone) > -1) {
48951                             targets.remove(this.dropZone);
48952                                 }
48953                     if (targets.length == 1) {
48954                                         this.dragZone.cachedTarget = null;
48955                         var el = Roo.get(targets[0].getEl());
48956                         var box = el.getBox(true);
48957                         targets[0].onNodeDrop(el.dom, {
48958                                 target: el.dom,
48959                                 xy: [box.x, box.y + box.height - 1]
48960                         }, null, this.getDragData(e));
48961                     }
48962                 }
48963         }
48964     },
48965     
48966     handleSelection: function(e) {
48967                 this.dragZone.cachedTarget = null;
48968         var item = this.findItemFromChild(e.getTarget());
48969         if (!item) {
48970                 this.clearSelections(true);
48971                 return;
48972         }
48973                 if (item && (this.multiSelect || this.singleSelect)){
48974                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48975                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48976                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48977                                 this.unselect(item);
48978                         } else {
48979                                 this.select(item, this.multiSelect && e.ctrlKey);
48980                                 this.lastSelection = item;
48981                         }
48982                 }
48983     },
48984
48985     onItemClick : function(item, index, e){
48986                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48987                         return false;
48988                 }
48989                 return true;
48990     },
48991
48992     unselect : function(nodeInfo, suppressEvent){
48993                 var node = this.getNode(nodeInfo);
48994                 if(node && this.isSelected(node)){
48995                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48996                                 Roo.fly(node).removeClass(this.selectedClass);
48997                                 this.selections.remove(node);
48998                                 if(!suppressEvent){
48999                                         this.fireEvent("selectionchange", this, this.selections);
49000                                 }
49001                         }
49002                 }
49003     }
49004 });
49005 /*
49006  * Based on:
49007  * Ext JS Library 1.1.1
49008  * Copyright(c) 2006-2007, Ext JS, LLC.
49009  *
49010  * Originally Released Under LGPL - original licence link has changed is not relivant.
49011  *
49012  * Fork - LGPL
49013  * <script type="text/javascript">
49014  */
49015  
49016 /**
49017  * @class Roo.LayoutManager
49018  * @extends Roo.util.Observable
49019  * Base class for layout managers.
49020  */
49021 Roo.LayoutManager = function(container, config){
49022     Roo.LayoutManager.superclass.constructor.call(this);
49023     this.el = Roo.get(container);
49024     // ie scrollbar fix
49025     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49026         document.body.scroll = "no";
49027     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49028         this.el.position('relative');
49029     }
49030     this.id = this.el.id;
49031     this.el.addClass("x-layout-container");
49032     /** false to disable window resize monitoring @type Boolean */
49033     this.monitorWindowResize = true;
49034     this.regions = {};
49035     this.addEvents({
49036         /**
49037          * @event layout
49038          * Fires when a layout is performed. 
49039          * @param {Roo.LayoutManager} this
49040          */
49041         "layout" : true,
49042         /**
49043          * @event regionresized
49044          * Fires when the user resizes a region. 
49045          * @param {Roo.LayoutRegion} region The resized region
49046          * @param {Number} newSize The new size (width for east/west, height for north/south)
49047          */
49048         "regionresized" : true,
49049         /**
49050          * @event regioncollapsed
49051          * Fires when a region is collapsed. 
49052          * @param {Roo.LayoutRegion} region The collapsed region
49053          */
49054         "regioncollapsed" : true,
49055         /**
49056          * @event regionexpanded
49057          * Fires when a region is expanded.  
49058          * @param {Roo.LayoutRegion} region The expanded region
49059          */
49060         "regionexpanded" : true
49061     });
49062     this.updating = false;
49063     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49064 };
49065
49066 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49067     /**
49068      * Returns true if this layout is currently being updated
49069      * @return {Boolean}
49070      */
49071     isUpdating : function(){
49072         return this.updating; 
49073     },
49074     
49075     /**
49076      * Suspend the LayoutManager from doing auto-layouts while
49077      * making multiple add or remove calls
49078      */
49079     beginUpdate : function(){
49080         this.updating = true;    
49081     },
49082     
49083     /**
49084      * Restore auto-layouts and optionally disable the manager from performing a layout
49085      * @param {Boolean} noLayout true to disable a layout update 
49086      */
49087     endUpdate : function(noLayout){
49088         this.updating = false;
49089         if(!noLayout){
49090             this.layout();
49091         }    
49092     },
49093     
49094     layout: function(){
49095         
49096     },
49097     
49098     onRegionResized : function(region, newSize){
49099         this.fireEvent("regionresized", region, newSize);
49100         this.layout();
49101     },
49102     
49103     onRegionCollapsed : function(region){
49104         this.fireEvent("regioncollapsed", region);
49105     },
49106     
49107     onRegionExpanded : function(region){
49108         this.fireEvent("regionexpanded", region);
49109     },
49110         
49111     /**
49112      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49113      * performs box-model adjustments.
49114      * @return {Object} The size as an object {width: (the width), height: (the height)}
49115      */
49116     getViewSize : function(){
49117         var size;
49118         if(this.el.dom != document.body){
49119             size = this.el.getSize();
49120         }else{
49121             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49122         }
49123         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49124         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49125         return size;
49126     },
49127     
49128     /**
49129      * Returns the Element this layout is bound to.
49130      * @return {Roo.Element}
49131      */
49132     getEl : function(){
49133         return this.el;
49134     },
49135     
49136     /**
49137      * Returns the specified region.
49138      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49139      * @return {Roo.LayoutRegion}
49140      */
49141     getRegion : function(target){
49142         return this.regions[target.toLowerCase()];
49143     },
49144     
49145     onWindowResize : function(){
49146         if(this.monitorWindowResize){
49147             this.layout();
49148         }
49149     }
49150 });/*
49151  * Based on:
49152  * Ext JS Library 1.1.1
49153  * Copyright(c) 2006-2007, Ext JS, LLC.
49154  *
49155  * Originally Released Under LGPL - original licence link has changed is not relivant.
49156  *
49157  * Fork - LGPL
49158  * <script type="text/javascript">
49159  */
49160 /**
49161  * @class Roo.BorderLayout
49162  * @extends Roo.LayoutManager
49163  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49164  * please see: <br><br>
49165  * <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>
49166  * <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>
49167  * Example:
49168  <pre><code>
49169  var layout = new Roo.BorderLayout(document.body, {
49170     north: {
49171         initialSize: 25,
49172         titlebar: false
49173     },
49174     west: {
49175         split:true,
49176         initialSize: 200,
49177         minSize: 175,
49178         maxSize: 400,
49179         titlebar: true,
49180         collapsible: true
49181     },
49182     east: {
49183         split:true,
49184         initialSize: 202,
49185         minSize: 175,
49186         maxSize: 400,
49187         titlebar: true,
49188         collapsible: true
49189     },
49190     south: {
49191         split:true,
49192         initialSize: 100,
49193         minSize: 100,
49194         maxSize: 200,
49195         titlebar: true,
49196         collapsible: true
49197     },
49198     center: {
49199         titlebar: true,
49200         autoScroll:true,
49201         resizeTabs: true,
49202         minTabWidth: 50,
49203         preferredTabWidth: 150
49204     }
49205 });
49206
49207 // shorthand
49208 var CP = Roo.ContentPanel;
49209
49210 layout.beginUpdate();
49211 layout.add("north", new CP("north", "North"));
49212 layout.add("south", new CP("south", {title: "South", closable: true}));
49213 layout.add("west", new CP("west", {title: "West"}));
49214 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49215 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49216 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49217 layout.getRegion("center").showPanel("center1");
49218 layout.endUpdate();
49219 </code></pre>
49220
49221 <b>The container the layout is rendered into can be either the body element or any other element.
49222 If it is not the body element, the container needs to either be an absolute positioned element,
49223 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49224 the container size if it is not the body element.</b>
49225
49226 * @constructor
49227 * Create a new BorderLayout
49228 * @param {String/HTMLElement/Element} container The container this layout is bound to
49229 * @param {Object} config Configuration options
49230  */
49231 Roo.BorderLayout = function(container, config){
49232     config = config || {};
49233     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49234     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49235     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49236         var target = this.factory.validRegions[i];
49237         if(config[target]){
49238             this.addRegion(target, config[target]);
49239         }
49240     }
49241 };
49242
49243 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49244     /**
49245      * Creates and adds a new region if it doesn't already exist.
49246      * @param {String} target The target region key (north, south, east, west or center).
49247      * @param {Object} config The regions config object
49248      * @return {BorderLayoutRegion} The new region
49249      */
49250     addRegion : function(target, config){
49251         if(!this.regions[target]){
49252             var r = this.factory.create(target, this, config);
49253             this.bindRegion(target, r);
49254         }
49255         return this.regions[target];
49256     },
49257
49258     // private (kinda)
49259     bindRegion : function(name, r){
49260         this.regions[name] = r;
49261         r.on("visibilitychange", this.layout, this);
49262         r.on("paneladded", this.layout, this);
49263         r.on("panelremoved", this.layout, this);
49264         r.on("invalidated", this.layout, this);
49265         r.on("resized", this.onRegionResized, this);
49266         r.on("collapsed", this.onRegionCollapsed, this);
49267         r.on("expanded", this.onRegionExpanded, this);
49268     },
49269
49270     /**
49271      * Performs a layout update.
49272      */
49273     layout : function(){
49274         if(this.updating) return;
49275         var size = this.getViewSize();
49276         var w = size.width;
49277         var h = size.height;
49278         var centerW = w;
49279         var centerH = h;
49280         var centerY = 0;
49281         var centerX = 0;
49282         //var x = 0, y = 0;
49283
49284         var rs = this.regions;
49285         var north = rs["north"];
49286         var south = rs["south"]; 
49287         var west = rs["west"];
49288         var east = rs["east"];
49289         var center = rs["center"];
49290         //if(this.hideOnLayout){ // not supported anymore
49291             //c.el.setStyle("display", "none");
49292         //}
49293         if(north && north.isVisible()){
49294             var b = north.getBox();
49295             var m = north.getMargins();
49296             b.width = w - (m.left+m.right);
49297             b.x = m.left;
49298             b.y = m.top;
49299             centerY = b.height + b.y + m.bottom;
49300             centerH -= centerY;
49301             north.updateBox(this.safeBox(b));
49302         }
49303         if(south && south.isVisible()){
49304             var b = south.getBox();
49305             var m = south.getMargins();
49306             b.width = w - (m.left+m.right);
49307             b.x = m.left;
49308             var totalHeight = (b.height + m.top + m.bottom);
49309             b.y = h - totalHeight + m.top;
49310             centerH -= totalHeight;
49311             south.updateBox(this.safeBox(b));
49312         }
49313         if(west && west.isVisible()){
49314             var b = west.getBox();
49315             var m = west.getMargins();
49316             b.height = centerH - (m.top+m.bottom);
49317             b.x = m.left;
49318             b.y = centerY + m.top;
49319             var totalWidth = (b.width + m.left + m.right);
49320             centerX += totalWidth;
49321             centerW -= totalWidth;
49322             west.updateBox(this.safeBox(b));
49323         }
49324         if(east && east.isVisible()){
49325             var b = east.getBox();
49326             var m = east.getMargins();
49327             b.height = centerH - (m.top+m.bottom);
49328             var totalWidth = (b.width + m.left + m.right);
49329             b.x = w - totalWidth + m.left;
49330             b.y = centerY + m.top;
49331             centerW -= totalWidth;
49332             east.updateBox(this.safeBox(b));
49333         }
49334         if(center){
49335             var m = center.getMargins();
49336             var centerBox = {
49337                 x: centerX + m.left,
49338                 y: centerY + m.top,
49339                 width: centerW - (m.left+m.right),
49340                 height: centerH - (m.top+m.bottom)
49341             };
49342             //if(this.hideOnLayout){
49343                 //center.el.setStyle("display", "block");
49344             //}
49345             center.updateBox(this.safeBox(centerBox));
49346         }
49347         this.el.repaint();
49348         this.fireEvent("layout", this);
49349     },
49350
49351     // private
49352     safeBox : function(box){
49353         box.width = Math.max(0, box.width);
49354         box.height = Math.max(0, box.height);
49355         return box;
49356     },
49357
49358     /**
49359      * Adds a ContentPanel (or subclass) to this layout.
49360      * @param {String} target The target region key (north, south, east, west or center).
49361      * @param {Roo.ContentPanel} panel The panel to add
49362      * @return {Roo.ContentPanel} The added panel
49363      */
49364     add : function(target, panel){
49365          
49366         target = target.toLowerCase();
49367         return this.regions[target].add(panel);
49368     },
49369
49370     /**
49371      * Remove a ContentPanel (or subclass) to this layout.
49372      * @param {String} target The target region key (north, south, east, west or center).
49373      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49374      * @return {Roo.ContentPanel} The removed panel
49375      */
49376     remove : function(target, panel){
49377         target = target.toLowerCase();
49378         return this.regions[target].remove(panel);
49379     },
49380
49381     /**
49382      * Searches all regions for a panel with the specified id
49383      * @param {String} panelId
49384      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49385      */
49386     findPanel : function(panelId){
49387         var rs = this.regions;
49388         for(var target in rs){
49389             if(typeof rs[target] != "function"){
49390                 var p = rs[target].getPanel(panelId);
49391                 if(p){
49392                     return p;
49393                 }
49394             }
49395         }
49396         return null;
49397     },
49398
49399     /**
49400      * Searches all regions for a panel with the specified id and activates (shows) it.
49401      * @param {String/ContentPanel} panelId The panels id or the panel itself
49402      * @return {Roo.ContentPanel} The shown panel or null
49403      */
49404     showPanel : function(panelId) {
49405       var rs = this.regions;
49406       for(var target in rs){
49407          var r = rs[target];
49408          if(typeof r != "function"){
49409             if(r.hasPanel(panelId)){
49410                return r.showPanel(panelId);
49411             }
49412          }
49413       }
49414       return null;
49415    },
49416
49417    /**
49418      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49419      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49420      */
49421     restoreState : function(provider){
49422         if(!provider){
49423             provider = Roo.state.Manager;
49424         }
49425         var sm = new Roo.LayoutStateManager();
49426         sm.init(this, provider);
49427     },
49428
49429     /**
49430      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49431      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49432      * a valid ContentPanel config object.  Example:
49433      * <pre><code>
49434 // Create the main layout
49435 var layout = new Roo.BorderLayout('main-ct', {
49436     west: {
49437         split:true,
49438         minSize: 175,
49439         titlebar: true
49440     },
49441     center: {
49442         title:'Components'
49443     }
49444 }, 'main-ct');
49445
49446 // Create and add multiple ContentPanels at once via configs
49447 layout.batchAdd({
49448    west: {
49449        id: 'source-files',
49450        autoCreate:true,
49451        title:'Ext Source Files',
49452        autoScroll:true,
49453        fitToFrame:true
49454    },
49455    center : {
49456        el: cview,
49457        autoScroll:true,
49458        fitToFrame:true,
49459        toolbar: tb,
49460        resizeEl:'cbody'
49461    }
49462 });
49463 </code></pre>
49464      * @param {Object} regions An object containing ContentPanel configs by region name
49465      */
49466     batchAdd : function(regions){
49467         this.beginUpdate();
49468         for(var rname in regions){
49469             var lr = this.regions[rname];
49470             if(lr){
49471                 this.addTypedPanels(lr, regions[rname]);
49472             }
49473         }
49474         this.endUpdate();
49475     },
49476
49477     // private
49478     addTypedPanels : function(lr, ps){
49479         if(typeof ps == 'string'){
49480             lr.add(new Roo.ContentPanel(ps));
49481         }
49482         else if(ps instanceof Array){
49483             for(var i =0, len = ps.length; i < len; i++){
49484                 this.addTypedPanels(lr, ps[i]);
49485             }
49486         }
49487         else if(!ps.events){ // raw config?
49488             var el = ps.el;
49489             delete ps.el; // prevent conflict
49490             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49491         }
49492         else {  // panel object assumed!
49493             lr.add(ps);
49494         }
49495     },
49496     /**
49497      * Adds a xtype elements to the layout.
49498      * <pre><code>
49499
49500 layout.addxtype({
49501        xtype : 'ContentPanel',
49502        region: 'west',
49503        items: [ .... ]
49504    }
49505 );
49506
49507 layout.addxtype({
49508         xtype : 'NestedLayoutPanel',
49509         region: 'west',
49510         layout: {
49511            center: { },
49512            west: { }   
49513         },
49514         items : [ ... list of content panels or nested layout panels.. ]
49515    }
49516 );
49517 </code></pre>
49518      * @param {Object} cfg Xtype definition of item to add.
49519      */
49520     addxtype : function(cfg)
49521     {
49522         // basically accepts a pannel...
49523         // can accept a layout region..!?!?
49524         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49525         
49526         if (!cfg.xtype.match(/Panel$/)) {
49527             return false;
49528         }
49529         var ret = false;
49530         
49531         if (typeof(cfg.region) == 'undefined') {
49532             Roo.log("Failed to add Panel, region was not set");
49533             Roo.log(cfg);
49534             return false;
49535         }
49536         var region = cfg.region;
49537         delete cfg.region;
49538         
49539           
49540         var xitems = [];
49541         if (cfg.items) {
49542             xitems = cfg.items;
49543             delete cfg.items;
49544         }
49545         var nb = false;
49546         
49547         switch(cfg.xtype) 
49548         {
49549             case 'ContentPanel':  // ContentPanel (el, cfg)
49550             case 'ScrollPanel':  // ContentPanel (el, cfg)
49551             case 'ViewPanel': 
49552                 if(cfg.autoCreate) {
49553                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49554                 } else {
49555                     var el = this.el.createChild();
49556                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49557                 }
49558                 
49559                 this.add(region, ret);
49560                 break;
49561             
49562             
49563             case 'TreePanel': // our new panel!
49564                 cfg.el = this.el.createChild();
49565                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49566                 this.add(region, ret);
49567                 break;
49568             
49569             case 'NestedLayoutPanel': 
49570                 // create a new Layout (which is  a Border Layout...
49571                 var el = this.el.createChild();
49572                 var clayout = cfg.layout;
49573                 delete cfg.layout;
49574                 clayout.items   = clayout.items  || [];
49575                 // replace this exitems with the clayout ones..
49576                 xitems = clayout.items;
49577                  
49578                 
49579                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49580                     cfg.background = false;
49581                 }
49582                 var layout = new Roo.BorderLayout(el, clayout);
49583                 
49584                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49585                 //console.log('adding nested layout panel '  + cfg.toSource());
49586                 this.add(region, ret);
49587                 nb = {}; /// find first...
49588                 break;
49589                 
49590             case 'GridPanel': 
49591             
49592                 // needs grid and region
49593                 
49594                 //var el = this.getRegion(region).el.createChild();
49595                 var el = this.el.createChild();
49596                 // create the grid first...
49597                 
49598                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49599                 delete cfg.grid;
49600                 if (region == 'center' && this.active ) {
49601                     cfg.background = false;
49602                 }
49603                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49604                 
49605                 this.add(region, ret);
49606                 if (cfg.background) {
49607                     ret.on('activate', function(gp) {
49608                         if (!gp.grid.rendered) {
49609                             gp.grid.render();
49610                         }
49611                     });
49612                 } else {
49613                     grid.render();
49614                 }
49615                 break;
49616            
49617            
49618            
49619                 
49620                 
49621                 
49622             default:
49623                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49624                     
49625                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49626                     this.add(region, ret);
49627                 } else {
49628                 
49629                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49630                     return null;
49631                 }
49632                 
49633              // GridPanel (grid, cfg)
49634             
49635         }
49636         this.beginUpdate();
49637         // add children..
49638         var region = '';
49639         var abn = {};
49640         Roo.each(xitems, function(i)  {
49641             region = nb && i.region ? i.region : false;
49642             
49643             var add = ret.addxtype(i);
49644            
49645             if (region) {
49646                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49647                 if (!i.background) {
49648                     abn[region] = nb[region] ;
49649                 }
49650             }
49651             
49652         });
49653         this.endUpdate();
49654
49655         // make the last non-background panel active..
49656         //if (nb) { Roo.log(abn); }
49657         if (nb) {
49658             
49659             for(var r in abn) {
49660                 region = this.getRegion(r);
49661                 if (region) {
49662                     // tried using nb[r], but it does not work..
49663                      
49664                     region.showPanel(abn[r]);
49665                    
49666                 }
49667             }
49668         }
49669         return ret;
49670         
49671     }
49672 });
49673
49674 /**
49675  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49676  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49677  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49678  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49679  * <pre><code>
49680 // shorthand
49681 var CP = Roo.ContentPanel;
49682
49683 var layout = Roo.BorderLayout.create({
49684     north: {
49685         initialSize: 25,
49686         titlebar: false,
49687         panels: [new CP("north", "North")]
49688     },
49689     west: {
49690         split:true,
49691         initialSize: 200,
49692         minSize: 175,
49693         maxSize: 400,
49694         titlebar: true,
49695         collapsible: true,
49696         panels: [new CP("west", {title: "West"})]
49697     },
49698     east: {
49699         split:true,
49700         initialSize: 202,
49701         minSize: 175,
49702         maxSize: 400,
49703         titlebar: true,
49704         collapsible: true,
49705         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49706     },
49707     south: {
49708         split:true,
49709         initialSize: 100,
49710         minSize: 100,
49711         maxSize: 200,
49712         titlebar: true,
49713         collapsible: true,
49714         panels: [new CP("south", {title: "South", closable: true})]
49715     },
49716     center: {
49717         titlebar: true,
49718         autoScroll:true,
49719         resizeTabs: true,
49720         minTabWidth: 50,
49721         preferredTabWidth: 150,
49722         panels: [
49723             new CP("center1", {title: "Close Me", closable: true}),
49724             new CP("center2", {title: "Center Panel", closable: false})
49725         ]
49726     }
49727 }, document.body);
49728
49729 layout.getRegion("center").showPanel("center1");
49730 </code></pre>
49731  * @param config
49732  * @param targetEl
49733  */
49734 Roo.BorderLayout.create = function(config, targetEl){
49735     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49736     layout.beginUpdate();
49737     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49738     for(var j = 0, jlen = regions.length; j < jlen; j++){
49739         var lr = regions[j];
49740         if(layout.regions[lr] && config[lr].panels){
49741             var r = layout.regions[lr];
49742             var ps = config[lr].panels;
49743             layout.addTypedPanels(r, ps);
49744         }
49745     }
49746     layout.endUpdate();
49747     return layout;
49748 };
49749
49750 // private
49751 Roo.BorderLayout.RegionFactory = {
49752     // private
49753     validRegions : ["north","south","east","west","center"],
49754
49755     // private
49756     create : function(target, mgr, config){
49757         target = target.toLowerCase();
49758         if(config.lightweight || config.basic){
49759             return new Roo.BasicLayoutRegion(mgr, config, target);
49760         }
49761         switch(target){
49762             case "north":
49763                 return new Roo.NorthLayoutRegion(mgr, config);
49764             case "south":
49765                 return new Roo.SouthLayoutRegion(mgr, config);
49766             case "east":
49767                 return new Roo.EastLayoutRegion(mgr, config);
49768             case "west":
49769                 return new Roo.WestLayoutRegion(mgr, config);
49770             case "center":
49771                 return new Roo.CenterLayoutRegion(mgr, config);
49772         }
49773         throw 'Layout region "'+target+'" not supported.';
49774     }
49775 };/*
49776  * Based on:
49777  * Ext JS Library 1.1.1
49778  * Copyright(c) 2006-2007, Ext JS, LLC.
49779  *
49780  * Originally Released Under LGPL - original licence link has changed is not relivant.
49781  *
49782  * Fork - LGPL
49783  * <script type="text/javascript">
49784  */
49785  
49786 /**
49787  * @class Roo.BasicLayoutRegion
49788  * @extends Roo.util.Observable
49789  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49790  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49791  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49792  */
49793 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49794     this.mgr = mgr;
49795     this.position  = pos;
49796     this.events = {
49797         /**
49798          * @scope Roo.BasicLayoutRegion
49799          */
49800         
49801         /**
49802          * @event beforeremove
49803          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49804          * @param {Roo.LayoutRegion} this
49805          * @param {Roo.ContentPanel} panel The panel
49806          * @param {Object} e The cancel event object
49807          */
49808         "beforeremove" : true,
49809         /**
49810          * @event invalidated
49811          * Fires when the layout for this region is changed.
49812          * @param {Roo.LayoutRegion} this
49813          */
49814         "invalidated" : true,
49815         /**
49816          * @event visibilitychange
49817          * Fires when this region is shown or hidden 
49818          * @param {Roo.LayoutRegion} this
49819          * @param {Boolean} visibility true or false
49820          */
49821         "visibilitychange" : true,
49822         /**
49823          * @event paneladded
49824          * Fires when a panel is added. 
49825          * @param {Roo.LayoutRegion} this
49826          * @param {Roo.ContentPanel} panel The panel
49827          */
49828         "paneladded" : true,
49829         /**
49830          * @event panelremoved
49831          * Fires when a panel is removed. 
49832          * @param {Roo.LayoutRegion} this
49833          * @param {Roo.ContentPanel} panel The panel
49834          */
49835         "panelremoved" : true,
49836         /**
49837          * @event collapsed
49838          * Fires when this region is collapsed.
49839          * @param {Roo.LayoutRegion} this
49840          */
49841         "collapsed" : true,
49842         /**
49843          * @event expanded
49844          * Fires when this region is expanded.
49845          * @param {Roo.LayoutRegion} this
49846          */
49847         "expanded" : true,
49848         /**
49849          * @event slideshow
49850          * Fires when this region is slid into view.
49851          * @param {Roo.LayoutRegion} this
49852          */
49853         "slideshow" : true,
49854         /**
49855          * @event slidehide
49856          * Fires when this region slides out of view. 
49857          * @param {Roo.LayoutRegion} this
49858          */
49859         "slidehide" : true,
49860         /**
49861          * @event panelactivated
49862          * Fires when a panel is activated. 
49863          * @param {Roo.LayoutRegion} this
49864          * @param {Roo.ContentPanel} panel The activated panel
49865          */
49866         "panelactivated" : true,
49867         /**
49868          * @event resized
49869          * Fires when the user resizes this region. 
49870          * @param {Roo.LayoutRegion} this
49871          * @param {Number} newSize The new size (width for east/west, height for north/south)
49872          */
49873         "resized" : true
49874     };
49875     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49876     this.panels = new Roo.util.MixedCollection();
49877     this.panels.getKey = this.getPanelId.createDelegate(this);
49878     this.box = null;
49879     this.activePanel = null;
49880     // ensure listeners are added...
49881     
49882     if (config.listeners || config.events) {
49883         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49884             listeners : config.listeners || {},
49885             events : config.events || {}
49886         });
49887     }
49888     
49889     if(skipConfig !== true){
49890         this.applyConfig(config);
49891     }
49892 };
49893
49894 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49895     getPanelId : function(p){
49896         return p.getId();
49897     },
49898     
49899     applyConfig : function(config){
49900         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49901         this.config = config;
49902         
49903     },
49904     
49905     /**
49906      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49907      * the width, for horizontal (north, south) the height.
49908      * @param {Number} newSize The new width or height
49909      */
49910     resizeTo : function(newSize){
49911         var el = this.el ? this.el :
49912                  (this.activePanel ? this.activePanel.getEl() : null);
49913         if(el){
49914             switch(this.position){
49915                 case "east":
49916                 case "west":
49917                     el.setWidth(newSize);
49918                     this.fireEvent("resized", this, newSize);
49919                 break;
49920                 case "north":
49921                 case "south":
49922                     el.setHeight(newSize);
49923                     this.fireEvent("resized", this, newSize);
49924                 break;                
49925             }
49926         }
49927     },
49928     
49929     getBox : function(){
49930         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49931     },
49932     
49933     getMargins : function(){
49934         return this.margins;
49935     },
49936     
49937     updateBox : function(box){
49938         this.box = box;
49939         var el = this.activePanel.getEl();
49940         el.dom.style.left = box.x + "px";
49941         el.dom.style.top = box.y + "px";
49942         this.activePanel.setSize(box.width, box.height);
49943     },
49944     
49945     /**
49946      * Returns the container element for this region.
49947      * @return {Roo.Element}
49948      */
49949     getEl : function(){
49950         return this.activePanel;
49951     },
49952     
49953     /**
49954      * Returns true if this region is currently visible.
49955      * @return {Boolean}
49956      */
49957     isVisible : function(){
49958         return this.activePanel ? true : false;
49959     },
49960     
49961     setActivePanel : function(panel){
49962         panel = this.getPanel(panel);
49963         if(this.activePanel && this.activePanel != panel){
49964             this.activePanel.setActiveState(false);
49965             this.activePanel.getEl().setLeftTop(-10000,-10000);
49966         }
49967         this.activePanel = panel;
49968         panel.setActiveState(true);
49969         if(this.box){
49970             panel.setSize(this.box.width, this.box.height);
49971         }
49972         this.fireEvent("panelactivated", this, panel);
49973         this.fireEvent("invalidated");
49974     },
49975     
49976     /**
49977      * Show the specified panel.
49978      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49979      * @return {Roo.ContentPanel} The shown panel or null
49980      */
49981     showPanel : function(panel){
49982         if(panel = this.getPanel(panel)){
49983             this.setActivePanel(panel);
49984         }
49985         return panel;
49986     },
49987     
49988     /**
49989      * Get the active panel for this region.
49990      * @return {Roo.ContentPanel} The active panel or null
49991      */
49992     getActivePanel : function(){
49993         return this.activePanel;
49994     },
49995     
49996     /**
49997      * Add the passed ContentPanel(s)
49998      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49999      * @return {Roo.ContentPanel} The panel added (if only one was added)
50000      */
50001     add : function(panel){
50002         if(arguments.length > 1){
50003             for(var i = 0, len = arguments.length; i < len; i++) {
50004                 this.add(arguments[i]);
50005             }
50006             return null;
50007         }
50008         if(this.hasPanel(panel)){
50009             this.showPanel(panel);
50010             return panel;
50011         }
50012         var el = panel.getEl();
50013         if(el.dom.parentNode != this.mgr.el.dom){
50014             this.mgr.el.dom.appendChild(el.dom);
50015         }
50016         if(panel.setRegion){
50017             panel.setRegion(this);
50018         }
50019         this.panels.add(panel);
50020         el.setStyle("position", "absolute");
50021         if(!panel.background){
50022             this.setActivePanel(panel);
50023             if(this.config.initialSize && this.panels.getCount()==1){
50024                 this.resizeTo(this.config.initialSize);
50025             }
50026         }
50027         this.fireEvent("paneladded", this, panel);
50028         return panel;
50029     },
50030     
50031     /**
50032      * Returns true if the panel is in this region.
50033      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50034      * @return {Boolean}
50035      */
50036     hasPanel : function(panel){
50037         if(typeof panel == "object"){ // must be panel obj
50038             panel = panel.getId();
50039         }
50040         return this.getPanel(panel) ? true : false;
50041     },
50042     
50043     /**
50044      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50045      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50046      * @param {Boolean} preservePanel Overrides the config preservePanel option
50047      * @return {Roo.ContentPanel} The panel that was removed
50048      */
50049     remove : function(panel, preservePanel){
50050         panel = this.getPanel(panel);
50051         if(!panel){
50052             return null;
50053         }
50054         var e = {};
50055         this.fireEvent("beforeremove", this, panel, e);
50056         if(e.cancel === true){
50057             return null;
50058         }
50059         var panelId = panel.getId();
50060         this.panels.removeKey(panelId);
50061         return panel;
50062     },
50063     
50064     /**
50065      * Returns the panel specified or null if it's not in this region.
50066      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50067      * @return {Roo.ContentPanel}
50068      */
50069     getPanel : function(id){
50070         if(typeof id == "object"){ // must be panel obj
50071             return id;
50072         }
50073         return this.panels.get(id);
50074     },
50075     
50076     /**
50077      * Returns this regions position (north/south/east/west/center).
50078      * @return {String} 
50079      */
50080     getPosition: function(){
50081         return this.position;    
50082     }
50083 });/*
50084  * Based on:
50085  * Ext JS Library 1.1.1
50086  * Copyright(c) 2006-2007, Ext JS, LLC.
50087  *
50088  * Originally Released Under LGPL - original licence link has changed is not relivant.
50089  *
50090  * Fork - LGPL
50091  * <script type="text/javascript">
50092  */
50093  
50094 /**
50095  * @class Roo.LayoutRegion
50096  * @extends Roo.BasicLayoutRegion
50097  * This class represents a region in a layout manager.
50098  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50099  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50100  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50101  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50102  * @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})
50103  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50104  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50105  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50106  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50107  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50108  * @cfg {String}    title           The title for the region (overrides panel titles)
50109  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50110  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50111  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50112  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50113  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50114  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50115  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50116  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50117  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50118  * @cfg {Boolean}   showPin         True to show a pin button
50119  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50120  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50121  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50122  * @cfg {Number}    width           For East/West panels
50123  * @cfg {Number}    height          For North/South panels
50124  * @cfg {Boolean}   split           To show the splitter
50125  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50126  */
50127 Roo.LayoutRegion = function(mgr, config, pos){
50128     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50129     var dh = Roo.DomHelper;
50130     /** This region's container element 
50131     * @type Roo.Element */
50132     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50133     /** This region's title element 
50134     * @type Roo.Element */
50135
50136     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50137         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50138         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50139     ]}, true);
50140     this.titleEl.enableDisplayMode();
50141     /** This region's title text element 
50142     * @type HTMLElement */
50143     this.titleTextEl = this.titleEl.dom.firstChild;
50144     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50145     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50146     this.closeBtn.enableDisplayMode();
50147     this.closeBtn.on("click", this.closeClicked, this);
50148     this.closeBtn.hide();
50149
50150     this.createBody(config);
50151     this.visible = true;
50152     this.collapsed = false;
50153
50154     if(config.hideWhenEmpty){
50155         this.hide();
50156         this.on("paneladded", this.validateVisibility, this);
50157         this.on("panelremoved", this.validateVisibility, this);
50158     }
50159     this.applyConfig(config);
50160 };
50161
50162 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50163
50164     createBody : function(){
50165         /** This region's body element 
50166         * @type Roo.Element */
50167         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50168     },
50169
50170     applyConfig : function(c){
50171         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50172             var dh = Roo.DomHelper;
50173             if(c.titlebar !== false){
50174                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50175                 this.collapseBtn.on("click", this.collapse, this);
50176                 this.collapseBtn.enableDisplayMode();
50177
50178                 if(c.showPin === true || this.showPin){
50179                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50180                     this.stickBtn.enableDisplayMode();
50181                     this.stickBtn.on("click", this.expand, this);
50182                     this.stickBtn.hide();
50183                 }
50184             }
50185             /** This region's collapsed element
50186             * @type Roo.Element */
50187             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50188                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50189             ]}, true);
50190             if(c.floatable !== false){
50191                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50192                this.collapsedEl.on("click", this.collapseClick, this);
50193             }
50194
50195             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50196                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50197                    id: "message", unselectable: "on", style:{"float":"left"}});
50198                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50199              }
50200             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50201             this.expandBtn.on("click", this.expand, this);
50202         }
50203         if(this.collapseBtn){
50204             this.collapseBtn.setVisible(c.collapsible == true);
50205         }
50206         this.cmargins = c.cmargins || this.cmargins ||
50207                          (this.position == "west" || this.position == "east" ?
50208                              {top: 0, left: 2, right:2, bottom: 0} :
50209                              {top: 2, left: 0, right:0, bottom: 2});
50210         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50211         this.bottomTabs = c.tabPosition != "top";
50212         this.autoScroll = c.autoScroll || false;
50213         if(this.autoScroll){
50214             this.bodyEl.setStyle("overflow", "auto");
50215         }else{
50216             this.bodyEl.setStyle("overflow", "hidden");
50217         }
50218         //if(c.titlebar !== false){
50219             if((!c.titlebar && !c.title) || c.titlebar === false){
50220                 this.titleEl.hide();
50221             }else{
50222                 this.titleEl.show();
50223                 if(c.title){
50224                     this.titleTextEl.innerHTML = c.title;
50225                 }
50226             }
50227         //}
50228         this.duration = c.duration || .30;
50229         this.slideDuration = c.slideDuration || .45;
50230         this.config = c;
50231         if(c.collapsed){
50232             this.collapse(true);
50233         }
50234         if(c.hidden){
50235             this.hide();
50236         }
50237     },
50238     /**
50239      * Returns true if this region is currently visible.
50240      * @return {Boolean}
50241      */
50242     isVisible : function(){
50243         return this.visible;
50244     },
50245
50246     /**
50247      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50248      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50249      */
50250     setCollapsedTitle : function(title){
50251         title = title || "&#160;";
50252         if(this.collapsedTitleTextEl){
50253             this.collapsedTitleTextEl.innerHTML = title;
50254         }
50255     },
50256
50257     getBox : function(){
50258         var b;
50259         if(!this.collapsed){
50260             b = this.el.getBox(false, true);
50261         }else{
50262             b = this.collapsedEl.getBox(false, true);
50263         }
50264         return b;
50265     },
50266
50267     getMargins : function(){
50268         return this.collapsed ? this.cmargins : this.margins;
50269     },
50270
50271     highlight : function(){
50272         this.el.addClass("x-layout-panel-dragover");
50273     },
50274
50275     unhighlight : function(){
50276         this.el.removeClass("x-layout-panel-dragover");
50277     },
50278
50279     updateBox : function(box){
50280         this.box = box;
50281         if(!this.collapsed){
50282             this.el.dom.style.left = box.x + "px";
50283             this.el.dom.style.top = box.y + "px";
50284             this.updateBody(box.width, box.height);
50285         }else{
50286             this.collapsedEl.dom.style.left = box.x + "px";
50287             this.collapsedEl.dom.style.top = box.y + "px";
50288             this.collapsedEl.setSize(box.width, box.height);
50289         }
50290         if(this.tabs){
50291             this.tabs.autoSizeTabs();
50292         }
50293     },
50294
50295     updateBody : function(w, h){
50296         if(w !== null){
50297             this.el.setWidth(w);
50298             w -= this.el.getBorderWidth("rl");
50299             if(this.config.adjustments){
50300                 w += this.config.adjustments[0];
50301             }
50302         }
50303         if(h !== null){
50304             this.el.setHeight(h);
50305             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50306             h -= this.el.getBorderWidth("tb");
50307             if(this.config.adjustments){
50308                 h += this.config.adjustments[1];
50309             }
50310             this.bodyEl.setHeight(h);
50311             if(this.tabs){
50312                 h = this.tabs.syncHeight(h);
50313             }
50314         }
50315         if(this.panelSize){
50316             w = w !== null ? w : this.panelSize.width;
50317             h = h !== null ? h : this.panelSize.height;
50318         }
50319         if(this.activePanel){
50320             var el = this.activePanel.getEl();
50321             w = w !== null ? w : el.getWidth();
50322             h = h !== null ? h : el.getHeight();
50323             this.panelSize = {width: w, height: h};
50324             this.activePanel.setSize(w, h);
50325         }
50326         if(Roo.isIE && this.tabs){
50327             this.tabs.el.repaint();
50328         }
50329     },
50330
50331     /**
50332      * Returns the container element for this region.
50333      * @return {Roo.Element}
50334      */
50335     getEl : function(){
50336         return this.el;
50337     },
50338
50339     /**
50340      * Hides this region.
50341      */
50342     hide : function(){
50343         if(!this.collapsed){
50344             this.el.dom.style.left = "-2000px";
50345             this.el.hide();
50346         }else{
50347             this.collapsedEl.dom.style.left = "-2000px";
50348             this.collapsedEl.hide();
50349         }
50350         this.visible = false;
50351         this.fireEvent("visibilitychange", this, false);
50352     },
50353
50354     /**
50355      * Shows this region if it was previously hidden.
50356      */
50357     show : function(){
50358         if(!this.collapsed){
50359             this.el.show();
50360         }else{
50361             this.collapsedEl.show();
50362         }
50363         this.visible = true;
50364         this.fireEvent("visibilitychange", this, true);
50365     },
50366
50367     closeClicked : function(){
50368         if(this.activePanel){
50369             this.remove(this.activePanel);
50370         }
50371     },
50372
50373     collapseClick : function(e){
50374         if(this.isSlid){
50375            e.stopPropagation();
50376            this.slideIn();
50377         }else{
50378            e.stopPropagation();
50379            this.slideOut();
50380         }
50381     },
50382
50383     /**
50384      * Collapses this region.
50385      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50386      */
50387     collapse : function(skipAnim){
50388         if(this.collapsed) return;
50389         this.collapsed = true;
50390         if(this.split){
50391             this.split.el.hide();
50392         }
50393         if(this.config.animate && skipAnim !== true){
50394             this.fireEvent("invalidated", this);
50395             this.animateCollapse();
50396         }else{
50397             this.el.setLocation(-20000,-20000);
50398             this.el.hide();
50399             this.collapsedEl.show();
50400             this.fireEvent("collapsed", this);
50401             this.fireEvent("invalidated", this);
50402         }
50403     },
50404
50405     animateCollapse : function(){
50406         // overridden
50407     },
50408
50409     /**
50410      * Expands this region if it was previously collapsed.
50411      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50412      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50413      */
50414     expand : function(e, skipAnim){
50415         if(e) e.stopPropagation();
50416         if(!this.collapsed || this.el.hasActiveFx()) return;
50417         if(this.isSlid){
50418             this.afterSlideIn();
50419             skipAnim = true;
50420         }
50421         this.collapsed = false;
50422         if(this.config.animate && skipAnim !== true){
50423             this.animateExpand();
50424         }else{
50425             this.el.show();
50426             if(this.split){
50427                 this.split.el.show();
50428             }
50429             this.collapsedEl.setLocation(-2000,-2000);
50430             this.collapsedEl.hide();
50431             this.fireEvent("invalidated", this);
50432             this.fireEvent("expanded", this);
50433         }
50434     },
50435
50436     animateExpand : function(){
50437         // overridden
50438     },
50439
50440     initTabs : function()
50441     {
50442         this.bodyEl.setStyle("overflow", "hidden");
50443         var ts = new Roo.TabPanel(
50444                 this.bodyEl.dom,
50445                 {
50446                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50447                     disableTooltips: this.config.disableTabTips,
50448                     toolbar : this.config.toolbar
50449                 }
50450         );
50451         if(this.config.hideTabs){
50452             ts.stripWrap.setDisplayed(false);
50453         }
50454         this.tabs = ts;
50455         ts.resizeTabs = this.config.resizeTabs === true;
50456         ts.minTabWidth = this.config.minTabWidth || 40;
50457         ts.maxTabWidth = this.config.maxTabWidth || 250;
50458         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50459         ts.monitorResize = false;
50460         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50461         ts.bodyEl.addClass('x-layout-tabs-body');
50462         this.panels.each(this.initPanelAsTab, this);
50463     },
50464
50465     initPanelAsTab : function(panel){
50466         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50467                     this.config.closeOnTab && panel.isClosable());
50468         if(panel.tabTip !== undefined){
50469             ti.setTooltip(panel.tabTip);
50470         }
50471         ti.on("activate", function(){
50472               this.setActivePanel(panel);
50473         }, this);
50474         if(this.config.closeOnTab){
50475             ti.on("beforeclose", function(t, e){
50476                 e.cancel = true;
50477                 this.remove(panel);
50478             }, this);
50479         }
50480         return ti;
50481     },
50482
50483     updatePanelTitle : function(panel, title){
50484         if(this.activePanel == panel){
50485             this.updateTitle(title);
50486         }
50487         if(this.tabs){
50488             var ti = this.tabs.getTab(panel.getEl().id);
50489             ti.setText(title);
50490             if(panel.tabTip !== undefined){
50491                 ti.setTooltip(panel.tabTip);
50492             }
50493         }
50494     },
50495
50496     updateTitle : function(title){
50497         if(this.titleTextEl && !this.config.title){
50498             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50499         }
50500     },
50501
50502     setActivePanel : function(panel){
50503         panel = this.getPanel(panel);
50504         if(this.activePanel && this.activePanel != panel){
50505             this.activePanel.setActiveState(false);
50506         }
50507         this.activePanel = panel;
50508         panel.setActiveState(true);
50509         if(this.panelSize){
50510             panel.setSize(this.panelSize.width, this.panelSize.height);
50511         }
50512         if(this.closeBtn){
50513             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50514         }
50515         this.updateTitle(panel.getTitle());
50516         if(this.tabs){
50517             this.fireEvent("invalidated", this);
50518         }
50519         this.fireEvent("panelactivated", this, panel);
50520     },
50521
50522     /**
50523      * Shows the specified panel.
50524      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50525      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50526      */
50527     showPanel : function(panel)
50528     {
50529         panel = this.getPanel(panel);
50530         if(panel){
50531             if(this.tabs){
50532                 var tab = this.tabs.getTab(panel.getEl().id);
50533                 if(tab.isHidden()){
50534                     this.tabs.unhideTab(tab.id);
50535                 }
50536                 tab.activate();
50537             }else{
50538                 this.setActivePanel(panel);
50539             }
50540         }
50541         return panel;
50542     },
50543
50544     /**
50545      * Get the active panel for this region.
50546      * @return {Roo.ContentPanel} The active panel or null
50547      */
50548     getActivePanel : function(){
50549         return this.activePanel;
50550     },
50551
50552     validateVisibility : function(){
50553         if(this.panels.getCount() < 1){
50554             this.updateTitle("&#160;");
50555             this.closeBtn.hide();
50556             this.hide();
50557         }else{
50558             if(!this.isVisible()){
50559                 this.show();
50560             }
50561         }
50562     },
50563
50564     /**
50565      * Adds the passed ContentPanel(s) to this region.
50566      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50567      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50568      */
50569     add : function(panel){
50570         if(arguments.length > 1){
50571             for(var i = 0, len = arguments.length; i < len; i++) {
50572                 this.add(arguments[i]);
50573             }
50574             return null;
50575         }
50576         if(this.hasPanel(panel)){
50577             this.showPanel(panel);
50578             return panel;
50579         }
50580         panel.setRegion(this);
50581         this.panels.add(panel);
50582         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50583             this.bodyEl.dom.appendChild(panel.getEl().dom);
50584             if(panel.background !== true){
50585                 this.setActivePanel(panel);
50586             }
50587             this.fireEvent("paneladded", this, panel);
50588             return panel;
50589         }
50590         if(!this.tabs){
50591             this.initTabs();
50592         }else{
50593             this.initPanelAsTab(panel);
50594         }
50595         if(panel.background !== true){
50596             this.tabs.activate(panel.getEl().id);
50597         }
50598         this.fireEvent("paneladded", this, panel);
50599         return panel;
50600     },
50601
50602     /**
50603      * Hides the tab for the specified panel.
50604      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50605      */
50606     hidePanel : function(panel){
50607         if(this.tabs && (panel = this.getPanel(panel))){
50608             this.tabs.hideTab(panel.getEl().id);
50609         }
50610     },
50611
50612     /**
50613      * Unhides the tab for a previously hidden panel.
50614      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50615      */
50616     unhidePanel : function(panel){
50617         if(this.tabs && (panel = this.getPanel(panel))){
50618             this.tabs.unhideTab(panel.getEl().id);
50619         }
50620     },
50621
50622     clearPanels : function(){
50623         while(this.panels.getCount() > 0){
50624              this.remove(this.panels.first());
50625         }
50626     },
50627
50628     /**
50629      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50630      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50631      * @param {Boolean} preservePanel Overrides the config preservePanel option
50632      * @return {Roo.ContentPanel} The panel that was removed
50633      */
50634     remove : function(panel, preservePanel){
50635         panel = this.getPanel(panel);
50636         if(!panel){
50637             return null;
50638         }
50639         var e = {};
50640         this.fireEvent("beforeremove", this, panel, e);
50641         if(e.cancel === true){
50642             return null;
50643         }
50644         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50645         var panelId = panel.getId();
50646         this.panels.removeKey(panelId);
50647         if(preservePanel){
50648             document.body.appendChild(panel.getEl().dom);
50649         }
50650         if(this.tabs){
50651             this.tabs.removeTab(panel.getEl().id);
50652         }else if (!preservePanel){
50653             this.bodyEl.dom.removeChild(panel.getEl().dom);
50654         }
50655         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50656             var p = this.panels.first();
50657             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50658             tempEl.appendChild(p.getEl().dom);
50659             this.bodyEl.update("");
50660             this.bodyEl.dom.appendChild(p.getEl().dom);
50661             tempEl = null;
50662             this.updateTitle(p.getTitle());
50663             this.tabs = null;
50664             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50665             this.setActivePanel(p);
50666         }
50667         panel.setRegion(null);
50668         if(this.activePanel == panel){
50669             this.activePanel = null;
50670         }
50671         if(this.config.autoDestroy !== false && preservePanel !== true){
50672             try{panel.destroy();}catch(e){}
50673         }
50674         this.fireEvent("panelremoved", this, panel);
50675         return panel;
50676     },
50677
50678     /**
50679      * Returns the TabPanel component used by this region
50680      * @return {Roo.TabPanel}
50681      */
50682     getTabs : function(){
50683         return this.tabs;
50684     },
50685
50686     createTool : function(parentEl, className){
50687         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50688             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50689         btn.addClassOnOver("x-layout-tools-button-over");
50690         return btn;
50691     }
50692 });/*
50693  * Based on:
50694  * Ext JS Library 1.1.1
50695  * Copyright(c) 2006-2007, Ext JS, LLC.
50696  *
50697  * Originally Released Under LGPL - original licence link has changed is not relivant.
50698  *
50699  * Fork - LGPL
50700  * <script type="text/javascript">
50701  */
50702  
50703
50704
50705 /**
50706  * @class Roo.SplitLayoutRegion
50707  * @extends Roo.LayoutRegion
50708  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50709  */
50710 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50711     this.cursor = cursor;
50712     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50713 };
50714
50715 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50716     splitTip : "Drag to resize.",
50717     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50718     useSplitTips : false,
50719
50720     applyConfig : function(config){
50721         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50722         if(config.split){
50723             if(!this.split){
50724                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50725                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50726                 /** The SplitBar for this region 
50727                 * @type Roo.SplitBar */
50728                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50729                 this.split.on("moved", this.onSplitMove, this);
50730                 this.split.useShim = config.useShim === true;
50731                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50732                 if(this.useSplitTips){
50733                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50734                 }
50735                 if(config.collapsible){
50736                     this.split.el.on("dblclick", this.collapse,  this);
50737                 }
50738             }
50739             if(typeof config.minSize != "undefined"){
50740                 this.split.minSize = config.minSize;
50741             }
50742             if(typeof config.maxSize != "undefined"){
50743                 this.split.maxSize = config.maxSize;
50744             }
50745             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50746                 this.hideSplitter();
50747             }
50748         }
50749     },
50750
50751     getHMaxSize : function(){
50752          var cmax = this.config.maxSize || 10000;
50753          var center = this.mgr.getRegion("center");
50754          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50755     },
50756
50757     getVMaxSize : function(){
50758          var cmax = this.config.maxSize || 10000;
50759          var center = this.mgr.getRegion("center");
50760          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50761     },
50762
50763     onSplitMove : function(split, newSize){
50764         this.fireEvent("resized", this, newSize);
50765     },
50766     
50767     /** 
50768      * Returns the {@link Roo.SplitBar} for this region.
50769      * @return {Roo.SplitBar}
50770      */
50771     getSplitBar : function(){
50772         return this.split;
50773     },
50774     
50775     hide : function(){
50776         this.hideSplitter();
50777         Roo.SplitLayoutRegion.superclass.hide.call(this);
50778     },
50779
50780     hideSplitter : function(){
50781         if(this.split){
50782             this.split.el.setLocation(-2000,-2000);
50783             this.split.el.hide();
50784         }
50785     },
50786
50787     show : function(){
50788         if(this.split){
50789             this.split.el.show();
50790         }
50791         Roo.SplitLayoutRegion.superclass.show.call(this);
50792     },
50793     
50794     beforeSlide: function(){
50795         if(Roo.isGecko){// firefox overflow auto bug workaround
50796             this.bodyEl.clip();
50797             if(this.tabs) this.tabs.bodyEl.clip();
50798             if(this.activePanel){
50799                 this.activePanel.getEl().clip();
50800                 
50801                 if(this.activePanel.beforeSlide){
50802                     this.activePanel.beforeSlide();
50803                 }
50804             }
50805         }
50806     },
50807     
50808     afterSlide : function(){
50809         if(Roo.isGecko){// firefox overflow auto bug workaround
50810             this.bodyEl.unclip();
50811             if(this.tabs) this.tabs.bodyEl.unclip();
50812             if(this.activePanel){
50813                 this.activePanel.getEl().unclip();
50814                 if(this.activePanel.afterSlide){
50815                     this.activePanel.afterSlide();
50816                 }
50817             }
50818         }
50819     },
50820
50821     initAutoHide : function(){
50822         if(this.autoHide !== false){
50823             if(!this.autoHideHd){
50824                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50825                 this.autoHideHd = {
50826                     "mouseout": function(e){
50827                         if(!e.within(this.el, true)){
50828                             st.delay(500);
50829                         }
50830                     },
50831                     "mouseover" : function(e){
50832                         st.cancel();
50833                     },
50834                     scope : this
50835                 };
50836             }
50837             this.el.on(this.autoHideHd);
50838         }
50839     },
50840
50841     clearAutoHide : function(){
50842         if(this.autoHide !== false){
50843             this.el.un("mouseout", this.autoHideHd.mouseout);
50844             this.el.un("mouseover", this.autoHideHd.mouseover);
50845         }
50846     },
50847
50848     clearMonitor : function(){
50849         Roo.get(document).un("click", this.slideInIf, this);
50850     },
50851
50852     // these names are backwards but not changed for compat
50853     slideOut : function(){
50854         if(this.isSlid || this.el.hasActiveFx()){
50855             return;
50856         }
50857         this.isSlid = true;
50858         if(this.collapseBtn){
50859             this.collapseBtn.hide();
50860         }
50861         this.closeBtnState = this.closeBtn.getStyle('display');
50862         this.closeBtn.hide();
50863         if(this.stickBtn){
50864             this.stickBtn.show();
50865         }
50866         this.el.show();
50867         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50868         this.beforeSlide();
50869         this.el.setStyle("z-index", 10001);
50870         this.el.slideIn(this.getSlideAnchor(), {
50871             callback: function(){
50872                 this.afterSlide();
50873                 this.initAutoHide();
50874                 Roo.get(document).on("click", this.slideInIf, this);
50875                 this.fireEvent("slideshow", this);
50876             },
50877             scope: this,
50878             block: true
50879         });
50880     },
50881
50882     afterSlideIn : function(){
50883         this.clearAutoHide();
50884         this.isSlid = false;
50885         this.clearMonitor();
50886         this.el.setStyle("z-index", "");
50887         if(this.collapseBtn){
50888             this.collapseBtn.show();
50889         }
50890         this.closeBtn.setStyle('display', this.closeBtnState);
50891         if(this.stickBtn){
50892             this.stickBtn.hide();
50893         }
50894         this.fireEvent("slidehide", this);
50895     },
50896
50897     slideIn : function(cb){
50898         if(!this.isSlid || this.el.hasActiveFx()){
50899             Roo.callback(cb);
50900             return;
50901         }
50902         this.isSlid = false;
50903         this.beforeSlide();
50904         this.el.slideOut(this.getSlideAnchor(), {
50905             callback: function(){
50906                 this.el.setLeftTop(-10000, -10000);
50907                 this.afterSlide();
50908                 this.afterSlideIn();
50909                 Roo.callback(cb);
50910             },
50911             scope: this,
50912             block: true
50913         });
50914     },
50915     
50916     slideInIf : function(e){
50917         if(!e.within(this.el)){
50918             this.slideIn();
50919         }
50920     },
50921
50922     animateCollapse : function(){
50923         this.beforeSlide();
50924         this.el.setStyle("z-index", 20000);
50925         var anchor = this.getSlideAnchor();
50926         this.el.slideOut(anchor, {
50927             callback : function(){
50928                 this.el.setStyle("z-index", "");
50929                 this.collapsedEl.slideIn(anchor, {duration:.3});
50930                 this.afterSlide();
50931                 this.el.setLocation(-10000,-10000);
50932                 this.el.hide();
50933                 this.fireEvent("collapsed", this);
50934             },
50935             scope: this,
50936             block: true
50937         });
50938     },
50939
50940     animateExpand : function(){
50941         this.beforeSlide();
50942         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50943         this.el.setStyle("z-index", 20000);
50944         this.collapsedEl.hide({
50945             duration:.1
50946         });
50947         this.el.slideIn(this.getSlideAnchor(), {
50948             callback : function(){
50949                 this.el.setStyle("z-index", "");
50950                 this.afterSlide();
50951                 if(this.split){
50952                     this.split.el.show();
50953                 }
50954                 this.fireEvent("invalidated", this);
50955                 this.fireEvent("expanded", this);
50956             },
50957             scope: this,
50958             block: true
50959         });
50960     },
50961
50962     anchors : {
50963         "west" : "left",
50964         "east" : "right",
50965         "north" : "top",
50966         "south" : "bottom"
50967     },
50968
50969     sanchors : {
50970         "west" : "l",
50971         "east" : "r",
50972         "north" : "t",
50973         "south" : "b"
50974     },
50975
50976     canchors : {
50977         "west" : "tl-tr",
50978         "east" : "tr-tl",
50979         "north" : "tl-bl",
50980         "south" : "bl-tl"
50981     },
50982
50983     getAnchor : function(){
50984         return this.anchors[this.position];
50985     },
50986
50987     getCollapseAnchor : function(){
50988         return this.canchors[this.position];
50989     },
50990
50991     getSlideAnchor : function(){
50992         return this.sanchors[this.position];
50993     },
50994
50995     getAlignAdj : function(){
50996         var cm = this.cmargins;
50997         switch(this.position){
50998             case "west":
50999                 return [0, 0];
51000             break;
51001             case "east":
51002                 return [0, 0];
51003             break;
51004             case "north":
51005                 return [0, 0];
51006             break;
51007             case "south":
51008                 return [0, 0];
51009             break;
51010         }
51011     },
51012
51013     getExpandAdj : function(){
51014         var c = this.collapsedEl, cm = this.cmargins;
51015         switch(this.position){
51016             case "west":
51017                 return [-(cm.right+c.getWidth()+cm.left), 0];
51018             break;
51019             case "east":
51020                 return [cm.right+c.getWidth()+cm.left, 0];
51021             break;
51022             case "north":
51023                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51024             break;
51025             case "south":
51026                 return [0, cm.top+cm.bottom+c.getHeight()];
51027             break;
51028         }
51029     }
51030 });/*
51031  * Based on:
51032  * Ext JS Library 1.1.1
51033  * Copyright(c) 2006-2007, Ext JS, LLC.
51034  *
51035  * Originally Released Under LGPL - original licence link has changed is not relivant.
51036  *
51037  * Fork - LGPL
51038  * <script type="text/javascript">
51039  */
51040 /*
51041  * These classes are private internal classes
51042  */
51043 Roo.CenterLayoutRegion = function(mgr, config){
51044     Roo.LayoutRegion.call(this, mgr, config, "center");
51045     this.visible = true;
51046     this.minWidth = config.minWidth || 20;
51047     this.minHeight = config.minHeight || 20;
51048 };
51049
51050 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51051     hide : function(){
51052         // center panel can't be hidden
51053     },
51054     
51055     show : function(){
51056         // center panel can't be hidden
51057     },
51058     
51059     getMinWidth: function(){
51060         return this.minWidth;
51061     },
51062     
51063     getMinHeight: function(){
51064         return this.minHeight;
51065     }
51066 });
51067
51068
51069 Roo.NorthLayoutRegion = function(mgr, config){
51070     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51071     if(this.split){
51072         this.split.placement = Roo.SplitBar.TOP;
51073         this.split.orientation = Roo.SplitBar.VERTICAL;
51074         this.split.el.addClass("x-layout-split-v");
51075     }
51076     var size = config.initialSize || config.height;
51077     if(typeof size != "undefined"){
51078         this.el.setHeight(size);
51079     }
51080 };
51081 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51082     orientation: Roo.SplitBar.VERTICAL,
51083     getBox : function(){
51084         if(this.collapsed){
51085             return this.collapsedEl.getBox();
51086         }
51087         var box = this.el.getBox();
51088         if(this.split){
51089             box.height += this.split.el.getHeight();
51090         }
51091         return box;
51092     },
51093     
51094     updateBox : function(box){
51095         if(this.split && !this.collapsed){
51096             box.height -= this.split.el.getHeight();
51097             this.split.el.setLeft(box.x);
51098             this.split.el.setTop(box.y+box.height);
51099             this.split.el.setWidth(box.width);
51100         }
51101         if(this.collapsed){
51102             this.updateBody(box.width, null);
51103         }
51104         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51105     }
51106 });
51107
51108 Roo.SouthLayoutRegion = function(mgr, config){
51109     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51110     if(this.split){
51111         this.split.placement = Roo.SplitBar.BOTTOM;
51112         this.split.orientation = Roo.SplitBar.VERTICAL;
51113         this.split.el.addClass("x-layout-split-v");
51114     }
51115     var size = config.initialSize || config.height;
51116     if(typeof size != "undefined"){
51117         this.el.setHeight(size);
51118     }
51119 };
51120 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51121     orientation: Roo.SplitBar.VERTICAL,
51122     getBox : function(){
51123         if(this.collapsed){
51124             return this.collapsedEl.getBox();
51125         }
51126         var box = this.el.getBox();
51127         if(this.split){
51128             var sh = this.split.el.getHeight();
51129             box.height += sh;
51130             box.y -= sh;
51131         }
51132         return box;
51133     },
51134     
51135     updateBox : function(box){
51136         if(this.split && !this.collapsed){
51137             var sh = this.split.el.getHeight();
51138             box.height -= sh;
51139             box.y += sh;
51140             this.split.el.setLeft(box.x);
51141             this.split.el.setTop(box.y-sh);
51142             this.split.el.setWidth(box.width);
51143         }
51144         if(this.collapsed){
51145             this.updateBody(box.width, null);
51146         }
51147         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51148     }
51149 });
51150
51151 Roo.EastLayoutRegion = function(mgr, config){
51152     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51153     if(this.split){
51154         this.split.placement = Roo.SplitBar.RIGHT;
51155         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51156         this.split.el.addClass("x-layout-split-h");
51157     }
51158     var size = config.initialSize || config.width;
51159     if(typeof size != "undefined"){
51160         this.el.setWidth(size);
51161     }
51162 };
51163 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51164     orientation: Roo.SplitBar.HORIZONTAL,
51165     getBox : function(){
51166         if(this.collapsed){
51167             return this.collapsedEl.getBox();
51168         }
51169         var box = this.el.getBox();
51170         if(this.split){
51171             var sw = this.split.el.getWidth();
51172             box.width += sw;
51173             box.x -= sw;
51174         }
51175         return box;
51176     },
51177
51178     updateBox : function(box){
51179         if(this.split && !this.collapsed){
51180             var sw = this.split.el.getWidth();
51181             box.width -= sw;
51182             this.split.el.setLeft(box.x);
51183             this.split.el.setTop(box.y);
51184             this.split.el.setHeight(box.height);
51185             box.x += sw;
51186         }
51187         if(this.collapsed){
51188             this.updateBody(null, box.height);
51189         }
51190         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51191     }
51192 });
51193
51194 Roo.WestLayoutRegion = function(mgr, config){
51195     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51196     if(this.split){
51197         this.split.placement = Roo.SplitBar.LEFT;
51198         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51199         this.split.el.addClass("x-layout-split-h");
51200     }
51201     var size = config.initialSize || config.width;
51202     if(typeof size != "undefined"){
51203         this.el.setWidth(size);
51204     }
51205 };
51206 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51207     orientation: Roo.SplitBar.HORIZONTAL,
51208     getBox : function(){
51209         if(this.collapsed){
51210             return this.collapsedEl.getBox();
51211         }
51212         var box = this.el.getBox();
51213         if(this.split){
51214             box.width += this.split.el.getWidth();
51215         }
51216         return box;
51217     },
51218     
51219     updateBox : function(box){
51220         if(this.split && !this.collapsed){
51221             var sw = this.split.el.getWidth();
51222             box.width -= sw;
51223             this.split.el.setLeft(box.x+box.width);
51224             this.split.el.setTop(box.y);
51225             this.split.el.setHeight(box.height);
51226         }
51227         if(this.collapsed){
51228             this.updateBody(null, box.height);
51229         }
51230         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51231     }
51232 });
51233 /*
51234  * Based on:
51235  * Ext JS Library 1.1.1
51236  * Copyright(c) 2006-2007, Ext JS, LLC.
51237  *
51238  * Originally Released Under LGPL - original licence link has changed is not relivant.
51239  *
51240  * Fork - LGPL
51241  * <script type="text/javascript">
51242  */
51243  
51244  
51245 /*
51246  * Private internal class for reading and applying state
51247  */
51248 Roo.LayoutStateManager = function(layout){
51249      // default empty state
51250      this.state = {
51251         north: {},
51252         south: {},
51253         east: {},
51254         west: {}       
51255     };
51256 };
51257
51258 Roo.LayoutStateManager.prototype = {
51259     init : function(layout, provider){
51260         this.provider = provider;
51261         var state = provider.get(layout.id+"-layout-state");
51262         if(state){
51263             var wasUpdating = layout.isUpdating();
51264             if(!wasUpdating){
51265                 layout.beginUpdate();
51266             }
51267             for(var key in state){
51268                 if(typeof state[key] != "function"){
51269                     var rstate = state[key];
51270                     var r = layout.getRegion(key);
51271                     if(r && rstate){
51272                         if(rstate.size){
51273                             r.resizeTo(rstate.size);
51274                         }
51275                         if(rstate.collapsed == true){
51276                             r.collapse(true);
51277                         }else{
51278                             r.expand(null, true);
51279                         }
51280                     }
51281                 }
51282             }
51283             if(!wasUpdating){
51284                 layout.endUpdate();
51285             }
51286             this.state = state; 
51287         }
51288         this.layout = layout;
51289         layout.on("regionresized", this.onRegionResized, this);
51290         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51291         layout.on("regionexpanded", this.onRegionExpanded, this);
51292     },
51293     
51294     storeState : function(){
51295         this.provider.set(this.layout.id+"-layout-state", this.state);
51296     },
51297     
51298     onRegionResized : function(region, newSize){
51299         this.state[region.getPosition()].size = newSize;
51300         this.storeState();
51301     },
51302     
51303     onRegionCollapsed : function(region){
51304         this.state[region.getPosition()].collapsed = true;
51305         this.storeState();
51306     },
51307     
51308     onRegionExpanded : function(region){
51309         this.state[region.getPosition()].collapsed = false;
51310         this.storeState();
51311     }
51312 };/*
51313  * Based on:
51314  * Ext JS Library 1.1.1
51315  * Copyright(c) 2006-2007, Ext JS, LLC.
51316  *
51317  * Originally Released Under LGPL - original licence link has changed is not relivant.
51318  *
51319  * Fork - LGPL
51320  * <script type="text/javascript">
51321  */
51322 /**
51323  * @class Roo.ContentPanel
51324  * @extends Roo.util.Observable
51325  * A basic ContentPanel element.
51326  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51327  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51328  * @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
51329  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51330  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51331  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51332  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51333  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51334  * @cfg {String} title          The title for this panel
51335  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51336  * @cfg {String} url            Calls {@link #setUrl} with this value
51337  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51338  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51339  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51340  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51341
51342  * @constructor
51343  * Create a new ContentPanel.
51344  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51345  * @param {String/Object} config A string to set only the title or a config object
51346  * @param {String} content (optional) Set the HTML content for this panel
51347  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51348  */
51349 Roo.ContentPanel = function(el, config, content){
51350     
51351      
51352     /*
51353     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51354         config = el;
51355         el = Roo.id();
51356     }
51357     if (config && config.parentLayout) { 
51358         el = config.parentLayout.el.createChild(); 
51359     }
51360     */
51361     if(el.autoCreate){ // xtype is available if this is called from factory
51362         config = el;
51363         el = Roo.id();
51364     }
51365     this.el = Roo.get(el);
51366     if(!this.el && config && config.autoCreate){
51367         if(typeof config.autoCreate == "object"){
51368             if(!config.autoCreate.id){
51369                 config.autoCreate.id = config.id||el;
51370             }
51371             this.el = Roo.DomHelper.append(document.body,
51372                         config.autoCreate, true);
51373         }else{
51374             this.el = Roo.DomHelper.append(document.body,
51375                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51376         }
51377     }
51378     this.closable = false;
51379     this.loaded = false;
51380     this.active = false;
51381     if(typeof config == "string"){
51382         this.title = config;
51383     }else{
51384         Roo.apply(this, config);
51385     }
51386     
51387     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51388         this.wrapEl = this.el.wrap();
51389         this.toolbar.container = this.el.insertSibling(false, 'before');
51390         this.toolbar = new Roo.Toolbar(this.toolbar);
51391     }
51392     
51393     // xtype created footer. - not sure if will work as we normally have to render first..
51394     if (this.footer && !this.footer.el && this.footer.xtype) {
51395         if (!this.wrapEl) {
51396             this.wrapEl = this.el.wrap();
51397         }
51398     
51399         this.footer.container = this.wrapEl.createChild();
51400          
51401         this.footer = Roo.factory(this.footer, Roo);
51402         
51403     }
51404     
51405     if(this.resizeEl){
51406         this.resizeEl = Roo.get(this.resizeEl, true);
51407     }else{
51408         this.resizeEl = this.el;
51409     }
51410     // handle view.xtype
51411     
51412  
51413     
51414     
51415     this.addEvents({
51416         /**
51417          * @event activate
51418          * Fires when this panel is activated. 
51419          * @param {Roo.ContentPanel} this
51420          */
51421         "activate" : true,
51422         /**
51423          * @event deactivate
51424          * Fires when this panel is activated. 
51425          * @param {Roo.ContentPanel} this
51426          */
51427         "deactivate" : true,
51428
51429         /**
51430          * @event resize
51431          * Fires when this panel is resized if fitToFrame is true.
51432          * @param {Roo.ContentPanel} this
51433          * @param {Number} width The width after any component adjustments
51434          * @param {Number} height The height after any component adjustments
51435          */
51436         "resize" : true,
51437         
51438          /**
51439          * @event render
51440          * Fires when this tab is created
51441          * @param {Roo.ContentPanel} this
51442          */
51443         "render" : true
51444         
51445         
51446         
51447     });
51448     
51449
51450     
51451     
51452     if(this.autoScroll){
51453         this.resizeEl.setStyle("overflow", "auto");
51454     } else {
51455         // fix randome scrolling
51456         this.el.on('scroll', function() {
51457             Roo.log('fix random scolling');
51458             this.scrollTo('top',0); 
51459         });
51460     }
51461     content = content || this.content;
51462     if(content){
51463         this.setContent(content);
51464     }
51465     if(config && config.url){
51466         this.setUrl(this.url, this.params, this.loadOnce);
51467     }
51468     
51469     
51470     
51471     Roo.ContentPanel.superclass.constructor.call(this);
51472     
51473     if (this.view && typeof(this.view.xtype) != 'undefined') {
51474         this.view.el = this.el.appendChild(document.createElement("div"));
51475         this.view = Roo.factory(this.view); 
51476         this.view.render  &&  this.view.render(false, '');  
51477     }
51478     
51479     
51480     this.fireEvent('render', this);
51481 };
51482
51483 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51484     tabTip:'',
51485     setRegion : function(region){
51486         this.region = region;
51487         if(region){
51488            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51489         }else{
51490            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51491         } 
51492     },
51493     
51494     /**
51495      * Returns the toolbar for this Panel if one was configured. 
51496      * @return {Roo.Toolbar} 
51497      */
51498     getToolbar : function(){
51499         return this.toolbar;
51500     },
51501     
51502     setActiveState : function(active){
51503         this.active = active;
51504         if(!active){
51505             this.fireEvent("deactivate", this);
51506         }else{
51507             this.fireEvent("activate", this);
51508         }
51509     },
51510     /**
51511      * Updates this panel's element
51512      * @param {String} content The new content
51513      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51514     */
51515     setContent : function(content, loadScripts){
51516         this.el.update(content, loadScripts);
51517     },
51518
51519     ignoreResize : function(w, h){
51520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51521             return true;
51522         }else{
51523             this.lastSize = {width: w, height: h};
51524             return false;
51525         }
51526     },
51527     /**
51528      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51529      * @return {Roo.UpdateManager} The UpdateManager
51530      */
51531     getUpdateManager : function(){
51532         return this.el.getUpdateManager();
51533     },
51534      /**
51535      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51536      * @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:
51537 <pre><code>
51538 panel.load({
51539     url: "your-url.php",
51540     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51541     callback: yourFunction,
51542     scope: yourObject, //(optional scope)
51543     discardUrl: false,
51544     nocache: false,
51545     text: "Loading...",
51546     timeout: 30,
51547     scripts: false
51548 });
51549 </code></pre>
51550      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51551      * 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.
51552      * @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}
51553      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51554      * @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.
51555      * @return {Roo.ContentPanel} this
51556      */
51557     load : function(){
51558         var um = this.el.getUpdateManager();
51559         um.update.apply(um, arguments);
51560         return this;
51561     },
51562
51563
51564     /**
51565      * 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.
51566      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51567      * @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)
51568      * @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)
51569      * @return {Roo.UpdateManager} The UpdateManager
51570      */
51571     setUrl : function(url, params, loadOnce){
51572         if(this.refreshDelegate){
51573             this.removeListener("activate", this.refreshDelegate);
51574         }
51575         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51576         this.on("activate", this.refreshDelegate);
51577         return this.el.getUpdateManager();
51578     },
51579     
51580     _handleRefresh : function(url, params, loadOnce){
51581         if(!loadOnce || !this.loaded){
51582             var updater = this.el.getUpdateManager();
51583             updater.update(url, params, this._setLoaded.createDelegate(this));
51584         }
51585     },
51586     
51587     _setLoaded : function(){
51588         this.loaded = true;
51589     }, 
51590     
51591     /**
51592      * Returns this panel's id
51593      * @return {String} 
51594      */
51595     getId : function(){
51596         return this.el.id;
51597     },
51598     
51599     /** 
51600      * Returns this panel's element - used by regiosn to add.
51601      * @return {Roo.Element} 
51602      */
51603     getEl : function(){
51604         return this.wrapEl || this.el;
51605     },
51606     
51607     adjustForComponents : function(width, height)
51608     {
51609         //Roo.log('adjustForComponents ');
51610         if(this.resizeEl != this.el){
51611             width -= this.el.getFrameWidth('lr');
51612             height -= this.el.getFrameWidth('tb');
51613         }
51614         if(this.toolbar){
51615             var te = this.toolbar.getEl();
51616             height -= te.getHeight();
51617             te.setWidth(width);
51618         }
51619         if(this.footer){
51620             var te = this.footer.getEl();
51621             Roo.log("footer:" + te.getHeight());
51622             
51623             height -= te.getHeight();
51624             te.setWidth(width);
51625         }
51626         
51627         
51628         if(this.adjustments){
51629             width += this.adjustments[0];
51630             height += this.adjustments[1];
51631         }
51632         return {"width": width, "height": height};
51633     },
51634     
51635     setSize : function(width, height){
51636         if(this.fitToFrame && !this.ignoreResize(width, height)){
51637             if(this.fitContainer && this.resizeEl != this.el){
51638                 this.el.setSize(width, height);
51639             }
51640             var size = this.adjustForComponents(width, height);
51641             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51642             this.fireEvent('resize', this, size.width, size.height);
51643         }
51644     },
51645     
51646     /**
51647      * Returns this panel's title
51648      * @return {String} 
51649      */
51650     getTitle : function(){
51651         return this.title;
51652     },
51653     
51654     /**
51655      * Set this panel's title
51656      * @param {String} title
51657      */
51658     setTitle : function(title){
51659         this.title = title;
51660         if(this.region){
51661             this.region.updatePanelTitle(this, title);
51662         }
51663     },
51664     
51665     /**
51666      * Returns true is this panel was configured to be closable
51667      * @return {Boolean} 
51668      */
51669     isClosable : function(){
51670         return this.closable;
51671     },
51672     
51673     beforeSlide : function(){
51674         this.el.clip();
51675         this.resizeEl.clip();
51676     },
51677     
51678     afterSlide : function(){
51679         this.el.unclip();
51680         this.resizeEl.unclip();
51681     },
51682     
51683     /**
51684      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51685      *   Will fail silently if the {@link #setUrl} method has not been called.
51686      *   This does not activate the panel, just updates its content.
51687      */
51688     refresh : function(){
51689         if(this.refreshDelegate){
51690            this.loaded = false;
51691            this.refreshDelegate();
51692         }
51693     },
51694     
51695     /**
51696      * Destroys this panel
51697      */
51698     destroy : function(){
51699         this.el.removeAllListeners();
51700         var tempEl = document.createElement("span");
51701         tempEl.appendChild(this.el.dom);
51702         tempEl.innerHTML = "";
51703         this.el.remove();
51704         this.el = null;
51705     },
51706     
51707     /**
51708      * form - if the content panel contains a form - this is a reference to it.
51709      * @type {Roo.form.Form}
51710      */
51711     form : false,
51712     /**
51713      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51714      *    This contains a reference to it.
51715      * @type {Roo.View}
51716      */
51717     view : false,
51718     
51719       /**
51720      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51721      * <pre><code>
51722
51723 layout.addxtype({
51724        xtype : 'Form',
51725        items: [ .... ]
51726    }
51727 );
51728
51729 </code></pre>
51730      * @param {Object} cfg Xtype definition of item to add.
51731      */
51732     
51733     addxtype : function(cfg) {
51734         // add form..
51735         if (cfg.xtype.match(/^Form$/)) {
51736             
51737             var el;
51738             //if (this.footer) {
51739             //    el = this.footer.container.insertSibling(false, 'before');
51740             //} else {
51741                 el = this.el.createChild();
51742             //}
51743
51744             this.form = new  Roo.form.Form(cfg);
51745             
51746             
51747             if ( this.form.allItems.length) this.form.render(el.dom);
51748             return this.form;
51749         }
51750         // should only have one of theses..
51751         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51752             // views.. should not be just added - used named prop 'view''
51753             
51754             cfg.el = this.el.appendChild(document.createElement("div"));
51755             // factory?
51756             
51757             var ret = new Roo.factory(cfg);
51758              
51759              ret.render && ret.render(false, ''); // render blank..
51760             this.view = ret;
51761             return ret;
51762         }
51763         return false;
51764     }
51765 });
51766
51767 /**
51768  * @class Roo.GridPanel
51769  * @extends Roo.ContentPanel
51770  * @constructor
51771  * Create a new GridPanel.
51772  * @param {Roo.grid.Grid} grid The grid for this panel
51773  * @param {String/Object} config A string to set only the panel's title, or a config object
51774  */
51775 Roo.GridPanel = function(grid, config){
51776     
51777   
51778     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51779         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51780         
51781     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51782     
51783     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51784     
51785     if(this.toolbar){
51786         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51787     }
51788     // xtype created footer. - not sure if will work as we normally have to render first..
51789     if (this.footer && !this.footer.el && this.footer.xtype) {
51790         
51791         this.footer.container = this.grid.getView().getFooterPanel(true);
51792         this.footer.dataSource = this.grid.dataSource;
51793         this.footer = Roo.factory(this.footer, Roo);
51794         
51795     }
51796     
51797     grid.monitorWindowResize = false; // turn off autosizing
51798     grid.autoHeight = false;
51799     grid.autoWidth = false;
51800     this.grid = grid;
51801     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51802 };
51803
51804 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51805     getId : function(){
51806         return this.grid.id;
51807     },
51808     
51809     /**
51810      * Returns the grid for this panel
51811      * @return {Roo.grid.Grid} 
51812      */
51813     getGrid : function(){
51814         return this.grid;    
51815     },
51816     
51817     setSize : function(width, height){
51818         if(!this.ignoreResize(width, height)){
51819             var grid = this.grid;
51820             var size = this.adjustForComponents(width, height);
51821             grid.getGridEl().setSize(size.width, size.height);
51822             grid.autoSize();
51823         }
51824     },
51825     
51826     beforeSlide : function(){
51827         this.grid.getView().scroller.clip();
51828     },
51829     
51830     afterSlide : function(){
51831         this.grid.getView().scroller.unclip();
51832     },
51833     
51834     destroy : function(){
51835         this.grid.destroy();
51836         delete this.grid;
51837         Roo.GridPanel.superclass.destroy.call(this); 
51838     }
51839 });
51840
51841
51842 /**
51843  * @class Roo.NestedLayoutPanel
51844  * @extends Roo.ContentPanel
51845  * @constructor
51846  * Create a new NestedLayoutPanel.
51847  * 
51848  * 
51849  * @param {Roo.BorderLayout} layout The layout for this panel
51850  * @param {String/Object} config A string to set only the title or a config object
51851  */
51852 Roo.NestedLayoutPanel = function(layout, config)
51853 {
51854     // construct with only one argument..
51855     /* FIXME - implement nicer consturctors
51856     if (layout.layout) {
51857         config = layout;
51858         layout = config.layout;
51859         delete config.layout;
51860     }
51861     if (layout.xtype && !layout.getEl) {
51862         // then layout needs constructing..
51863         layout = Roo.factory(layout, Roo);
51864     }
51865     */
51866     
51867     
51868     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51869     
51870     layout.monitorWindowResize = false; // turn off autosizing
51871     this.layout = layout;
51872     this.layout.getEl().addClass("x-layout-nested-layout");
51873     
51874     
51875     
51876     
51877 };
51878
51879 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51880
51881     setSize : function(width, height){
51882         if(!this.ignoreResize(width, height)){
51883             var size = this.adjustForComponents(width, height);
51884             var el = this.layout.getEl();
51885             el.setSize(size.width, size.height);
51886             var touch = el.dom.offsetWidth;
51887             this.layout.layout();
51888             // ie requires a double layout on the first pass
51889             if(Roo.isIE && !this.initialized){
51890                 this.initialized = true;
51891                 this.layout.layout();
51892             }
51893         }
51894     },
51895     
51896     // activate all subpanels if not currently active..
51897     
51898     setActiveState : function(active){
51899         this.active = active;
51900         if(!active){
51901             this.fireEvent("deactivate", this);
51902             return;
51903         }
51904         
51905         this.fireEvent("activate", this);
51906         // not sure if this should happen before or after..
51907         if (!this.layout) {
51908             return; // should not happen..
51909         }
51910         var reg = false;
51911         for (var r in this.layout.regions) {
51912             reg = this.layout.getRegion(r);
51913             if (reg.getActivePanel()) {
51914                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51915                 reg.setActivePanel(reg.getActivePanel());
51916                 continue;
51917             }
51918             if (!reg.panels.length) {
51919                 continue;
51920             }
51921             reg.showPanel(reg.getPanel(0));
51922         }
51923         
51924         
51925         
51926         
51927     },
51928     
51929     /**
51930      * Returns the nested BorderLayout for this panel
51931      * @return {Roo.BorderLayout} 
51932      */
51933     getLayout : function(){
51934         return this.layout;
51935     },
51936     
51937      /**
51938      * Adds a xtype elements to the layout of the nested panel
51939      * <pre><code>
51940
51941 panel.addxtype({
51942        xtype : 'ContentPanel',
51943        region: 'west',
51944        items: [ .... ]
51945    }
51946 );
51947
51948 panel.addxtype({
51949         xtype : 'NestedLayoutPanel',
51950         region: 'west',
51951         layout: {
51952            center: { },
51953            west: { }   
51954         },
51955         items : [ ... list of content panels or nested layout panels.. ]
51956    }
51957 );
51958 </code></pre>
51959      * @param {Object} cfg Xtype definition of item to add.
51960      */
51961     addxtype : function(cfg) {
51962         return this.layout.addxtype(cfg);
51963     
51964     }
51965 });
51966
51967 Roo.ScrollPanel = function(el, config, content){
51968     config = config || {};
51969     config.fitToFrame = true;
51970     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51971     
51972     this.el.dom.style.overflow = "hidden";
51973     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51974     this.el.removeClass("x-layout-inactive-content");
51975     this.el.on("mousewheel", this.onWheel, this);
51976
51977     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51978     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51979     up.unselectable(); down.unselectable();
51980     up.on("click", this.scrollUp, this);
51981     down.on("click", this.scrollDown, this);
51982     up.addClassOnOver("x-scroller-btn-over");
51983     down.addClassOnOver("x-scroller-btn-over");
51984     up.addClassOnClick("x-scroller-btn-click");
51985     down.addClassOnClick("x-scroller-btn-click");
51986     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51987
51988     this.resizeEl = this.el;
51989     this.el = wrap; this.up = up; this.down = down;
51990 };
51991
51992 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51993     increment : 100,
51994     wheelIncrement : 5,
51995     scrollUp : function(){
51996         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51997     },
51998
51999     scrollDown : function(){
52000         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52001     },
52002
52003     afterScroll : function(){
52004         var el = this.resizeEl;
52005         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52006         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52007         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52008     },
52009
52010     setSize : function(){
52011         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52012         this.afterScroll();
52013     },
52014
52015     onWheel : function(e){
52016         var d = e.getWheelDelta();
52017         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52018         this.afterScroll();
52019         e.stopEvent();
52020     },
52021
52022     setContent : function(content, loadScripts){
52023         this.resizeEl.update(content, loadScripts);
52024     }
52025
52026 });
52027
52028
52029
52030
52031
52032
52033
52034
52035
52036 /**
52037  * @class Roo.TreePanel
52038  * @extends Roo.ContentPanel
52039  * @constructor
52040  * Create a new TreePanel. - defaults to fit/scoll contents.
52041  * @param {String/Object} config A string to set only the panel's title, or a config object
52042  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52043  */
52044 Roo.TreePanel = function(config){
52045     var el = config.el;
52046     var tree = config.tree;
52047     delete config.tree; 
52048     delete config.el; // hopefull!
52049     
52050     // wrapper for IE7 strict & safari scroll issue
52051     
52052     var treeEl = el.createChild();
52053     config.resizeEl = treeEl;
52054     
52055     
52056     
52057     Roo.TreePanel.superclass.constructor.call(this, el, config);
52058  
52059  
52060     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52061     //console.log(tree);
52062     this.on('activate', function()
52063     {
52064         if (this.tree.rendered) {
52065             return;
52066         }
52067         //console.log('render tree');
52068         this.tree.render();
52069     });
52070     // this should not be needed.. - it's actually the 'el' that resizes?
52071     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52072     
52073     //this.on('resize',  function (cp, w, h) {
52074     //        this.tree.innerCt.setWidth(w);
52075     //        this.tree.innerCt.setHeight(h);
52076     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52077     //});
52078
52079         
52080     
52081 };
52082
52083 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52084     fitToFrame : true,
52085     autoScroll : true
52086 });
52087
52088
52089
52090
52091
52092
52093
52094
52095
52096
52097
52098 /*
52099  * Based on:
52100  * Ext JS Library 1.1.1
52101  * Copyright(c) 2006-2007, Ext JS, LLC.
52102  *
52103  * Originally Released Under LGPL - original licence link has changed is not relivant.
52104  *
52105  * Fork - LGPL
52106  * <script type="text/javascript">
52107  */
52108  
52109
52110 /**
52111  * @class Roo.ReaderLayout
52112  * @extends Roo.BorderLayout
52113  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52114  * center region containing two nested regions (a top one for a list view and one for item preview below),
52115  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52116  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52117  * expedites the setup of the overall layout and regions for this common application style.
52118  * Example:
52119  <pre><code>
52120 var reader = new Roo.ReaderLayout();
52121 var CP = Roo.ContentPanel;  // shortcut for adding
52122
52123 reader.beginUpdate();
52124 reader.add("north", new CP("north", "North"));
52125 reader.add("west", new CP("west", {title: "West"}));
52126 reader.add("east", new CP("east", {title: "East"}));
52127
52128 reader.regions.listView.add(new CP("listView", "List"));
52129 reader.regions.preview.add(new CP("preview", "Preview"));
52130 reader.endUpdate();
52131 </code></pre>
52132 * @constructor
52133 * Create a new ReaderLayout
52134 * @param {Object} config Configuration options
52135 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52136 * document.body if omitted)
52137 */
52138 Roo.ReaderLayout = function(config, renderTo){
52139     var c = config || {size:{}};
52140     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52141         north: c.north !== false ? Roo.apply({
52142             split:false,
52143             initialSize: 32,
52144             titlebar: false
52145         }, c.north) : false,
52146         west: c.west !== false ? Roo.apply({
52147             split:true,
52148             initialSize: 200,
52149             minSize: 175,
52150             maxSize: 400,
52151             titlebar: true,
52152             collapsible: true,
52153             animate: true,
52154             margins:{left:5,right:0,bottom:5,top:5},
52155             cmargins:{left:5,right:5,bottom:5,top:5}
52156         }, c.west) : false,
52157         east: c.east !== false ? Roo.apply({
52158             split:true,
52159             initialSize: 200,
52160             minSize: 175,
52161             maxSize: 400,
52162             titlebar: true,
52163             collapsible: true,
52164             animate: true,
52165             margins:{left:0,right:5,bottom:5,top:5},
52166             cmargins:{left:5,right:5,bottom:5,top:5}
52167         }, c.east) : false,
52168         center: Roo.apply({
52169             tabPosition: 'top',
52170             autoScroll:false,
52171             closeOnTab: true,
52172             titlebar:false,
52173             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52174         }, c.center)
52175     });
52176
52177     this.el.addClass('x-reader');
52178
52179     this.beginUpdate();
52180
52181     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52182         south: c.preview !== false ? Roo.apply({
52183             split:true,
52184             initialSize: 200,
52185             minSize: 100,
52186             autoScroll:true,
52187             collapsible:true,
52188             titlebar: true,
52189             cmargins:{top:5,left:0, right:0, bottom:0}
52190         }, c.preview) : false,
52191         center: Roo.apply({
52192             autoScroll:false,
52193             titlebar:false,
52194             minHeight:200
52195         }, c.listView)
52196     });
52197     this.add('center', new Roo.NestedLayoutPanel(inner,
52198             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52199
52200     this.endUpdate();
52201
52202     this.regions.preview = inner.getRegion('south');
52203     this.regions.listView = inner.getRegion('center');
52204 };
52205
52206 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52207  * Based on:
52208  * Ext JS Library 1.1.1
52209  * Copyright(c) 2006-2007, Ext JS, LLC.
52210  *
52211  * Originally Released Under LGPL - original licence link has changed is not relivant.
52212  *
52213  * Fork - LGPL
52214  * <script type="text/javascript">
52215  */
52216  
52217 /**
52218  * @class Roo.grid.Grid
52219  * @extends Roo.util.Observable
52220  * This class represents the primary interface of a component based grid control.
52221  * <br><br>Usage:<pre><code>
52222  var grid = new Roo.grid.Grid("my-container-id", {
52223      ds: myDataStore,
52224      cm: myColModel,
52225      selModel: mySelectionModel,
52226      autoSizeColumns: true,
52227      monitorWindowResize: false,
52228      trackMouseOver: true
52229  });
52230  // set any options
52231  grid.render();
52232  * </code></pre>
52233  * <b>Common Problems:</b><br/>
52234  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52235  * element will correct this<br/>
52236  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52237  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52238  * are unpredictable.<br/>
52239  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52240  * grid to calculate dimensions/offsets.<br/>
52241   * @constructor
52242  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52243  * The container MUST have some type of size defined for the grid to fill. The container will be
52244  * automatically set to position relative if it isn't already.
52245  * @param {Object} config A config object that sets properties on this grid.
52246  */
52247 Roo.grid.Grid = function(container, config){
52248         // initialize the container
52249         this.container = Roo.get(container);
52250         this.container.update("");
52251         this.container.setStyle("overflow", "hidden");
52252     this.container.addClass('x-grid-container');
52253
52254     this.id = this.container.id;
52255
52256     Roo.apply(this, config);
52257     // check and correct shorthanded configs
52258     if(this.ds){
52259         this.dataSource = this.ds;
52260         delete this.ds;
52261     }
52262     if(this.cm){
52263         this.colModel = this.cm;
52264         delete this.cm;
52265     }
52266     if(this.sm){
52267         this.selModel = this.sm;
52268         delete this.sm;
52269     }
52270
52271     if (this.selModel) {
52272         this.selModel = Roo.factory(this.selModel, Roo.grid);
52273         this.sm = this.selModel;
52274         this.sm.xmodule = this.xmodule || false;
52275     }
52276     if (typeof(this.colModel.config) == 'undefined') {
52277         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52278         this.cm = this.colModel;
52279         this.cm.xmodule = this.xmodule || false;
52280     }
52281     if (this.dataSource) {
52282         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52283         this.ds = this.dataSource;
52284         this.ds.xmodule = this.xmodule || false;
52285          
52286     }
52287     
52288     
52289     
52290     if(this.width){
52291         this.container.setWidth(this.width);
52292     }
52293
52294     if(this.height){
52295         this.container.setHeight(this.height);
52296     }
52297     /** @private */
52298         this.addEvents({
52299         // raw events
52300         /**
52301          * @event click
52302          * The raw click event for the entire grid.
52303          * @param {Roo.EventObject} e
52304          */
52305         "click" : true,
52306         /**
52307          * @event dblclick
52308          * The raw dblclick event for the entire grid.
52309          * @param {Roo.EventObject} e
52310          */
52311         "dblclick" : true,
52312         /**
52313          * @event contextmenu
52314          * The raw contextmenu event for the entire grid.
52315          * @param {Roo.EventObject} e
52316          */
52317         "contextmenu" : true,
52318         /**
52319          * @event mousedown
52320          * The raw mousedown event for the entire grid.
52321          * @param {Roo.EventObject} e
52322          */
52323         "mousedown" : true,
52324         /**
52325          * @event mouseup
52326          * The raw mouseup event for the entire grid.
52327          * @param {Roo.EventObject} e
52328          */
52329         "mouseup" : true,
52330         /**
52331          * @event mouseover
52332          * The raw mouseover event for the entire grid.
52333          * @param {Roo.EventObject} e
52334          */
52335         "mouseover" : true,
52336         /**
52337          * @event mouseout
52338          * The raw mouseout event for the entire grid.
52339          * @param {Roo.EventObject} e
52340          */
52341         "mouseout" : true,
52342         /**
52343          * @event keypress
52344          * The raw keypress event for the entire grid.
52345          * @param {Roo.EventObject} e
52346          */
52347         "keypress" : true,
52348         /**
52349          * @event keydown
52350          * The raw keydown event for the entire grid.
52351          * @param {Roo.EventObject} e
52352          */
52353         "keydown" : true,
52354
52355         // custom events
52356
52357         /**
52358          * @event cellclick
52359          * Fires when a cell is clicked
52360          * @param {Grid} this
52361          * @param {Number} rowIndex
52362          * @param {Number} columnIndex
52363          * @param {Roo.EventObject} e
52364          */
52365         "cellclick" : true,
52366         /**
52367          * @event celldblclick
52368          * Fires when a cell is double clicked
52369          * @param {Grid} this
52370          * @param {Number} rowIndex
52371          * @param {Number} columnIndex
52372          * @param {Roo.EventObject} e
52373          */
52374         "celldblclick" : true,
52375         /**
52376          * @event rowclick
52377          * Fires when a row is clicked
52378          * @param {Grid} this
52379          * @param {Number} rowIndex
52380          * @param {Roo.EventObject} e
52381          */
52382         "rowclick" : true,
52383         /**
52384          * @event rowdblclick
52385          * Fires when a row is double clicked
52386          * @param {Grid} this
52387          * @param {Number} rowIndex
52388          * @param {Roo.EventObject} e
52389          */
52390         "rowdblclick" : true,
52391         /**
52392          * @event headerclick
52393          * Fires when a header is clicked
52394          * @param {Grid} this
52395          * @param {Number} columnIndex
52396          * @param {Roo.EventObject} e
52397          */
52398         "headerclick" : true,
52399         /**
52400          * @event headerdblclick
52401          * Fires when a header cell is double clicked
52402          * @param {Grid} this
52403          * @param {Number} columnIndex
52404          * @param {Roo.EventObject} e
52405          */
52406         "headerdblclick" : true,
52407         /**
52408          * @event rowcontextmenu
52409          * Fires when a row is right clicked
52410          * @param {Grid} this
52411          * @param {Number} rowIndex
52412          * @param {Roo.EventObject} e
52413          */
52414         "rowcontextmenu" : true,
52415         /**
52416          * @event cellcontextmenu
52417          * Fires when a cell is right clicked
52418          * @param {Grid} this
52419          * @param {Number} rowIndex
52420          * @param {Number} cellIndex
52421          * @param {Roo.EventObject} e
52422          */
52423          "cellcontextmenu" : true,
52424         /**
52425          * @event headercontextmenu
52426          * Fires when a header is right clicked
52427          * @param {Grid} this
52428          * @param {Number} columnIndex
52429          * @param {Roo.EventObject} e
52430          */
52431         "headercontextmenu" : true,
52432         /**
52433          * @event bodyscroll
52434          * Fires when the body element is scrolled
52435          * @param {Number} scrollLeft
52436          * @param {Number} scrollTop
52437          */
52438         "bodyscroll" : true,
52439         /**
52440          * @event columnresize
52441          * Fires when the user resizes a column
52442          * @param {Number} columnIndex
52443          * @param {Number} newSize
52444          */
52445         "columnresize" : true,
52446         /**
52447          * @event columnmove
52448          * Fires when the user moves a column
52449          * @param {Number} oldIndex
52450          * @param {Number} newIndex
52451          */
52452         "columnmove" : true,
52453         /**
52454          * @event startdrag
52455          * Fires when row(s) start being dragged
52456          * @param {Grid} this
52457          * @param {Roo.GridDD} dd The drag drop object
52458          * @param {event} e The raw browser event
52459          */
52460         "startdrag" : true,
52461         /**
52462          * @event enddrag
52463          * Fires when a drag operation is complete
52464          * @param {Grid} this
52465          * @param {Roo.GridDD} dd The drag drop object
52466          * @param {event} e The raw browser event
52467          */
52468         "enddrag" : true,
52469         /**
52470          * @event dragdrop
52471          * Fires when dragged row(s) are dropped on a valid DD target
52472          * @param {Grid} this
52473          * @param {Roo.GridDD} dd The drag drop object
52474          * @param {String} targetId The target drag drop object
52475          * @param {event} e The raw browser event
52476          */
52477         "dragdrop" : true,
52478         /**
52479          * @event dragover
52480          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52481          * @param {Grid} this
52482          * @param {Roo.GridDD} dd The drag drop object
52483          * @param {String} targetId The target drag drop object
52484          * @param {event} e The raw browser event
52485          */
52486         "dragover" : true,
52487         /**
52488          * @event dragenter
52489          *  Fires when the dragged row(s) first cross another DD target while being dragged
52490          * @param {Grid} this
52491          * @param {Roo.GridDD} dd The drag drop object
52492          * @param {String} targetId The target drag drop object
52493          * @param {event} e The raw browser event
52494          */
52495         "dragenter" : true,
52496         /**
52497          * @event dragout
52498          * Fires when the dragged row(s) leave another DD target while being dragged
52499          * @param {Grid} this
52500          * @param {Roo.GridDD} dd The drag drop object
52501          * @param {String} targetId The target drag drop object
52502          * @param {event} e The raw browser event
52503          */
52504         "dragout" : true,
52505         /**
52506          * @event rowclass
52507          * Fires when a row is rendered, so you can change add a style to it.
52508          * @param {GridView} gridview   The grid view
52509          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52510          */
52511         'rowclass' : true,
52512
52513         /**
52514          * @event render
52515          * Fires when the grid is rendered
52516          * @param {Grid} grid
52517          */
52518         'render' : true
52519     });
52520
52521     Roo.grid.Grid.superclass.constructor.call(this);
52522 };
52523 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52524     
52525     /**
52526      * @cfg {String} ddGroup - drag drop group.
52527      */
52528
52529     /**
52530      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52531      */
52532     minColumnWidth : 25,
52533
52534     /**
52535      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52536      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52537      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52538      */
52539     autoSizeColumns : false,
52540
52541     /**
52542      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52543      */
52544     autoSizeHeaders : true,
52545
52546     /**
52547      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52548      */
52549     monitorWindowResize : true,
52550
52551     /**
52552      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52553      * rows measured to get a columns size. Default is 0 (all rows).
52554      */
52555     maxRowsToMeasure : 0,
52556
52557     /**
52558      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52559      */
52560     trackMouseOver : true,
52561
52562     /**
52563     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52564     */
52565     
52566     /**
52567     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52568     */
52569     enableDragDrop : false,
52570     
52571     /**
52572     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52573     */
52574     enableColumnMove : true,
52575     
52576     /**
52577     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52578     */
52579     enableColumnHide : true,
52580     
52581     /**
52582     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52583     */
52584     enableRowHeightSync : false,
52585     
52586     /**
52587     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52588     */
52589     stripeRows : true,
52590     
52591     /**
52592     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52593     */
52594     autoHeight : false,
52595
52596     /**
52597      * @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.
52598      */
52599     autoExpandColumn : false,
52600
52601     /**
52602     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52603     * Default is 50.
52604     */
52605     autoExpandMin : 50,
52606
52607     /**
52608     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52609     */
52610     autoExpandMax : 1000,
52611
52612     /**
52613     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52614     */
52615     view : null,
52616
52617     /**
52618     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52619     */
52620     loadMask : false,
52621     /**
52622     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52623     */
52624     dropTarget: false,
52625     
52626    
52627     
52628     // private
52629     rendered : false,
52630
52631     /**
52632     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52633     * of a fixed width. Default is false.
52634     */
52635     /**
52636     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52637     */
52638     /**
52639      * Called once after all setup has been completed and the grid is ready to be rendered.
52640      * @return {Roo.grid.Grid} this
52641      */
52642     render : function()
52643     {
52644         var c = this.container;
52645         // try to detect autoHeight/width mode
52646         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52647             this.autoHeight = true;
52648         }
52649         var view = this.getView();
52650         view.init(this);
52651
52652         c.on("click", this.onClick, this);
52653         c.on("dblclick", this.onDblClick, this);
52654         c.on("contextmenu", this.onContextMenu, this);
52655         c.on("keydown", this.onKeyDown, this);
52656         if (Roo.isTouch) {
52657             c.on("touchstart", this.onTouchStart, this);
52658         }
52659
52660         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52661
52662         this.getSelectionModel().init(this);
52663
52664         view.render();
52665
52666         if(this.loadMask){
52667             this.loadMask = new Roo.LoadMask(this.container,
52668                     Roo.apply({store:this.dataSource}, this.loadMask));
52669         }
52670         
52671         
52672         if (this.toolbar && this.toolbar.xtype) {
52673             this.toolbar.container = this.getView().getHeaderPanel(true);
52674             this.toolbar = new Roo.Toolbar(this.toolbar);
52675         }
52676         if (this.footer && this.footer.xtype) {
52677             this.footer.dataSource = this.getDataSource();
52678             this.footer.container = this.getView().getFooterPanel(true);
52679             this.footer = Roo.factory(this.footer, Roo);
52680         }
52681         if (this.dropTarget && this.dropTarget.xtype) {
52682             delete this.dropTarget.xtype;
52683             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52684         }
52685         
52686         
52687         this.rendered = true;
52688         this.fireEvent('render', this);
52689         return this;
52690     },
52691
52692         /**
52693          * Reconfigures the grid to use a different Store and Column Model.
52694          * The View will be bound to the new objects and refreshed.
52695          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52696          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52697          */
52698     reconfigure : function(dataSource, colModel){
52699         if(this.loadMask){
52700             this.loadMask.destroy();
52701             this.loadMask = new Roo.LoadMask(this.container,
52702                     Roo.apply({store:dataSource}, this.loadMask));
52703         }
52704         this.view.bind(dataSource, colModel);
52705         this.dataSource = dataSource;
52706         this.colModel = colModel;
52707         this.view.refresh(true);
52708     },
52709
52710     // private
52711     onKeyDown : function(e){
52712         this.fireEvent("keydown", e);
52713     },
52714
52715     /**
52716      * Destroy this grid.
52717      * @param {Boolean} removeEl True to remove the element
52718      */
52719     destroy : function(removeEl, keepListeners){
52720         if(this.loadMask){
52721             this.loadMask.destroy();
52722         }
52723         var c = this.container;
52724         c.removeAllListeners();
52725         this.view.destroy();
52726         this.colModel.purgeListeners();
52727         if(!keepListeners){
52728             this.purgeListeners();
52729         }
52730         c.update("");
52731         if(removeEl === true){
52732             c.remove();
52733         }
52734     },
52735
52736     // private
52737     processEvent : function(name, e){
52738         // does this fire select???
52739         //Roo.log('grid:processEvent '  + name);
52740         
52741         if (name != 'touchstart' ) {
52742             this.fireEvent(name, e);    
52743         }
52744         
52745         var t = e.getTarget();
52746         var v = this.view;
52747         var header = v.findHeaderIndex(t);
52748         if(header !== false){
52749             var ename = name == 'touchstart' ? 'click' : name;
52750              
52751             this.fireEvent("header" + ename, this, header, e);
52752         }else{
52753             var row = v.findRowIndex(t);
52754             var cell = v.findCellIndex(t);
52755             if (name == 'touchstart') {
52756                 // first touch is always a click.
52757                 // hopefull this happens after selection is updated.?
52758                 name = false;
52759                 
52760                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52761                     var cs = this.selModel.getSelectedCell();
52762                     if (row == cs[0] && cell == cs[1]){
52763                         name = 'dblclick';
52764                     }
52765                 }
52766                 if (typeof(this.selModel.getSelections) != 'undefined') {
52767                     var cs = this.selModel.getSelections();
52768                     var ds = this.dataSource;
52769                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52770                         name = 'dblclick';
52771                     }
52772                 }
52773                 if (!name) {
52774                     return;
52775                 }
52776             }
52777             
52778             
52779             if(row !== false){
52780                 this.fireEvent("row" + name, this, row, e);
52781                 if(cell !== false){
52782                     this.fireEvent("cell" + name, this, row, cell, e);
52783                 }
52784             }
52785         }
52786     },
52787
52788     // private
52789     onClick : function(e){
52790         this.processEvent("click", e);
52791     },
52792    // private
52793     onTouchStart : function(e){
52794         this.processEvent("touchstart", e);
52795     },
52796
52797     // private
52798     onContextMenu : function(e, t){
52799         this.processEvent("contextmenu", e);
52800     },
52801
52802     // private
52803     onDblClick : function(e){
52804         this.processEvent("dblclick", e);
52805     },
52806
52807     // private
52808     walkCells : function(row, col, step, fn, scope){
52809         var cm = this.colModel, clen = cm.getColumnCount();
52810         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52811         if(step < 0){
52812             if(col < 0){
52813                 row--;
52814                 first = false;
52815             }
52816             while(row >= 0){
52817                 if(!first){
52818                     col = clen-1;
52819                 }
52820                 first = false;
52821                 while(col >= 0){
52822                     if(fn.call(scope || this, row, col, cm) === true){
52823                         return [row, col];
52824                     }
52825                     col--;
52826                 }
52827                 row--;
52828             }
52829         } else {
52830             if(col >= clen){
52831                 row++;
52832                 first = false;
52833             }
52834             while(row < rlen){
52835                 if(!first){
52836                     col = 0;
52837                 }
52838                 first = false;
52839                 while(col < clen){
52840                     if(fn.call(scope || this, row, col, cm) === true){
52841                         return [row, col];
52842                     }
52843                     col++;
52844                 }
52845                 row++;
52846             }
52847         }
52848         return null;
52849     },
52850
52851     // private
52852     getSelections : function(){
52853         return this.selModel.getSelections();
52854     },
52855
52856     /**
52857      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52858      * but if manual update is required this method will initiate it.
52859      */
52860     autoSize : function(){
52861         if(this.rendered){
52862             this.view.layout();
52863             if(this.view.adjustForScroll){
52864                 this.view.adjustForScroll();
52865             }
52866         }
52867     },
52868
52869     /**
52870      * Returns the grid's underlying element.
52871      * @return {Element} The element
52872      */
52873     getGridEl : function(){
52874         return this.container;
52875     },
52876
52877     // private for compatibility, overridden by editor grid
52878     stopEditing : function(){},
52879
52880     /**
52881      * Returns the grid's SelectionModel.
52882      * @return {SelectionModel}
52883      */
52884     getSelectionModel : function(){
52885         if(!this.selModel){
52886             this.selModel = new Roo.grid.RowSelectionModel();
52887         }
52888         return this.selModel;
52889     },
52890
52891     /**
52892      * Returns the grid's DataSource.
52893      * @return {DataSource}
52894      */
52895     getDataSource : function(){
52896         return this.dataSource;
52897     },
52898
52899     /**
52900      * Returns the grid's ColumnModel.
52901      * @return {ColumnModel}
52902      */
52903     getColumnModel : function(){
52904         return this.colModel;
52905     },
52906
52907     /**
52908      * Returns the grid's GridView object.
52909      * @return {GridView}
52910      */
52911     getView : function(){
52912         if(!this.view){
52913             this.view = new Roo.grid.GridView(this.viewConfig);
52914         }
52915         return this.view;
52916     },
52917     /**
52918      * Called to get grid's drag proxy text, by default returns this.ddText.
52919      * @return {String}
52920      */
52921     getDragDropText : function(){
52922         var count = this.selModel.getCount();
52923         return String.format(this.ddText, count, count == 1 ? '' : 's');
52924     }
52925 });
52926 /**
52927  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52928  * %0 is replaced with the number of selected rows.
52929  * @type String
52930  */
52931 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52932  * Based on:
52933  * Ext JS Library 1.1.1
52934  * Copyright(c) 2006-2007, Ext JS, LLC.
52935  *
52936  * Originally Released Under LGPL - original licence link has changed is not relivant.
52937  *
52938  * Fork - LGPL
52939  * <script type="text/javascript">
52940  */
52941  
52942 Roo.grid.AbstractGridView = function(){
52943         this.grid = null;
52944         
52945         this.events = {
52946             "beforerowremoved" : true,
52947             "beforerowsinserted" : true,
52948             "beforerefresh" : true,
52949             "rowremoved" : true,
52950             "rowsinserted" : true,
52951             "rowupdated" : true,
52952             "refresh" : true
52953         };
52954     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52955 };
52956
52957 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52958     rowClass : "x-grid-row",
52959     cellClass : "x-grid-cell",
52960     tdClass : "x-grid-td",
52961     hdClass : "x-grid-hd",
52962     splitClass : "x-grid-hd-split",
52963     
52964     init: function(grid){
52965         this.grid = grid;
52966                 var cid = this.grid.getGridEl().id;
52967         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52968         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52969         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52970         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52971         },
52972         
52973     getColumnRenderers : function(){
52974         var renderers = [];
52975         var cm = this.grid.colModel;
52976         var colCount = cm.getColumnCount();
52977         for(var i = 0; i < colCount; i++){
52978             renderers[i] = cm.getRenderer(i);
52979         }
52980         return renderers;
52981     },
52982     
52983     getColumnIds : function(){
52984         var ids = [];
52985         var cm = this.grid.colModel;
52986         var colCount = cm.getColumnCount();
52987         for(var i = 0; i < colCount; i++){
52988             ids[i] = cm.getColumnId(i);
52989         }
52990         return ids;
52991     },
52992     
52993     getDataIndexes : function(){
52994         if(!this.indexMap){
52995             this.indexMap = this.buildIndexMap();
52996         }
52997         return this.indexMap.colToData;
52998     },
52999     
53000     getColumnIndexByDataIndex : function(dataIndex){
53001         if(!this.indexMap){
53002             this.indexMap = this.buildIndexMap();
53003         }
53004         return this.indexMap.dataToCol[dataIndex];
53005     },
53006     
53007     /**
53008      * Set a css style for a column dynamically. 
53009      * @param {Number} colIndex The index of the column
53010      * @param {String} name The css property name
53011      * @param {String} value The css value
53012      */
53013     setCSSStyle : function(colIndex, name, value){
53014         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53015         Roo.util.CSS.updateRule(selector, name, value);
53016     },
53017     
53018     generateRules : function(cm){
53019         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53020         Roo.util.CSS.removeStyleSheet(rulesId);
53021         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53022             var cid = cm.getColumnId(i);
53023             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53024                          this.tdSelector, cid, " {\n}\n",
53025                          this.hdSelector, cid, " {\n}\n",
53026                          this.splitSelector, cid, " {\n}\n");
53027         }
53028         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53029     }
53030 });/*
53031  * Based on:
53032  * Ext JS Library 1.1.1
53033  * Copyright(c) 2006-2007, Ext JS, LLC.
53034  *
53035  * Originally Released Under LGPL - original licence link has changed is not relivant.
53036  *
53037  * Fork - LGPL
53038  * <script type="text/javascript">
53039  */
53040
53041 // private
53042 // This is a support class used internally by the Grid components
53043 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53044     this.grid = grid;
53045     this.view = grid.getView();
53046     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53047     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53048     if(hd2){
53049         this.setHandleElId(Roo.id(hd));
53050         this.setOuterHandleElId(Roo.id(hd2));
53051     }
53052     this.scroll = false;
53053 };
53054 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53055     maxDragWidth: 120,
53056     getDragData : function(e){
53057         var t = Roo.lib.Event.getTarget(e);
53058         var h = this.view.findHeaderCell(t);
53059         if(h){
53060             return {ddel: h.firstChild, header:h};
53061         }
53062         return false;
53063     },
53064
53065     onInitDrag : function(e){
53066         this.view.headersDisabled = true;
53067         var clone = this.dragData.ddel.cloneNode(true);
53068         clone.id = Roo.id();
53069         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53070         this.proxy.update(clone);
53071         return true;
53072     },
53073
53074     afterValidDrop : function(){
53075         var v = this.view;
53076         setTimeout(function(){
53077             v.headersDisabled = false;
53078         }, 50);
53079     },
53080
53081     afterInvalidDrop : function(){
53082         var v = this.view;
53083         setTimeout(function(){
53084             v.headersDisabled = false;
53085         }, 50);
53086     }
53087 });
53088 /*
53089  * Based on:
53090  * Ext JS Library 1.1.1
53091  * Copyright(c) 2006-2007, Ext JS, LLC.
53092  *
53093  * Originally Released Under LGPL - original licence link has changed is not relivant.
53094  *
53095  * Fork - LGPL
53096  * <script type="text/javascript">
53097  */
53098 // private
53099 // This is a support class used internally by the Grid components
53100 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53101     this.grid = grid;
53102     this.view = grid.getView();
53103     // split the proxies so they don't interfere with mouse events
53104     this.proxyTop = Roo.DomHelper.append(document.body, {
53105         cls:"col-move-top", html:"&#160;"
53106     }, true);
53107     this.proxyBottom = Roo.DomHelper.append(document.body, {
53108         cls:"col-move-bottom", html:"&#160;"
53109     }, true);
53110     this.proxyTop.hide = this.proxyBottom.hide = function(){
53111         this.setLeftTop(-100,-100);
53112         this.setStyle("visibility", "hidden");
53113     };
53114     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53115     // temporarily disabled
53116     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53117     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53118 };
53119 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53120     proxyOffsets : [-4, -9],
53121     fly: Roo.Element.fly,
53122
53123     getTargetFromEvent : function(e){
53124         var t = Roo.lib.Event.getTarget(e);
53125         var cindex = this.view.findCellIndex(t);
53126         if(cindex !== false){
53127             return this.view.getHeaderCell(cindex);
53128         }
53129         return null;
53130     },
53131
53132     nextVisible : function(h){
53133         var v = this.view, cm = this.grid.colModel;
53134         h = h.nextSibling;
53135         while(h){
53136             if(!cm.isHidden(v.getCellIndex(h))){
53137                 return h;
53138             }
53139             h = h.nextSibling;
53140         }
53141         return null;
53142     },
53143
53144     prevVisible : function(h){
53145         var v = this.view, cm = this.grid.colModel;
53146         h = h.prevSibling;
53147         while(h){
53148             if(!cm.isHidden(v.getCellIndex(h))){
53149                 return h;
53150             }
53151             h = h.prevSibling;
53152         }
53153         return null;
53154     },
53155
53156     positionIndicator : function(h, n, e){
53157         var x = Roo.lib.Event.getPageX(e);
53158         var r = Roo.lib.Dom.getRegion(n.firstChild);
53159         var px, pt, py = r.top + this.proxyOffsets[1];
53160         if((r.right - x) <= (r.right-r.left)/2){
53161             px = r.right+this.view.borderWidth;
53162             pt = "after";
53163         }else{
53164             px = r.left;
53165             pt = "before";
53166         }
53167         var oldIndex = this.view.getCellIndex(h);
53168         var newIndex = this.view.getCellIndex(n);
53169
53170         if(this.grid.colModel.isFixed(newIndex)){
53171             return false;
53172         }
53173
53174         var locked = this.grid.colModel.isLocked(newIndex);
53175
53176         if(pt == "after"){
53177             newIndex++;
53178         }
53179         if(oldIndex < newIndex){
53180             newIndex--;
53181         }
53182         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53183             return false;
53184         }
53185         px +=  this.proxyOffsets[0];
53186         this.proxyTop.setLeftTop(px, py);
53187         this.proxyTop.show();
53188         if(!this.bottomOffset){
53189             this.bottomOffset = this.view.mainHd.getHeight();
53190         }
53191         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53192         this.proxyBottom.show();
53193         return pt;
53194     },
53195
53196     onNodeEnter : function(n, dd, e, data){
53197         if(data.header != n){
53198             this.positionIndicator(data.header, n, e);
53199         }
53200     },
53201
53202     onNodeOver : function(n, dd, e, data){
53203         var result = false;
53204         if(data.header != n){
53205             result = this.positionIndicator(data.header, n, e);
53206         }
53207         if(!result){
53208             this.proxyTop.hide();
53209             this.proxyBottom.hide();
53210         }
53211         return result ? this.dropAllowed : this.dropNotAllowed;
53212     },
53213
53214     onNodeOut : function(n, dd, e, data){
53215         this.proxyTop.hide();
53216         this.proxyBottom.hide();
53217     },
53218
53219     onNodeDrop : function(n, dd, e, data){
53220         var h = data.header;
53221         if(h != n){
53222             var cm = this.grid.colModel;
53223             var x = Roo.lib.Event.getPageX(e);
53224             var r = Roo.lib.Dom.getRegion(n.firstChild);
53225             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53226             var oldIndex = this.view.getCellIndex(h);
53227             var newIndex = this.view.getCellIndex(n);
53228             var locked = cm.isLocked(newIndex);
53229             if(pt == "after"){
53230                 newIndex++;
53231             }
53232             if(oldIndex < newIndex){
53233                 newIndex--;
53234             }
53235             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53236                 return false;
53237             }
53238             cm.setLocked(oldIndex, locked, true);
53239             cm.moveColumn(oldIndex, newIndex);
53240             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53241             return true;
53242         }
53243         return false;
53244     }
53245 });
53246 /*
53247  * Based on:
53248  * Ext JS Library 1.1.1
53249  * Copyright(c) 2006-2007, Ext JS, LLC.
53250  *
53251  * Originally Released Under LGPL - original licence link has changed is not relivant.
53252  *
53253  * Fork - LGPL
53254  * <script type="text/javascript">
53255  */
53256   
53257 /**
53258  * @class Roo.grid.GridView
53259  * @extends Roo.util.Observable
53260  *
53261  * @constructor
53262  * @param {Object} config
53263  */
53264 Roo.grid.GridView = function(config){
53265     Roo.grid.GridView.superclass.constructor.call(this);
53266     this.el = null;
53267
53268     Roo.apply(this, config);
53269 };
53270
53271 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53272
53273     unselectable :  'unselectable="on"',
53274     unselectableCls :  'x-unselectable',
53275     
53276     
53277     rowClass : "x-grid-row",
53278
53279     cellClass : "x-grid-col",
53280
53281     tdClass : "x-grid-td",
53282
53283     hdClass : "x-grid-hd",
53284
53285     splitClass : "x-grid-split",
53286
53287     sortClasses : ["sort-asc", "sort-desc"],
53288
53289     enableMoveAnim : false,
53290
53291     hlColor: "C3DAF9",
53292
53293     dh : Roo.DomHelper,
53294
53295     fly : Roo.Element.fly,
53296
53297     css : Roo.util.CSS,
53298
53299     borderWidth: 1,
53300
53301     splitOffset: 3,
53302
53303     scrollIncrement : 22,
53304
53305     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53306
53307     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53308
53309     bind : function(ds, cm){
53310         if(this.ds){
53311             this.ds.un("load", this.onLoad, this);
53312             this.ds.un("datachanged", this.onDataChange, this);
53313             this.ds.un("add", this.onAdd, this);
53314             this.ds.un("remove", this.onRemove, this);
53315             this.ds.un("update", this.onUpdate, this);
53316             this.ds.un("clear", this.onClear, this);
53317         }
53318         if(ds){
53319             ds.on("load", this.onLoad, this);
53320             ds.on("datachanged", this.onDataChange, this);
53321             ds.on("add", this.onAdd, this);
53322             ds.on("remove", this.onRemove, this);
53323             ds.on("update", this.onUpdate, this);
53324             ds.on("clear", this.onClear, this);
53325         }
53326         this.ds = ds;
53327
53328         if(this.cm){
53329             this.cm.un("widthchange", this.onColWidthChange, this);
53330             this.cm.un("headerchange", this.onHeaderChange, this);
53331             this.cm.un("hiddenchange", this.onHiddenChange, this);
53332             this.cm.un("columnmoved", this.onColumnMove, this);
53333             this.cm.un("columnlockchange", this.onColumnLock, this);
53334         }
53335         if(cm){
53336             this.generateRules(cm);
53337             cm.on("widthchange", this.onColWidthChange, this);
53338             cm.on("headerchange", this.onHeaderChange, this);
53339             cm.on("hiddenchange", this.onHiddenChange, this);
53340             cm.on("columnmoved", this.onColumnMove, this);
53341             cm.on("columnlockchange", this.onColumnLock, this);
53342         }
53343         this.cm = cm;
53344     },
53345
53346     init: function(grid){
53347         Roo.grid.GridView.superclass.init.call(this, grid);
53348
53349         this.bind(grid.dataSource, grid.colModel);
53350
53351         grid.on("headerclick", this.handleHeaderClick, this);
53352
53353         if(grid.trackMouseOver){
53354             grid.on("mouseover", this.onRowOver, this);
53355             grid.on("mouseout", this.onRowOut, this);
53356         }
53357         grid.cancelTextSelection = function(){};
53358         this.gridId = grid.id;
53359
53360         var tpls = this.templates || {};
53361
53362         if(!tpls.master){
53363             tpls.master = new Roo.Template(
53364                '<div class="x-grid" hidefocus="true">',
53365                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53366                   '<div class="x-grid-topbar"></div>',
53367                   '<div class="x-grid-scroller"><div></div></div>',
53368                   '<div class="x-grid-locked">',
53369                       '<div class="x-grid-header">{lockedHeader}</div>',
53370                       '<div class="x-grid-body">{lockedBody}</div>',
53371                   "</div>",
53372                   '<div class="x-grid-viewport">',
53373                       '<div class="x-grid-header">{header}</div>',
53374                       '<div class="x-grid-body">{body}</div>',
53375                   "</div>",
53376                   '<div class="x-grid-bottombar"></div>',
53377                  
53378                   '<div class="x-grid-resize-proxy">&#160;</div>',
53379                "</div>"
53380             );
53381             tpls.master.disableformats = true;
53382         }
53383
53384         if(!tpls.header){
53385             tpls.header = new Roo.Template(
53386                '<table border="0" cellspacing="0" cellpadding="0">',
53387                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53388                "</table>{splits}"
53389             );
53390             tpls.header.disableformats = true;
53391         }
53392         tpls.header.compile();
53393
53394         if(!tpls.hcell){
53395             tpls.hcell = new Roo.Template(
53396                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53397                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53398                 "</div></td>"
53399              );
53400              tpls.hcell.disableFormats = true;
53401         }
53402         tpls.hcell.compile();
53403
53404         if(!tpls.hsplit){
53405             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53406                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53407             tpls.hsplit.disableFormats = true;
53408         }
53409         tpls.hsplit.compile();
53410
53411         if(!tpls.body){
53412             tpls.body = new Roo.Template(
53413                '<table border="0" cellspacing="0" cellpadding="0">',
53414                "<tbody>{rows}</tbody>",
53415                "</table>"
53416             );
53417             tpls.body.disableFormats = true;
53418         }
53419         tpls.body.compile();
53420
53421         if(!tpls.row){
53422             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53423             tpls.row.disableFormats = true;
53424         }
53425         tpls.row.compile();
53426
53427         if(!tpls.cell){
53428             tpls.cell = new Roo.Template(
53429                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53430                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53431                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53432                 "</td>"
53433             );
53434             tpls.cell.disableFormats = true;
53435         }
53436         tpls.cell.compile();
53437
53438         this.templates = tpls;
53439     },
53440
53441     // remap these for backwards compat
53442     onColWidthChange : function(){
53443         this.updateColumns.apply(this, arguments);
53444     },
53445     onHeaderChange : function(){
53446         this.updateHeaders.apply(this, arguments);
53447     }, 
53448     onHiddenChange : function(){
53449         this.handleHiddenChange.apply(this, arguments);
53450     },
53451     onColumnMove : function(){
53452         this.handleColumnMove.apply(this, arguments);
53453     },
53454     onColumnLock : function(){
53455         this.handleLockChange.apply(this, arguments);
53456     },
53457
53458     onDataChange : function(){
53459         this.refresh();
53460         this.updateHeaderSortState();
53461     },
53462
53463     onClear : function(){
53464         this.refresh();
53465     },
53466
53467     onUpdate : function(ds, record){
53468         this.refreshRow(record);
53469     },
53470
53471     refreshRow : function(record){
53472         var ds = this.ds, index;
53473         if(typeof record == 'number'){
53474             index = record;
53475             record = ds.getAt(index);
53476         }else{
53477             index = ds.indexOf(record);
53478         }
53479         this.insertRows(ds, index, index, true);
53480         this.onRemove(ds, record, index+1, true);
53481         this.syncRowHeights(index, index);
53482         this.layout();
53483         this.fireEvent("rowupdated", this, index, record);
53484     },
53485
53486     onAdd : function(ds, records, index){
53487         this.insertRows(ds, index, index + (records.length-1));
53488     },
53489
53490     onRemove : function(ds, record, index, isUpdate){
53491         if(isUpdate !== true){
53492             this.fireEvent("beforerowremoved", this, index, record);
53493         }
53494         var bt = this.getBodyTable(), lt = this.getLockedTable();
53495         if(bt.rows[index]){
53496             bt.firstChild.removeChild(bt.rows[index]);
53497         }
53498         if(lt.rows[index]){
53499             lt.firstChild.removeChild(lt.rows[index]);
53500         }
53501         if(isUpdate !== true){
53502             this.stripeRows(index);
53503             this.syncRowHeights(index, index);
53504             this.layout();
53505             this.fireEvent("rowremoved", this, index, record);
53506         }
53507     },
53508
53509     onLoad : function(){
53510         this.scrollToTop();
53511     },
53512
53513     /**
53514      * Scrolls the grid to the top
53515      */
53516     scrollToTop : function(){
53517         if(this.scroller){
53518             this.scroller.dom.scrollTop = 0;
53519             this.syncScroll();
53520         }
53521     },
53522
53523     /**
53524      * Gets a panel in the header of the grid that can be used for toolbars etc.
53525      * After modifying the contents of this panel a call to grid.autoSize() may be
53526      * required to register any changes in size.
53527      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53528      * @return Roo.Element
53529      */
53530     getHeaderPanel : function(doShow){
53531         if(doShow){
53532             this.headerPanel.show();
53533         }
53534         return this.headerPanel;
53535     },
53536
53537     /**
53538      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53539      * After modifying the contents of this panel a call to grid.autoSize() may be
53540      * required to register any changes in size.
53541      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53542      * @return Roo.Element
53543      */
53544     getFooterPanel : function(doShow){
53545         if(doShow){
53546             this.footerPanel.show();
53547         }
53548         return this.footerPanel;
53549     },
53550
53551     initElements : function(){
53552         var E = Roo.Element;
53553         var el = this.grid.getGridEl().dom.firstChild;
53554         var cs = el.childNodes;
53555
53556         this.el = new E(el);
53557         
53558          this.focusEl = new E(el.firstChild);
53559         this.focusEl.swallowEvent("click", true);
53560         
53561         this.headerPanel = new E(cs[1]);
53562         this.headerPanel.enableDisplayMode("block");
53563
53564         this.scroller = new E(cs[2]);
53565         this.scrollSizer = new E(this.scroller.dom.firstChild);
53566
53567         this.lockedWrap = new E(cs[3]);
53568         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53569         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53570
53571         this.mainWrap = new E(cs[4]);
53572         this.mainHd = new E(this.mainWrap.dom.firstChild);
53573         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53574
53575         this.footerPanel = new E(cs[5]);
53576         this.footerPanel.enableDisplayMode("block");
53577
53578         this.resizeProxy = new E(cs[6]);
53579
53580         this.headerSelector = String.format(
53581            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53582            this.lockedHd.id, this.mainHd.id
53583         );
53584
53585         this.splitterSelector = String.format(
53586            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53587            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53588         );
53589     },
53590     idToCssName : function(s)
53591     {
53592         return s.replace(/[^a-z0-9]+/ig, '-');
53593     },
53594
53595     getHeaderCell : function(index){
53596         return Roo.DomQuery.select(this.headerSelector)[index];
53597     },
53598
53599     getHeaderCellMeasure : function(index){
53600         return this.getHeaderCell(index).firstChild;
53601     },
53602
53603     getHeaderCellText : function(index){
53604         return this.getHeaderCell(index).firstChild.firstChild;
53605     },
53606
53607     getLockedTable : function(){
53608         return this.lockedBody.dom.firstChild;
53609     },
53610
53611     getBodyTable : function(){
53612         return this.mainBody.dom.firstChild;
53613     },
53614
53615     getLockedRow : function(index){
53616         return this.getLockedTable().rows[index];
53617     },
53618
53619     getRow : function(index){
53620         return this.getBodyTable().rows[index];
53621     },
53622
53623     getRowComposite : function(index){
53624         if(!this.rowEl){
53625             this.rowEl = new Roo.CompositeElementLite();
53626         }
53627         var els = [], lrow, mrow;
53628         if(lrow = this.getLockedRow(index)){
53629             els.push(lrow);
53630         }
53631         if(mrow = this.getRow(index)){
53632             els.push(mrow);
53633         }
53634         this.rowEl.elements = els;
53635         return this.rowEl;
53636     },
53637     /**
53638      * Gets the 'td' of the cell
53639      * 
53640      * @param {Integer} rowIndex row to select
53641      * @param {Integer} colIndex column to select
53642      * 
53643      * @return {Object} 
53644      */
53645     getCell : function(rowIndex, colIndex){
53646         var locked = this.cm.getLockedCount();
53647         var source;
53648         if(colIndex < locked){
53649             source = this.lockedBody.dom.firstChild;
53650         }else{
53651             source = this.mainBody.dom.firstChild;
53652             colIndex -= locked;
53653         }
53654         return source.rows[rowIndex].childNodes[colIndex];
53655     },
53656
53657     getCellText : function(rowIndex, colIndex){
53658         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53659     },
53660
53661     getCellBox : function(cell){
53662         var b = this.fly(cell).getBox();
53663         if(Roo.isOpera){ // opera fails to report the Y
53664             b.y = cell.offsetTop + this.mainBody.getY();
53665         }
53666         return b;
53667     },
53668
53669     getCellIndex : function(cell){
53670         var id = String(cell.className).match(this.cellRE);
53671         if(id){
53672             return parseInt(id[1], 10);
53673         }
53674         return 0;
53675     },
53676
53677     findHeaderIndex : function(n){
53678         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53679         return r ? this.getCellIndex(r) : false;
53680     },
53681
53682     findHeaderCell : function(n){
53683         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53684         return r ? r : false;
53685     },
53686
53687     findRowIndex : function(n){
53688         if(!n){
53689             return false;
53690         }
53691         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53692         return r ? r.rowIndex : false;
53693     },
53694
53695     findCellIndex : function(node){
53696         var stop = this.el.dom;
53697         while(node && node != stop){
53698             if(this.findRE.test(node.className)){
53699                 return this.getCellIndex(node);
53700             }
53701             node = node.parentNode;
53702         }
53703         return false;
53704     },
53705
53706     getColumnId : function(index){
53707         return this.cm.getColumnId(index);
53708     },
53709
53710     getSplitters : function()
53711     {
53712         if(this.splitterSelector){
53713            return Roo.DomQuery.select(this.splitterSelector);
53714         }else{
53715             return null;
53716       }
53717     },
53718
53719     getSplitter : function(index){
53720         return this.getSplitters()[index];
53721     },
53722
53723     onRowOver : function(e, t){
53724         var row;
53725         if((row = this.findRowIndex(t)) !== false){
53726             this.getRowComposite(row).addClass("x-grid-row-over");
53727         }
53728     },
53729
53730     onRowOut : function(e, t){
53731         var row;
53732         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53733             this.getRowComposite(row).removeClass("x-grid-row-over");
53734         }
53735     },
53736
53737     renderHeaders : function(){
53738         var cm = this.cm;
53739         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53740         var cb = [], lb = [], sb = [], lsb = [], p = {};
53741         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53742             p.cellId = "x-grid-hd-0-" + i;
53743             p.splitId = "x-grid-csplit-0-" + i;
53744             p.id = cm.getColumnId(i);
53745             p.title = cm.getColumnTooltip(i) || "";
53746             p.value = cm.getColumnHeader(i) || "";
53747             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53748             if(!cm.isLocked(i)){
53749                 cb[cb.length] = ct.apply(p);
53750                 sb[sb.length] = st.apply(p);
53751             }else{
53752                 lb[lb.length] = ct.apply(p);
53753                 lsb[lsb.length] = st.apply(p);
53754             }
53755         }
53756         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53757                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53758     },
53759
53760     updateHeaders : function(){
53761         var html = this.renderHeaders();
53762         this.lockedHd.update(html[0]);
53763         this.mainHd.update(html[1]);
53764     },
53765
53766     /**
53767      * Focuses the specified row.
53768      * @param {Number} row The row index
53769      */
53770     focusRow : function(row)
53771     {
53772         //Roo.log('GridView.focusRow');
53773         var x = this.scroller.dom.scrollLeft;
53774         this.focusCell(row, 0, false);
53775         this.scroller.dom.scrollLeft = x;
53776     },
53777
53778     /**
53779      * Focuses the specified cell.
53780      * @param {Number} row The row index
53781      * @param {Number} col The column index
53782      * @param {Boolean} hscroll false to disable horizontal scrolling
53783      */
53784     focusCell : function(row, col, hscroll)
53785     {
53786         //Roo.log('GridView.focusCell');
53787         var el = this.ensureVisible(row, col, hscroll);
53788         this.focusEl.alignTo(el, "tl-tl");
53789         if(Roo.isGecko){
53790             this.focusEl.focus();
53791         }else{
53792             this.focusEl.focus.defer(1, this.focusEl);
53793         }
53794     },
53795
53796     /**
53797      * Scrolls the specified cell into view
53798      * @param {Number} row The row index
53799      * @param {Number} col The column index
53800      * @param {Boolean} hscroll false to disable horizontal scrolling
53801      */
53802     ensureVisible : function(row, col, hscroll)
53803     {
53804         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53805         //return null; //disable for testing.
53806         if(typeof row != "number"){
53807             row = row.rowIndex;
53808         }
53809         if(row < 0 && row >= this.ds.getCount()){
53810             return  null;
53811         }
53812         col = (col !== undefined ? col : 0);
53813         var cm = this.grid.colModel;
53814         while(cm.isHidden(col)){
53815             col++;
53816         }
53817
53818         var el = this.getCell(row, col);
53819         if(!el){
53820             return null;
53821         }
53822         var c = this.scroller.dom;
53823
53824         var ctop = parseInt(el.offsetTop, 10);
53825         var cleft = parseInt(el.offsetLeft, 10);
53826         var cbot = ctop + el.offsetHeight;
53827         var cright = cleft + el.offsetWidth;
53828         
53829         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53830         var stop = parseInt(c.scrollTop, 10);
53831         var sleft = parseInt(c.scrollLeft, 10);
53832         var sbot = stop + ch;
53833         var sright = sleft + c.clientWidth;
53834         /*
53835         Roo.log('GridView.ensureVisible:' +
53836                 ' ctop:' + ctop +
53837                 ' c.clientHeight:' + c.clientHeight +
53838                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53839                 ' stop:' + stop +
53840                 ' cbot:' + cbot +
53841                 ' sbot:' + sbot +
53842                 ' ch:' + ch  
53843                 );
53844         */
53845         if(ctop < stop){
53846              c.scrollTop = ctop;
53847             //Roo.log("set scrolltop to ctop DISABLE?");
53848         }else if(cbot > sbot){
53849             //Roo.log("set scrolltop to cbot-ch");
53850             c.scrollTop = cbot-ch;
53851         }
53852         
53853         if(hscroll !== false){
53854             if(cleft < sleft){
53855                 c.scrollLeft = cleft;
53856             }else if(cright > sright){
53857                 c.scrollLeft = cright-c.clientWidth;
53858             }
53859         }
53860          
53861         return el;
53862     },
53863
53864     updateColumns : function(){
53865         this.grid.stopEditing();
53866         var cm = this.grid.colModel, colIds = this.getColumnIds();
53867         //var totalWidth = cm.getTotalWidth();
53868         var pos = 0;
53869         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53870             //if(cm.isHidden(i)) continue;
53871             var w = cm.getColumnWidth(i);
53872             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53873             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53874         }
53875         this.updateSplitters();
53876     },
53877
53878     generateRules : function(cm){
53879         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53880         Roo.util.CSS.removeStyleSheet(rulesId);
53881         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53882             var cid = cm.getColumnId(i);
53883             var align = '';
53884             if(cm.config[i].align){
53885                 align = 'text-align:'+cm.config[i].align+';';
53886             }
53887             var hidden = '';
53888             if(cm.isHidden(i)){
53889                 hidden = 'display:none;';
53890             }
53891             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53892             ruleBuf.push(
53893                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53894                     this.hdSelector, cid, " {\n", align, width, "}\n",
53895                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53896                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53897         }
53898         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53899     },
53900
53901     updateSplitters : function(){
53902         var cm = this.cm, s = this.getSplitters();
53903         if(s){ // splitters not created yet
53904             var pos = 0, locked = true;
53905             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53906                 if(cm.isHidden(i)) continue;
53907                 var w = cm.getColumnWidth(i); // make sure it's a number
53908                 if(!cm.isLocked(i) && locked){
53909                     pos = 0;
53910                     locked = false;
53911                 }
53912                 pos += w;
53913                 s[i].style.left = (pos-this.splitOffset) + "px";
53914             }
53915         }
53916     },
53917
53918     handleHiddenChange : function(colModel, colIndex, hidden){
53919         if(hidden){
53920             this.hideColumn(colIndex);
53921         }else{
53922             this.unhideColumn(colIndex);
53923         }
53924     },
53925
53926     hideColumn : function(colIndex){
53927         var cid = this.getColumnId(colIndex);
53928         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53929         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53930         if(Roo.isSafari){
53931             this.updateHeaders();
53932         }
53933         this.updateSplitters();
53934         this.layout();
53935     },
53936
53937     unhideColumn : function(colIndex){
53938         var cid = this.getColumnId(colIndex);
53939         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53940         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53941
53942         if(Roo.isSafari){
53943             this.updateHeaders();
53944         }
53945         this.updateSplitters();
53946         this.layout();
53947     },
53948
53949     insertRows : function(dm, firstRow, lastRow, isUpdate){
53950         if(firstRow == 0 && lastRow == dm.getCount()-1){
53951             this.refresh();
53952         }else{
53953             if(!isUpdate){
53954                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53955             }
53956             var s = this.getScrollState();
53957             var markup = this.renderRows(firstRow, lastRow);
53958             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53959             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53960             this.restoreScroll(s);
53961             if(!isUpdate){
53962                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53963                 this.syncRowHeights(firstRow, lastRow);
53964                 this.stripeRows(firstRow);
53965                 this.layout();
53966             }
53967         }
53968     },
53969
53970     bufferRows : function(markup, target, index){
53971         var before = null, trows = target.rows, tbody = target.tBodies[0];
53972         if(index < trows.length){
53973             before = trows[index];
53974         }
53975         var b = document.createElement("div");
53976         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53977         var rows = b.firstChild.rows;
53978         for(var i = 0, len = rows.length; i < len; i++){
53979             if(before){
53980                 tbody.insertBefore(rows[0], before);
53981             }else{
53982                 tbody.appendChild(rows[0]);
53983             }
53984         }
53985         b.innerHTML = "";
53986         b = null;
53987     },
53988
53989     deleteRows : function(dm, firstRow, lastRow){
53990         if(dm.getRowCount()<1){
53991             this.fireEvent("beforerefresh", this);
53992             this.mainBody.update("");
53993             this.lockedBody.update("");
53994             this.fireEvent("refresh", this);
53995         }else{
53996             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53997             var bt = this.getBodyTable();
53998             var tbody = bt.firstChild;
53999             var rows = bt.rows;
54000             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54001                 tbody.removeChild(rows[firstRow]);
54002             }
54003             this.stripeRows(firstRow);
54004             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54005         }
54006     },
54007
54008     updateRows : function(dataSource, firstRow, lastRow){
54009         var s = this.getScrollState();
54010         this.refresh();
54011         this.restoreScroll(s);
54012     },
54013
54014     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54015         if(!noRefresh){
54016            this.refresh();
54017         }
54018         this.updateHeaderSortState();
54019     },
54020
54021     getScrollState : function(){
54022         
54023         var sb = this.scroller.dom;
54024         return {left: sb.scrollLeft, top: sb.scrollTop};
54025     },
54026
54027     stripeRows : function(startRow){
54028         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54029             return;
54030         }
54031         startRow = startRow || 0;
54032         var rows = this.getBodyTable().rows;
54033         var lrows = this.getLockedTable().rows;
54034         var cls = ' x-grid-row-alt ';
54035         for(var i = startRow, len = rows.length; i < len; i++){
54036             var row = rows[i], lrow = lrows[i];
54037             var isAlt = ((i+1) % 2 == 0);
54038             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54039             if(isAlt == hasAlt){
54040                 continue;
54041             }
54042             if(isAlt){
54043                 row.className += " x-grid-row-alt";
54044             }else{
54045                 row.className = row.className.replace("x-grid-row-alt", "");
54046             }
54047             if(lrow){
54048                 lrow.className = row.className;
54049             }
54050         }
54051     },
54052
54053     restoreScroll : function(state){
54054         //Roo.log('GridView.restoreScroll');
54055         var sb = this.scroller.dom;
54056         sb.scrollLeft = state.left;
54057         sb.scrollTop = state.top;
54058         this.syncScroll();
54059     },
54060
54061     syncScroll : function(){
54062         //Roo.log('GridView.syncScroll');
54063         var sb = this.scroller.dom;
54064         var sh = this.mainHd.dom;
54065         var bs = this.mainBody.dom;
54066         var lv = this.lockedBody.dom;
54067         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54068         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54069     },
54070
54071     handleScroll : function(e){
54072         this.syncScroll();
54073         var sb = this.scroller.dom;
54074         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54075         e.stopEvent();
54076     },
54077
54078     handleWheel : function(e){
54079         var d = e.getWheelDelta();
54080         this.scroller.dom.scrollTop -= d*22;
54081         // set this here to prevent jumpy scrolling on large tables
54082         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54083         e.stopEvent();
54084     },
54085
54086     renderRows : function(startRow, endRow){
54087         // pull in all the crap needed to render rows
54088         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54089         var colCount = cm.getColumnCount();
54090
54091         if(ds.getCount() < 1){
54092             return ["", ""];
54093         }
54094
54095         // build a map for all the columns
54096         var cs = [];
54097         for(var i = 0; i < colCount; i++){
54098             var name = cm.getDataIndex(i);
54099             cs[i] = {
54100                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54101                 renderer : cm.getRenderer(i),
54102                 id : cm.getColumnId(i),
54103                 locked : cm.isLocked(i)
54104             };
54105         }
54106
54107         startRow = startRow || 0;
54108         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54109
54110         // records to render
54111         var rs = ds.getRange(startRow, endRow);
54112
54113         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54114     },
54115
54116     // As much as I hate to duplicate code, this was branched because FireFox really hates
54117     // [].join("") on strings. The performance difference was substantial enough to
54118     // branch this function
54119     doRender : Roo.isGecko ?
54120             function(cs, rs, ds, startRow, colCount, stripe){
54121                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54122                 // buffers
54123                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54124                 
54125                 var hasListener = this.grid.hasListener('rowclass');
54126                 var rowcfg = {};
54127                 for(var j = 0, len = rs.length; j < len; j++){
54128                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54129                     for(var i = 0; i < colCount; i++){
54130                         c = cs[i];
54131                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54132                         p.id = c.id;
54133                         p.css = p.attr = "";
54134                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54135                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54136                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54137                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54138                         }
54139                         var markup = ct.apply(p);
54140                         if(!c.locked){
54141                             cb+= markup;
54142                         }else{
54143                             lcb+= markup;
54144                         }
54145                     }
54146                     var alt = [];
54147                     if(stripe && ((rowIndex+1) % 2 == 0)){
54148                         alt.push("x-grid-row-alt")
54149                     }
54150                     if(r.dirty){
54151                         alt.push(  " x-grid-dirty-row");
54152                     }
54153                     rp.cells = lcb;
54154                     if(this.getRowClass){
54155                         alt.push(this.getRowClass(r, rowIndex));
54156                     }
54157                     if (hasListener) {
54158                         rowcfg = {
54159                              
54160                             record: r,
54161                             rowIndex : rowIndex,
54162                             rowClass : ''
54163                         }
54164                         this.grid.fireEvent('rowclass', this, rowcfg);
54165                         alt.push(rowcfg.rowClass);
54166                     }
54167                     rp.alt = alt.join(" ");
54168                     lbuf+= rt.apply(rp);
54169                     rp.cells = cb;
54170                     buf+=  rt.apply(rp);
54171                 }
54172                 return [lbuf, buf];
54173             } :
54174             function(cs, rs, ds, startRow, colCount, stripe){
54175                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54176                 // buffers
54177                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54178                 var hasListener = this.grid.hasListener('rowclass');
54179  
54180                 var rowcfg = {};
54181                 for(var j = 0, len = rs.length; j < len; j++){
54182                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54183                     for(var i = 0; i < colCount; i++){
54184                         c = cs[i];
54185                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54186                         p.id = c.id;
54187                         p.css = p.attr = "";
54188                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54189                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54190                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54191                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54192                         }
54193                         
54194                         var markup = ct.apply(p);
54195                         if(!c.locked){
54196                             cb[cb.length] = markup;
54197                         }else{
54198                             lcb[lcb.length] = markup;
54199                         }
54200                     }
54201                     var alt = [];
54202                     if(stripe && ((rowIndex+1) % 2 == 0)){
54203                         alt.push( "x-grid-row-alt");
54204                     }
54205                     if(r.dirty){
54206                         alt.push(" x-grid-dirty-row");
54207                     }
54208                     rp.cells = lcb;
54209                     if(this.getRowClass){
54210                         alt.push( this.getRowClass(r, rowIndex));
54211                     }
54212                     if (hasListener) {
54213                         rowcfg = {
54214                              
54215                             record: r,
54216                             rowIndex : rowIndex,
54217                             rowClass : ''
54218                         }
54219                         this.grid.fireEvent('rowclass', this, rowcfg);
54220                         alt.push(rowcfg.rowClass);
54221                     }
54222                     rp.alt = alt.join(" ");
54223                     rp.cells = lcb.join("");
54224                     lbuf[lbuf.length] = rt.apply(rp);
54225                     rp.cells = cb.join("");
54226                     buf[buf.length] =  rt.apply(rp);
54227                 }
54228                 return [lbuf.join(""), buf.join("")];
54229             },
54230
54231     renderBody : function(){
54232         var markup = this.renderRows();
54233         var bt = this.templates.body;
54234         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54235     },
54236
54237     /**
54238      * Refreshes the grid
54239      * @param {Boolean} headersToo
54240      */
54241     refresh : function(headersToo){
54242         this.fireEvent("beforerefresh", this);
54243         this.grid.stopEditing();
54244         var result = this.renderBody();
54245         this.lockedBody.update(result[0]);
54246         this.mainBody.update(result[1]);
54247         if(headersToo === true){
54248             this.updateHeaders();
54249             this.updateColumns();
54250             this.updateSplitters();
54251             this.updateHeaderSortState();
54252         }
54253         this.syncRowHeights();
54254         this.layout();
54255         this.fireEvent("refresh", this);
54256     },
54257
54258     handleColumnMove : function(cm, oldIndex, newIndex){
54259         this.indexMap = null;
54260         var s = this.getScrollState();
54261         this.refresh(true);
54262         this.restoreScroll(s);
54263         this.afterMove(newIndex);
54264     },
54265
54266     afterMove : function(colIndex){
54267         if(this.enableMoveAnim && Roo.enableFx){
54268             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54269         }
54270         // if multisort - fix sortOrder, and reload..
54271         if (this.grid.dataSource.multiSort) {
54272             // the we can call sort again..
54273             var dm = this.grid.dataSource;
54274             var cm = this.grid.colModel;
54275             var so = [];
54276             for(var i = 0; i < cm.config.length; i++ ) {
54277                 
54278                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54279                     continue; // dont' bother, it's not in sort list or being set.
54280                 }
54281                 
54282                 so.push(cm.config[i].dataIndex);
54283             };
54284             dm.sortOrder = so;
54285             dm.load(dm.lastOptions);
54286             
54287             
54288         }
54289         
54290     },
54291
54292     updateCell : function(dm, rowIndex, dataIndex){
54293         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54294         if(typeof colIndex == "undefined"){ // not present in grid
54295             return;
54296         }
54297         var cm = this.grid.colModel;
54298         var cell = this.getCell(rowIndex, colIndex);
54299         var cellText = this.getCellText(rowIndex, colIndex);
54300
54301         var p = {
54302             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54303             id : cm.getColumnId(colIndex),
54304             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54305         };
54306         var renderer = cm.getRenderer(colIndex);
54307         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54308         if(typeof val == "undefined" || val === "") val = "&#160;";
54309         cellText.innerHTML = val;
54310         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54311         this.syncRowHeights(rowIndex, rowIndex);
54312     },
54313
54314     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54315         var maxWidth = 0;
54316         if(this.grid.autoSizeHeaders){
54317             var h = this.getHeaderCellMeasure(colIndex);
54318             maxWidth = Math.max(maxWidth, h.scrollWidth);
54319         }
54320         var tb, index;
54321         if(this.cm.isLocked(colIndex)){
54322             tb = this.getLockedTable();
54323             index = colIndex;
54324         }else{
54325             tb = this.getBodyTable();
54326             index = colIndex - this.cm.getLockedCount();
54327         }
54328         if(tb && tb.rows){
54329             var rows = tb.rows;
54330             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54331             for(var i = 0; i < stopIndex; i++){
54332                 var cell = rows[i].childNodes[index].firstChild;
54333                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54334             }
54335         }
54336         return maxWidth + /*margin for error in IE*/ 5;
54337     },
54338     /**
54339      * Autofit a column to its content.
54340      * @param {Number} colIndex
54341      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54342      */
54343      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54344          if(this.cm.isHidden(colIndex)){
54345              return; // can't calc a hidden column
54346          }
54347         if(forceMinSize){
54348             var cid = this.cm.getColumnId(colIndex);
54349             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54350            if(this.grid.autoSizeHeaders){
54351                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54352            }
54353         }
54354         var newWidth = this.calcColumnWidth(colIndex);
54355         this.cm.setColumnWidth(colIndex,
54356             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54357         if(!suppressEvent){
54358             this.grid.fireEvent("columnresize", colIndex, newWidth);
54359         }
54360     },
54361
54362     /**
54363      * Autofits all columns to their content and then expands to fit any extra space in the grid
54364      */
54365      autoSizeColumns : function(){
54366         var cm = this.grid.colModel;
54367         var colCount = cm.getColumnCount();
54368         for(var i = 0; i < colCount; i++){
54369             this.autoSizeColumn(i, true, true);
54370         }
54371         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54372             this.fitColumns();
54373         }else{
54374             this.updateColumns();
54375             this.layout();
54376         }
54377     },
54378
54379     /**
54380      * Autofits all columns to the grid's width proportionate with their current size
54381      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54382      */
54383     fitColumns : function(reserveScrollSpace){
54384         var cm = this.grid.colModel;
54385         var colCount = cm.getColumnCount();
54386         var cols = [];
54387         var width = 0;
54388         var i, w;
54389         for (i = 0; i < colCount; i++){
54390             if(!cm.isHidden(i) && !cm.isFixed(i)){
54391                 w = cm.getColumnWidth(i);
54392                 cols.push(i);
54393                 cols.push(w);
54394                 width += w;
54395             }
54396         }
54397         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54398         if(reserveScrollSpace){
54399             avail -= 17;
54400         }
54401         var frac = (avail - cm.getTotalWidth())/width;
54402         while (cols.length){
54403             w = cols.pop();
54404             i = cols.pop();
54405             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54406         }
54407         this.updateColumns();
54408         this.layout();
54409     },
54410
54411     onRowSelect : function(rowIndex){
54412         var row = this.getRowComposite(rowIndex);
54413         row.addClass("x-grid-row-selected");
54414     },
54415
54416     onRowDeselect : function(rowIndex){
54417         var row = this.getRowComposite(rowIndex);
54418         row.removeClass("x-grid-row-selected");
54419     },
54420
54421     onCellSelect : function(row, col){
54422         var cell = this.getCell(row, col);
54423         if(cell){
54424             Roo.fly(cell).addClass("x-grid-cell-selected");
54425         }
54426     },
54427
54428     onCellDeselect : function(row, col){
54429         var cell = this.getCell(row, col);
54430         if(cell){
54431             Roo.fly(cell).removeClass("x-grid-cell-selected");
54432         }
54433     },
54434
54435     updateHeaderSortState : function(){
54436         
54437         // sort state can be single { field: xxx, direction : yyy}
54438         // or   { xxx=>ASC , yyy : DESC ..... }
54439         
54440         var mstate = {};
54441         if (!this.ds.multiSort) { 
54442             var state = this.ds.getSortState();
54443             if(!state){
54444                 return;
54445             }
54446             mstate[state.field] = state.direction;
54447             // FIXME... - this is not used here.. but might be elsewhere..
54448             this.sortState = state;
54449             
54450         } else {
54451             mstate = this.ds.sortToggle;
54452         }
54453         //remove existing sort classes..
54454         
54455         var sc = this.sortClasses;
54456         var hds = this.el.select(this.headerSelector).removeClass(sc);
54457         
54458         for(var f in mstate) {
54459         
54460             var sortColumn = this.cm.findColumnIndex(f);
54461             
54462             if(sortColumn != -1){
54463                 var sortDir = mstate[f];        
54464                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54465             }
54466         }
54467         
54468          
54469         
54470     },
54471
54472
54473     handleHeaderClick : function(g, index,e){
54474         
54475         Roo.log("header click");
54476         
54477         if (Roo.isTouch) {
54478             // touch events on header are handled by context
54479             this.handleHdCtx(g,index,e);
54480             return;
54481         }
54482         
54483         
54484         if(this.headersDisabled){
54485             return;
54486         }
54487         var dm = g.dataSource, cm = g.colModel;
54488         if(!cm.isSortable(index)){
54489             return;
54490         }
54491         g.stopEditing();
54492         
54493         if (dm.multiSort) {
54494             // update the sortOrder
54495             var so = [];
54496             for(var i = 0; i < cm.config.length; i++ ) {
54497                 
54498                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54499                     continue; // dont' bother, it's not in sort list or being set.
54500                 }
54501                 
54502                 so.push(cm.config[i].dataIndex);
54503             };
54504             dm.sortOrder = so;
54505         }
54506         
54507         
54508         dm.sort(cm.getDataIndex(index));
54509     },
54510
54511
54512     destroy : function(){
54513         if(this.colMenu){
54514             this.colMenu.removeAll();
54515             Roo.menu.MenuMgr.unregister(this.colMenu);
54516             this.colMenu.getEl().remove();
54517             delete this.colMenu;
54518         }
54519         if(this.hmenu){
54520             this.hmenu.removeAll();
54521             Roo.menu.MenuMgr.unregister(this.hmenu);
54522             this.hmenu.getEl().remove();
54523             delete this.hmenu;
54524         }
54525         if(this.grid.enableColumnMove){
54526             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54527             if(dds){
54528                 for(var dd in dds){
54529                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54530                         var elid = dds[dd].dragElId;
54531                         dds[dd].unreg();
54532                         Roo.get(elid).remove();
54533                     } else if(dds[dd].config.isTarget){
54534                         dds[dd].proxyTop.remove();
54535                         dds[dd].proxyBottom.remove();
54536                         dds[dd].unreg();
54537                     }
54538                     if(Roo.dd.DDM.locationCache[dd]){
54539                         delete Roo.dd.DDM.locationCache[dd];
54540                     }
54541                 }
54542                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54543             }
54544         }
54545         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54546         this.bind(null, null);
54547         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54548     },
54549
54550     handleLockChange : function(){
54551         this.refresh(true);
54552     },
54553
54554     onDenyColumnLock : function(){
54555
54556     },
54557
54558     onDenyColumnHide : function(){
54559
54560     },
54561
54562     handleHdMenuClick : function(item){
54563         var index = this.hdCtxIndex;
54564         var cm = this.cm, ds = this.ds;
54565         switch(item.id){
54566             case "asc":
54567                 ds.sort(cm.getDataIndex(index), "ASC");
54568                 break;
54569             case "desc":
54570                 ds.sort(cm.getDataIndex(index), "DESC");
54571                 break;
54572             case "lock":
54573                 var lc = cm.getLockedCount();
54574                 if(cm.getColumnCount(true) <= lc+1){
54575                     this.onDenyColumnLock();
54576                     return;
54577                 }
54578                 if(lc != index){
54579                     cm.setLocked(index, true, true);
54580                     cm.moveColumn(index, lc);
54581                     this.grid.fireEvent("columnmove", index, lc);
54582                 }else{
54583                     cm.setLocked(index, true);
54584                 }
54585             break;
54586             case "unlock":
54587                 var lc = cm.getLockedCount();
54588                 if((lc-1) != index){
54589                     cm.setLocked(index, false, true);
54590                     cm.moveColumn(index, lc-1);
54591                     this.grid.fireEvent("columnmove", index, lc-1);
54592                 }else{
54593                     cm.setLocked(index, false);
54594                 }
54595             break;
54596             case 'wider': // used to expand cols on touch..
54597             case 'narrow':
54598                 var cw = cm.getColumnWidth(index);
54599                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54600                 cw = Math.max(0, cw);
54601                 cw = Math.min(cw,4000);
54602                 cm.setColumnWidth(index, cw);
54603                 break;
54604                 
54605             default:
54606                 index = cm.getIndexById(item.id.substr(4));
54607                 if(index != -1){
54608                     if(item.checked && cm.getColumnCount(true) <= 1){
54609                         this.onDenyColumnHide();
54610                         return false;
54611                     }
54612                     cm.setHidden(index, item.checked);
54613                 }
54614         }
54615         return true;
54616     },
54617
54618     beforeColMenuShow : function(){
54619         var cm = this.cm,  colCount = cm.getColumnCount();
54620         this.colMenu.removeAll();
54621         for(var i = 0; i < colCount; i++){
54622             this.colMenu.add(new Roo.menu.CheckItem({
54623                 id: "col-"+cm.getColumnId(i),
54624                 text: cm.getColumnHeader(i),
54625                 checked: !cm.isHidden(i),
54626                 hideOnClick:false
54627             }));
54628         }
54629     },
54630
54631     handleHdCtx : function(g, index, e){
54632         e.stopEvent();
54633         var hd = this.getHeaderCell(index);
54634         this.hdCtxIndex = index;
54635         var ms = this.hmenu.items, cm = this.cm;
54636         ms.get("asc").setDisabled(!cm.isSortable(index));
54637         ms.get("desc").setDisabled(!cm.isSortable(index));
54638         if(this.grid.enableColLock !== false){
54639             ms.get("lock").setDisabled(cm.isLocked(index));
54640             ms.get("unlock").setDisabled(!cm.isLocked(index));
54641         }
54642         this.hmenu.show(hd, "tl-bl");
54643     },
54644
54645     handleHdOver : function(e){
54646         var hd = this.findHeaderCell(e.getTarget());
54647         if(hd && !this.headersDisabled){
54648             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54649                this.fly(hd).addClass("x-grid-hd-over");
54650             }
54651         }
54652     },
54653
54654     handleHdOut : function(e){
54655         var hd = this.findHeaderCell(e.getTarget());
54656         if(hd){
54657             this.fly(hd).removeClass("x-grid-hd-over");
54658         }
54659     },
54660
54661     handleSplitDblClick : function(e, t){
54662         var i = this.getCellIndex(t);
54663         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54664             this.autoSizeColumn(i, true);
54665             this.layout();
54666         }
54667     },
54668
54669     render : function(){
54670
54671         var cm = this.cm;
54672         var colCount = cm.getColumnCount();
54673
54674         if(this.grid.monitorWindowResize === true){
54675             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54676         }
54677         var header = this.renderHeaders();
54678         var body = this.templates.body.apply({rows:""});
54679         var html = this.templates.master.apply({
54680             lockedBody: body,
54681             body: body,
54682             lockedHeader: header[0],
54683             header: header[1]
54684         });
54685
54686         //this.updateColumns();
54687
54688         this.grid.getGridEl().dom.innerHTML = html;
54689
54690         this.initElements();
54691         
54692         // a kludge to fix the random scolling effect in webkit
54693         this.el.on("scroll", function() {
54694             this.el.dom.scrollTop=0; // hopefully not recursive..
54695         },this);
54696
54697         this.scroller.on("scroll", this.handleScroll, this);
54698         this.lockedBody.on("mousewheel", this.handleWheel, this);
54699         this.mainBody.on("mousewheel", this.handleWheel, this);
54700
54701         this.mainHd.on("mouseover", this.handleHdOver, this);
54702         this.mainHd.on("mouseout", this.handleHdOut, this);
54703         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54704                 {delegate: "."+this.splitClass});
54705
54706         this.lockedHd.on("mouseover", this.handleHdOver, this);
54707         this.lockedHd.on("mouseout", this.handleHdOut, this);
54708         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54709                 {delegate: "."+this.splitClass});
54710
54711         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54712             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54713         }
54714
54715         this.updateSplitters();
54716
54717         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54718             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54719             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54720         }
54721
54722         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54723             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54724             this.hmenu.add(
54725                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54726                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54727             );
54728             if(this.grid.enableColLock !== false){
54729                 this.hmenu.add('-',
54730                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54731                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54732                 );
54733             }
54734             if (Roo.isTouch) {
54735                  this.hmenu.add('-',
54736                     {id:"wider", text: this.columnsWiderText},
54737                     {id:"narrow", text: this.columnsNarrowText }
54738                 );
54739                 
54740                  
54741             }
54742             
54743             if(this.grid.enableColumnHide !== false){
54744
54745                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54746                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54747                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54748
54749                 this.hmenu.add('-',
54750                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54751                 );
54752             }
54753             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54754
54755             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54756         }
54757
54758         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54759             this.dd = new Roo.grid.GridDragZone(this.grid, {
54760                 ddGroup : this.grid.ddGroup || 'GridDD'
54761             });
54762             
54763         }
54764
54765         /*
54766         for(var i = 0; i < colCount; i++){
54767             if(cm.isHidden(i)){
54768                 this.hideColumn(i);
54769             }
54770             if(cm.config[i].align){
54771                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54772                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54773             }
54774         }*/
54775         
54776         this.updateHeaderSortState();
54777
54778         this.beforeInitialResize();
54779         this.layout(true);
54780
54781         // two part rendering gives faster view to the user
54782         this.renderPhase2.defer(1, this);
54783     },
54784
54785     renderPhase2 : function(){
54786         // render the rows now
54787         this.refresh();
54788         if(this.grid.autoSizeColumns){
54789             this.autoSizeColumns();
54790         }
54791     },
54792
54793     beforeInitialResize : function(){
54794
54795     },
54796
54797     onColumnSplitterMoved : function(i, w){
54798         this.userResized = true;
54799         var cm = this.grid.colModel;
54800         cm.setColumnWidth(i, w, true);
54801         var cid = cm.getColumnId(i);
54802         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54803         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54804         this.updateSplitters();
54805         this.layout();
54806         this.grid.fireEvent("columnresize", i, w);
54807     },
54808
54809     syncRowHeights : function(startIndex, endIndex){
54810         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54811             startIndex = startIndex || 0;
54812             var mrows = this.getBodyTable().rows;
54813             var lrows = this.getLockedTable().rows;
54814             var len = mrows.length-1;
54815             endIndex = Math.min(endIndex || len, len);
54816             for(var i = startIndex; i <= endIndex; i++){
54817                 var m = mrows[i], l = lrows[i];
54818                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54819                 m.style.height = l.style.height = h + "px";
54820             }
54821         }
54822     },
54823
54824     layout : function(initialRender, is2ndPass){
54825         var g = this.grid;
54826         var auto = g.autoHeight;
54827         var scrollOffset = 16;
54828         var c = g.getGridEl(), cm = this.cm,
54829                 expandCol = g.autoExpandColumn,
54830                 gv = this;
54831         //c.beginMeasure();
54832
54833         if(!c.dom.offsetWidth){ // display:none?
54834             if(initialRender){
54835                 this.lockedWrap.show();
54836                 this.mainWrap.show();
54837             }
54838             return;
54839         }
54840
54841         var hasLock = this.cm.isLocked(0);
54842
54843         var tbh = this.headerPanel.getHeight();
54844         var bbh = this.footerPanel.getHeight();
54845
54846         if(auto){
54847             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54848             var newHeight = ch + c.getBorderWidth("tb");
54849             if(g.maxHeight){
54850                 newHeight = Math.min(g.maxHeight, newHeight);
54851             }
54852             c.setHeight(newHeight);
54853         }
54854
54855         if(g.autoWidth){
54856             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54857         }
54858
54859         var s = this.scroller;
54860
54861         var csize = c.getSize(true);
54862
54863         this.el.setSize(csize.width, csize.height);
54864
54865         this.headerPanel.setWidth(csize.width);
54866         this.footerPanel.setWidth(csize.width);
54867
54868         var hdHeight = this.mainHd.getHeight();
54869         var vw = csize.width;
54870         var vh = csize.height - (tbh + bbh);
54871
54872         s.setSize(vw, vh);
54873
54874         var bt = this.getBodyTable();
54875         var ltWidth = hasLock ?
54876                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54877
54878         var scrollHeight = bt.offsetHeight;
54879         var scrollWidth = ltWidth + bt.offsetWidth;
54880         var vscroll = false, hscroll = false;
54881
54882         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54883
54884         var lw = this.lockedWrap, mw = this.mainWrap;
54885         var lb = this.lockedBody, mb = this.mainBody;
54886
54887         setTimeout(function(){
54888             var t = s.dom.offsetTop;
54889             var w = s.dom.clientWidth,
54890                 h = s.dom.clientHeight;
54891
54892             lw.setTop(t);
54893             lw.setSize(ltWidth, h);
54894
54895             mw.setLeftTop(ltWidth, t);
54896             mw.setSize(w-ltWidth, h);
54897
54898             lb.setHeight(h-hdHeight);
54899             mb.setHeight(h-hdHeight);
54900
54901             if(is2ndPass !== true && !gv.userResized && expandCol){
54902                 // high speed resize without full column calculation
54903                 
54904                 var ci = cm.getIndexById(expandCol);
54905                 if (ci < 0) {
54906                     ci = cm.findColumnIndex(expandCol);
54907                 }
54908                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54909                 var expandId = cm.getColumnId(ci);
54910                 var  tw = cm.getTotalWidth(false);
54911                 var currentWidth = cm.getColumnWidth(ci);
54912                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54913                 if(currentWidth != cw){
54914                     cm.setColumnWidth(ci, cw, true);
54915                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54916                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54917                     gv.updateSplitters();
54918                     gv.layout(false, true);
54919                 }
54920             }
54921
54922             if(initialRender){
54923                 lw.show();
54924                 mw.show();
54925             }
54926             //c.endMeasure();
54927         }, 10);
54928     },
54929
54930     onWindowResize : function(){
54931         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54932             return;
54933         }
54934         this.layout();
54935     },
54936
54937     appendFooter : function(parentEl){
54938         return null;
54939     },
54940
54941     sortAscText : "Sort Ascending",
54942     sortDescText : "Sort Descending",
54943     lockText : "Lock Column",
54944     unlockText : "Unlock Column",
54945     columnsText : "Columns",
54946  
54947     columnsWiderText : "Wider",
54948     columnsNarrowText : "Thinner"
54949 });
54950
54951
54952 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54953     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54954     this.proxy.el.addClass('x-grid3-col-dd');
54955 };
54956
54957 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54958     handleMouseDown : function(e){
54959
54960     },
54961
54962     callHandleMouseDown : function(e){
54963         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54964     }
54965 });
54966 /*
54967  * Based on:
54968  * Ext JS Library 1.1.1
54969  * Copyright(c) 2006-2007, Ext JS, LLC.
54970  *
54971  * Originally Released Under LGPL - original licence link has changed is not relivant.
54972  *
54973  * Fork - LGPL
54974  * <script type="text/javascript">
54975  */
54976  
54977 // private
54978 // This is a support class used internally by the Grid components
54979 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54980     this.grid = grid;
54981     this.view = grid.getView();
54982     this.proxy = this.view.resizeProxy;
54983     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54984         "gridSplitters" + this.grid.getGridEl().id, {
54985         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54986     });
54987     this.setHandleElId(Roo.id(hd));
54988     this.setOuterHandleElId(Roo.id(hd2));
54989     this.scroll = false;
54990 };
54991 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54992     fly: Roo.Element.fly,
54993
54994     b4StartDrag : function(x, y){
54995         this.view.headersDisabled = true;
54996         this.proxy.setHeight(this.view.mainWrap.getHeight());
54997         var w = this.cm.getColumnWidth(this.cellIndex);
54998         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54999         this.resetConstraints();
55000         this.setXConstraint(minw, 1000);
55001         this.setYConstraint(0, 0);
55002         this.minX = x - minw;
55003         this.maxX = x + 1000;
55004         this.startPos = x;
55005         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55006     },
55007
55008
55009     handleMouseDown : function(e){
55010         ev = Roo.EventObject.setEvent(e);
55011         var t = this.fly(ev.getTarget());
55012         if(t.hasClass("x-grid-split")){
55013             this.cellIndex = this.view.getCellIndex(t.dom);
55014             this.split = t.dom;
55015             this.cm = this.grid.colModel;
55016             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55017                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55018             }
55019         }
55020     },
55021
55022     endDrag : function(e){
55023         this.view.headersDisabled = false;
55024         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55025         var diff = endX - this.startPos;
55026         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55027     },
55028
55029     autoOffset : function(){
55030         this.setDelta(0,0);
55031     }
55032 });/*
55033  * Based on:
55034  * Ext JS Library 1.1.1
55035  * Copyright(c) 2006-2007, Ext JS, LLC.
55036  *
55037  * Originally Released Under LGPL - original licence link has changed is not relivant.
55038  *
55039  * Fork - LGPL
55040  * <script type="text/javascript">
55041  */
55042  
55043 // private
55044 // This is a support class used internally by the Grid components
55045 Roo.grid.GridDragZone = function(grid, config){
55046     this.view = grid.getView();
55047     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55048     if(this.view.lockedBody){
55049         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55050         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55051     }
55052     this.scroll = false;
55053     this.grid = grid;
55054     this.ddel = document.createElement('div');
55055     this.ddel.className = 'x-grid-dd-wrap';
55056 };
55057
55058 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55059     ddGroup : "GridDD",
55060
55061     getDragData : function(e){
55062         var t = Roo.lib.Event.getTarget(e);
55063         var rowIndex = this.view.findRowIndex(t);
55064         var sm = this.grid.selModel;
55065             
55066         //Roo.log(rowIndex);
55067         
55068         if (sm.getSelectedCell) {
55069             // cell selection..
55070             if (!sm.getSelectedCell()) {
55071                 return false;
55072             }
55073             if (rowIndex != sm.getSelectedCell()[0]) {
55074                 return false;
55075             }
55076         
55077         }
55078         
55079         if(rowIndex !== false){
55080             
55081             // if editorgrid.. 
55082             
55083             
55084             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55085                
55086             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55087               //  
55088             //}
55089             if (e.hasModifier()){
55090                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55091             }
55092             
55093             Roo.log("getDragData");
55094             
55095             return {
55096                 grid: this.grid,
55097                 ddel: this.ddel,
55098                 rowIndex: rowIndex,
55099                 selections:sm.getSelections ? sm.getSelections() : (
55100                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55101                 )
55102             };
55103         }
55104         return false;
55105     },
55106
55107     onInitDrag : function(e){
55108         var data = this.dragData;
55109         this.ddel.innerHTML = this.grid.getDragDropText();
55110         this.proxy.update(this.ddel);
55111         // fire start drag?
55112     },
55113
55114     afterRepair : function(){
55115         this.dragging = false;
55116     },
55117
55118     getRepairXY : function(e, data){
55119         return false;
55120     },
55121
55122     onEndDrag : function(data, e){
55123         // fire end drag?
55124     },
55125
55126     onValidDrop : function(dd, e, id){
55127         // fire drag drop?
55128         this.hideProxy();
55129     },
55130
55131     beforeInvalidDrop : function(e, id){
55132
55133     }
55134 });/*
55135  * Based on:
55136  * Ext JS Library 1.1.1
55137  * Copyright(c) 2006-2007, Ext JS, LLC.
55138  *
55139  * Originally Released Under LGPL - original licence link has changed is not relivant.
55140  *
55141  * Fork - LGPL
55142  * <script type="text/javascript">
55143  */
55144  
55145
55146 /**
55147  * @class Roo.grid.ColumnModel
55148  * @extends Roo.util.Observable
55149  * This is the default implementation of a ColumnModel used by the Grid. It defines
55150  * the columns in the grid.
55151  * <br>Usage:<br>
55152  <pre><code>
55153  var colModel = new Roo.grid.ColumnModel([
55154         {header: "Ticker", width: 60, sortable: true, locked: true},
55155         {header: "Company Name", width: 150, sortable: true},
55156         {header: "Market Cap.", width: 100, sortable: true},
55157         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55158         {header: "Employees", width: 100, sortable: true, resizable: false}
55159  ]);
55160  </code></pre>
55161  * <p>
55162  
55163  * The config options listed for this class are options which may appear in each
55164  * individual column definition.
55165  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55166  * @constructor
55167  * @param {Object} config An Array of column config objects. See this class's
55168  * config objects for details.
55169 */
55170 Roo.grid.ColumnModel = function(config){
55171         /**
55172      * The config passed into the constructor
55173      */
55174     this.config = config;
55175     this.lookup = {};
55176
55177     // if no id, create one
55178     // if the column does not have a dataIndex mapping,
55179     // map it to the order it is in the config
55180     for(var i = 0, len = config.length; i < len; i++){
55181         var c = config[i];
55182         if(typeof c.dataIndex == "undefined"){
55183             c.dataIndex = i;
55184         }
55185         if(typeof c.renderer == "string"){
55186             c.renderer = Roo.util.Format[c.renderer];
55187         }
55188         if(typeof c.id == "undefined"){
55189             c.id = Roo.id();
55190         }
55191         if(c.editor && c.editor.xtype){
55192             c.editor  = Roo.factory(c.editor, Roo.grid);
55193         }
55194         if(c.editor && c.editor.isFormField){
55195             c.editor = new Roo.grid.GridEditor(c.editor);
55196         }
55197         this.lookup[c.id] = c;
55198     }
55199
55200     /**
55201      * The width of columns which have no width specified (defaults to 100)
55202      * @type Number
55203      */
55204     this.defaultWidth = 100;
55205
55206     /**
55207      * Default sortable of columns which have no sortable specified (defaults to false)
55208      * @type Boolean
55209      */
55210     this.defaultSortable = false;
55211
55212     this.addEvents({
55213         /**
55214              * @event widthchange
55215              * Fires when the width of a column changes.
55216              * @param {ColumnModel} this
55217              * @param {Number} columnIndex The column index
55218              * @param {Number} newWidth The new width
55219              */
55220             "widthchange": true,
55221         /**
55222              * @event headerchange
55223              * Fires when the text of a header changes.
55224              * @param {ColumnModel} this
55225              * @param {Number} columnIndex The column index
55226              * @param {Number} newText The new header text
55227              */
55228             "headerchange": true,
55229         /**
55230              * @event hiddenchange
55231              * Fires when a column is hidden or "unhidden".
55232              * @param {ColumnModel} this
55233              * @param {Number} columnIndex The column index
55234              * @param {Boolean} hidden true if hidden, false otherwise
55235              */
55236             "hiddenchange": true,
55237             /**
55238          * @event columnmoved
55239          * Fires when a column is moved.
55240          * @param {ColumnModel} this
55241          * @param {Number} oldIndex
55242          * @param {Number} newIndex
55243          */
55244         "columnmoved" : true,
55245         /**
55246          * @event columlockchange
55247          * Fires when a column's locked state is changed
55248          * @param {ColumnModel} this
55249          * @param {Number} colIndex
55250          * @param {Boolean} locked true if locked
55251          */
55252         "columnlockchange" : true
55253     });
55254     Roo.grid.ColumnModel.superclass.constructor.call(this);
55255 };
55256 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55257     /**
55258      * @cfg {String} header The header text to display in the Grid view.
55259      */
55260     /**
55261      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55262      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55263      * specified, the column's index is used as an index into the Record's data Array.
55264      */
55265     /**
55266      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55267      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55268      */
55269     /**
55270      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55271      * Defaults to the value of the {@link #defaultSortable} property.
55272      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55273      */
55274     /**
55275      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55276      */
55277     /**
55278      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55279      */
55280     /**
55281      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55282      */
55283     /**
55284      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55285      */
55286     /**
55287      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55288      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55289      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55290      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55291      */
55292        /**
55293      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55294      */
55295     /**
55296      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55297      */
55298     /**
55299      * @cfg {String} cursor (Optional)
55300      */
55301     /**
55302      * @cfg {String} tooltip (Optional)
55303      */
55304     /**
55305      * Returns the id of the column at the specified index.
55306      * @param {Number} index The column index
55307      * @return {String} the id
55308      */
55309     getColumnId : function(index){
55310         return this.config[index].id;
55311     },
55312
55313     /**
55314      * Returns the column for a specified id.
55315      * @param {String} id The column id
55316      * @return {Object} the column
55317      */
55318     getColumnById : function(id){
55319         return this.lookup[id];
55320     },
55321
55322     
55323     /**
55324      * Returns the column for a specified dataIndex.
55325      * @param {String} dataIndex The column dataIndex
55326      * @return {Object|Boolean} the column or false if not found
55327      */
55328     getColumnByDataIndex: function(dataIndex){
55329         var index = this.findColumnIndex(dataIndex);
55330         return index > -1 ? this.config[index] : false;
55331     },
55332     
55333     /**
55334      * Returns the index for a specified column id.
55335      * @param {String} id The column id
55336      * @return {Number} the index, or -1 if not found
55337      */
55338     getIndexById : function(id){
55339         for(var i = 0, len = this.config.length; i < len; i++){
55340             if(this.config[i].id == id){
55341                 return i;
55342             }
55343         }
55344         return -1;
55345     },
55346     
55347     /**
55348      * Returns the index for a specified column dataIndex.
55349      * @param {String} dataIndex The column dataIndex
55350      * @return {Number} the index, or -1 if not found
55351      */
55352     
55353     findColumnIndex : function(dataIndex){
55354         for(var i = 0, len = this.config.length; i < len; i++){
55355             if(this.config[i].dataIndex == dataIndex){
55356                 return i;
55357             }
55358         }
55359         return -1;
55360     },
55361     
55362     
55363     moveColumn : function(oldIndex, newIndex){
55364         var c = this.config[oldIndex];
55365         this.config.splice(oldIndex, 1);
55366         this.config.splice(newIndex, 0, c);
55367         this.dataMap = null;
55368         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55369     },
55370
55371     isLocked : function(colIndex){
55372         return this.config[colIndex].locked === true;
55373     },
55374
55375     setLocked : function(colIndex, value, suppressEvent){
55376         if(this.isLocked(colIndex) == value){
55377             return;
55378         }
55379         this.config[colIndex].locked = value;
55380         if(!suppressEvent){
55381             this.fireEvent("columnlockchange", this, colIndex, value);
55382         }
55383     },
55384
55385     getTotalLockedWidth : function(){
55386         var totalWidth = 0;
55387         for(var i = 0; i < this.config.length; i++){
55388             if(this.isLocked(i) && !this.isHidden(i)){
55389                 this.totalWidth += this.getColumnWidth(i);
55390             }
55391         }
55392         return totalWidth;
55393     },
55394
55395     getLockedCount : function(){
55396         for(var i = 0, len = this.config.length; i < len; i++){
55397             if(!this.isLocked(i)){
55398                 return i;
55399             }
55400         }
55401     },
55402
55403     /**
55404      * Returns the number of columns.
55405      * @return {Number}
55406      */
55407     getColumnCount : function(visibleOnly){
55408         if(visibleOnly === true){
55409             var c = 0;
55410             for(var i = 0, len = this.config.length; i < len; i++){
55411                 if(!this.isHidden(i)){
55412                     c++;
55413                 }
55414             }
55415             return c;
55416         }
55417         return this.config.length;
55418     },
55419
55420     /**
55421      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55422      * @param {Function} fn
55423      * @param {Object} scope (optional)
55424      * @return {Array} result
55425      */
55426     getColumnsBy : function(fn, scope){
55427         var r = [];
55428         for(var i = 0, len = this.config.length; i < len; i++){
55429             var c = this.config[i];
55430             if(fn.call(scope||this, c, i) === true){
55431                 r[r.length] = c;
55432             }
55433         }
55434         return r;
55435     },
55436
55437     /**
55438      * Returns true if the specified column is sortable.
55439      * @param {Number} col The column index
55440      * @return {Boolean}
55441      */
55442     isSortable : function(col){
55443         if(typeof this.config[col].sortable == "undefined"){
55444             return this.defaultSortable;
55445         }
55446         return this.config[col].sortable;
55447     },
55448
55449     /**
55450      * Returns the rendering (formatting) function defined for the column.
55451      * @param {Number} col The column index.
55452      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55453      */
55454     getRenderer : function(col){
55455         if(!this.config[col].renderer){
55456             return Roo.grid.ColumnModel.defaultRenderer;
55457         }
55458         return this.config[col].renderer;
55459     },
55460
55461     /**
55462      * Sets the rendering (formatting) function for a column.
55463      * @param {Number} col The column index
55464      * @param {Function} fn The function to use to process the cell's raw data
55465      * to return HTML markup for the grid view. The render function is called with
55466      * the following parameters:<ul>
55467      * <li>Data value.</li>
55468      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55469      * <li>css A CSS style string to apply to the table cell.</li>
55470      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55471      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55472      * <li>Row index</li>
55473      * <li>Column index</li>
55474      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55475      */
55476     setRenderer : function(col, fn){
55477         this.config[col].renderer = fn;
55478     },
55479
55480     /**
55481      * Returns the width for the specified column.
55482      * @param {Number} col The column index
55483      * @return {Number}
55484      */
55485     getColumnWidth : function(col){
55486         return this.config[col].width * 1 || this.defaultWidth;
55487     },
55488
55489     /**
55490      * Sets the width for a column.
55491      * @param {Number} col The column index
55492      * @param {Number} width The new width
55493      */
55494     setColumnWidth : function(col, width, suppressEvent){
55495         this.config[col].width = width;
55496         this.totalWidth = null;
55497         if(!suppressEvent){
55498              this.fireEvent("widthchange", this, col, width);
55499         }
55500     },
55501
55502     /**
55503      * Returns the total width of all columns.
55504      * @param {Boolean} includeHidden True to include hidden column widths
55505      * @return {Number}
55506      */
55507     getTotalWidth : function(includeHidden){
55508         if(!this.totalWidth){
55509             this.totalWidth = 0;
55510             for(var i = 0, len = this.config.length; i < len; i++){
55511                 if(includeHidden || !this.isHidden(i)){
55512                     this.totalWidth += this.getColumnWidth(i);
55513                 }
55514             }
55515         }
55516         return this.totalWidth;
55517     },
55518
55519     /**
55520      * Returns the header for the specified column.
55521      * @param {Number} col The column index
55522      * @return {String}
55523      */
55524     getColumnHeader : function(col){
55525         return this.config[col].header;
55526     },
55527
55528     /**
55529      * Sets the header for a column.
55530      * @param {Number} col The column index
55531      * @param {String} header The new header
55532      */
55533     setColumnHeader : function(col, header){
55534         this.config[col].header = header;
55535         this.fireEvent("headerchange", this, col, header);
55536     },
55537
55538     /**
55539      * Returns the tooltip for the specified column.
55540      * @param {Number} col The column index
55541      * @return {String}
55542      */
55543     getColumnTooltip : function(col){
55544             return this.config[col].tooltip;
55545     },
55546     /**
55547      * Sets the tooltip for a column.
55548      * @param {Number} col The column index
55549      * @param {String} tooltip The new tooltip
55550      */
55551     setColumnTooltip : function(col, tooltip){
55552             this.config[col].tooltip = tooltip;
55553     },
55554
55555     /**
55556      * Returns the dataIndex for the specified column.
55557      * @param {Number} col The column index
55558      * @return {Number}
55559      */
55560     getDataIndex : function(col){
55561         return this.config[col].dataIndex;
55562     },
55563
55564     /**
55565      * Sets the dataIndex for a column.
55566      * @param {Number} col The column index
55567      * @param {Number} dataIndex The new dataIndex
55568      */
55569     setDataIndex : function(col, dataIndex){
55570         this.config[col].dataIndex = dataIndex;
55571     },
55572
55573     
55574     
55575     /**
55576      * Returns true if the cell is editable.
55577      * @param {Number} colIndex The column index
55578      * @param {Number} rowIndex The row index
55579      * @return {Boolean}
55580      */
55581     isCellEditable : function(colIndex, rowIndex){
55582         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55583     },
55584
55585     /**
55586      * Returns the editor defined for the cell/column.
55587      * return false or null to disable editing.
55588      * @param {Number} colIndex The column index
55589      * @param {Number} rowIndex The row index
55590      * @return {Object}
55591      */
55592     getCellEditor : function(colIndex, rowIndex){
55593         return this.config[colIndex].editor;
55594     },
55595
55596     /**
55597      * Sets if a column is editable.
55598      * @param {Number} col The column index
55599      * @param {Boolean} editable True if the column is editable
55600      */
55601     setEditable : function(col, editable){
55602         this.config[col].editable = editable;
55603     },
55604
55605
55606     /**
55607      * Returns true if the column is hidden.
55608      * @param {Number} colIndex The column index
55609      * @return {Boolean}
55610      */
55611     isHidden : function(colIndex){
55612         return this.config[colIndex].hidden;
55613     },
55614
55615
55616     /**
55617      * Returns true if the column width cannot be changed
55618      */
55619     isFixed : function(colIndex){
55620         return this.config[colIndex].fixed;
55621     },
55622
55623     /**
55624      * Returns true if the column can be resized
55625      * @return {Boolean}
55626      */
55627     isResizable : function(colIndex){
55628         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55629     },
55630     /**
55631      * Sets if a column is hidden.
55632      * @param {Number} colIndex The column index
55633      * @param {Boolean} hidden True if the column is hidden
55634      */
55635     setHidden : function(colIndex, hidden){
55636         this.config[colIndex].hidden = hidden;
55637         this.totalWidth = null;
55638         this.fireEvent("hiddenchange", this, colIndex, hidden);
55639     },
55640
55641     /**
55642      * Sets the editor for a column.
55643      * @param {Number} col The column index
55644      * @param {Object} editor The editor object
55645      */
55646     setEditor : function(col, editor){
55647         this.config[col].editor = editor;
55648     }
55649 });
55650
55651 Roo.grid.ColumnModel.defaultRenderer = function(value){
55652         if(typeof value == "string" && value.length < 1){
55653             return "&#160;";
55654         }
55655         return value;
55656 };
55657
55658 // Alias for backwards compatibility
55659 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55660 /*
55661  * Based on:
55662  * Ext JS Library 1.1.1
55663  * Copyright(c) 2006-2007, Ext JS, LLC.
55664  *
55665  * Originally Released Under LGPL - original licence link has changed is not relivant.
55666  *
55667  * Fork - LGPL
55668  * <script type="text/javascript">
55669  */
55670
55671 /**
55672  * @class Roo.grid.AbstractSelectionModel
55673  * @extends Roo.util.Observable
55674  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55675  * implemented by descendant classes.  This class should not be directly instantiated.
55676  * @constructor
55677  */
55678 Roo.grid.AbstractSelectionModel = function(){
55679     this.locked = false;
55680     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55681 };
55682
55683 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55684     /** @ignore Called by the grid automatically. Do not call directly. */
55685     init : function(grid){
55686         this.grid = grid;
55687         this.initEvents();
55688     },
55689
55690     /**
55691      * Locks the selections.
55692      */
55693     lock : function(){
55694         this.locked = true;
55695     },
55696
55697     /**
55698      * Unlocks the selections.
55699      */
55700     unlock : function(){
55701         this.locked = false;
55702     },
55703
55704     /**
55705      * Returns true if the selections are locked.
55706      * @return {Boolean}
55707      */
55708     isLocked : function(){
55709         return this.locked;
55710     }
55711 });/*
55712  * Based on:
55713  * Ext JS Library 1.1.1
55714  * Copyright(c) 2006-2007, Ext JS, LLC.
55715  *
55716  * Originally Released Under LGPL - original licence link has changed is not relivant.
55717  *
55718  * Fork - LGPL
55719  * <script type="text/javascript">
55720  */
55721 /**
55722  * @extends Roo.grid.AbstractSelectionModel
55723  * @class Roo.grid.RowSelectionModel
55724  * The default SelectionModel used by {@link Roo.grid.Grid}.
55725  * It supports multiple selections and keyboard selection/navigation. 
55726  * @constructor
55727  * @param {Object} config
55728  */
55729 Roo.grid.RowSelectionModel = function(config){
55730     Roo.apply(this, config);
55731     this.selections = new Roo.util.MixedCollection(false, function(o){
55732         return o.id;
55733     });
55734
55735     this.last = false;
55736     this.lastActive = false;
55737
55738     this.addEvents({
55739         /**
55740              * @event selectionchange
55741              * Fires when the selection changes
55742              * @param {SelectionModel} this
55743              */
55744             "selectionchange" : true,
55745         /**
55746              * @event afterselectionchange
55747              * Fires after the selection changes (eg. by key press or clicking)
55748              * @param {SelectionModel} this
55749              */
55750             "afterselectionchange" : true,
55751         /**
55752              * @event beforerowselect
55753              * Fires when a row is selected being selected, return false to cancel.
55754              * @param {SelectionModel} this
55755              * @param {Number} rowIndex The selected index
55756              * @param {Boolean} keepExisting False if other selections will be cleared
55757              */
55758             "beforerowselect" : true,
55759         /**
55760              * @event rowselect
55761              * Fires when a row is selected.
55762              * @param {SelectionModel} this
55763              * @param {Number} rowIndex The selected index
55764              * @param {Roo.data.Record} r The record
55765              */
55766             "rowselect" : true,
55767         /**
55768              * @event rowdeselect
55769              * Fires when a row is deselected.
55770              * @param {SelectionModel} this
55771              * @param {Number} rowIndex The selected index
55772              */
55773         "rowdeselect" : true
55774     });
55775     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55776     this.locked = false;
55777 };
55778
55779 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55780     /**
55781      * @cfg {Boolean} singleSelect
55782      * True to allow selection of only one row at a time (defaults to false)
55783      */
55784     singleSelect : false,
55785
55786     // private
55787     initEvents : function(){
55788
55789         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55790             this.grid.on("mousedown", this.handleMouseDown, this);
55791         }else{ // allow click to work like normal
55792             this.grid.on("rowclick", this.handleDragableRowClick, this);
55793         }
55794
55795         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55796             "up" : function(e){
55797                 if(!e.shiftKey){
55798                     this.selectPrevious(e.shiftKey);
55799                 }else if(this.last !== false && this.lastActive !== false){
55800                     var last = this.last;
55801                     this.selectRange(this.last,  this.lastActive-1);
55802                     this.grid.getView().focusRow(this.lastActive);
55803                     if(last !== false){
55804                         this.last = last;
55805                     }
55806                 }else{
55807                     this.selectFirstRow();
55808                 }
55809                 this.fireEvent("afterselectionchange", this);
55810             },
55811             "down" : function(e){
55812                 if(!e.shiftKey){
55813                     this.selectNext(e.shiftKey);
55814                 }else if(this.last !== false && this.lastActive !== false){
55815                     var last = this.last;
55816                     this.selectRange(this.last,  this.lastActive+1);
55817                     this.grid.getView().focusRow(this.lastActive);
55818                     if(last !== false){
55819                         this.last = last;
55820                     }
55821                 }else{
55822                     this.selectFirstRow();
55823                 }
55824                 this.fireEvent("afterselectionchange", this);
55825             },
55826             scope: this
55827         });
55828
55829         var view = this.grid.view;
55830         view.on("refresh", this.onRefresh, this);
55831         view.on("rowupdated", this.onRowUpdated, this);
55832         view.on("rowremoved", this.onRemove, this);
55833     },
55834
55835     // private
55836     onRefresh : function(){
55837         var ds = this.grid.dataSource, i, v = this.grid.view;
55838         var s = this.selections;
55839         s.each(function(r){
55840             if((i = ds.indexOfId(r.id)) != -1){
55841                 v.onRowSelect(i);
55842             }else{
55843                 s.remove(r);
55844             }
55845         });
55846     },
55847
55848     // private
55849     onRemove : function(v, index, r){
55850         this.selections.remove(r);
55851     },
55852
55853     // private
55854     onRowUpdated : function(v, index, r){
55855         if(this.isSelected(r)){
55856             v.onRowSelect(index);
55857         }
55858     },
55859
55860     /**
55861      * Select records.
55862      * @param {Array} records The records to select
55863      * @param {Boolean} keepExisting (optional) True to keep existing selections
55864      */
55865     selectRecords : function(records, keepExisting){
55866         if(!keepExisting){
55867             this.clearSelections();
55868         }
55869         var ds = this.grid.dataSource;
55870         for(var i = 0, len = records.length; i < len; i++){
55871             this.selectRow(ds.indexOf(records[i]), true);
55872         }
55873     },
55874
55875     /**
55876      * Gets the number of selected rows.
55877      * @return {Number}
55878      */
55879     getCount : function(){
55880         return this.selections.length;
55881     },
55882
55883     /**
55884      * Selects the first row in the grid.
55885      */
55886     selectFirstRow : function(){
55887         this.selectRow(0);
55888     },
55889
55890     /**
55891      * Select the last row.
55892      * @param {Boolean} keepExisting (optional) True to keep existing selections
55893      */
55894     selectLastRow : function(keepExisting){
55895         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55896     },
55897
55898     /**
55899      * Selects the row immediately following the last selected row.
55900      * @param {Boolean} keepExisting (optional) True to keep existing selections
55901      */
55902     selectNext : function(keepExisting){
55903         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55904             this.selectRow(this.last+1, keepExisting);
55905             this.grid.getView().focusRow(this.last);
55906         }
55907     },
55908
55909     /**
55910      * Selects the row that precedes the last selected row.
55911      * @param {Boolean} keepExisting (optional) True to keep existing selections
55912      */
55913     selectPrevious : function(keepExisting){
55914         if(this.last){
55915             this.selectRow(this.last-1, keepExisting);
55916             this.grid.getView().focusRow(this.last);
55917         }
55918     },
55919
55920     /**
55921      * Returns the selected records
55922      * @return {Array} Array of selected records
55923      */
55924     getSelections : function(){
55925         return [].concat(this.selections.items);
55926     },
55927
55928     /**
55929      * Returns the first selected record.
55930      * @return {Record}
55931      */
55932     getSelected : function(){
55933         return this.selections.itemAt(0);
55934     },
55935
55936
55937     /**
55938      * Clears all selections.
55939      */
55940     clearSelections : function(fast){
55941         if(this.locked) return;
55942         if(fast !== true){
55943             var ds = this.grid.dataSource;
55944             var s = this.selections;
55945             s.each(function(r){
55946                 this.deselectRow(ds.indexOfId(r.id));
55947             }, this);
55948             s.clear();
55949         }else{
55950             this.selections.clear();
55951         }
55952         this.last = false;
55953     },
55954
55955
55956     /**
55957      * Selects all rows.
55958      */
55959     selectAll : function(){
55960         if(this.locked) return;
55961         this.selections.clear();
55962         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55963             this.selectRow(i, true);
55964         }
55965     },
55966
55967     /**
55968      * Returns True if there is a selection.
55969      * @return {Boolean}
55970      */
55971     hasSelection : function(){
55972         return this.selections.length > 0;
55973     },
55974
55975     /**
55976      * Returns True if the specified row is selected.
55977      * @param {Number/Record} record The record or index of the record to check
55978      * @return {Boolean}
55979      */
55980     isSelected : function(index){
55981         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55982         return (r && this.selections.key(r.id) ? true : false);
55983     },
55984
55985     /**
55986      * Returns True if the specified record id is selected.
55987      * @param {String} id The id of record to check
55988      * @return {Boolean}
55989      */
55990     isIdSelected : function(id){
55991         return (this.selections.key(id) ? true : false);
55992     },
55993
55994     // private
55995     handleMouseDown : function(e, t){
55996         var view = this.grid.getView(), rowIndex;
55997         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55998             return;
55999         };
56000         if(e.shiftKey && this.last !== false){
56001             var last = this.last;
56002             this.selectRange(last, rowIndex, e.ctrlKey);
56003             this.last = last; // reset the last
56004             view.focusRow(rowIndex);
56005         }else{
56006             var isSelected = this.isSelected(rowIndex);
56007             if(e.button !== 0 && isSelected){
56008                 view.focusRow(rowIndex);
56009             }else if(e.ctrlKey && isSelected){
56010                 this.deselectRow(rowIndex);
56011             }else if(!isSelected){
56012                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56013                 view.focusRow(rowIndex);
56014             }
56015         }
56016         this.fireEvent("afterselectionchange", this);
56017     },
56018     // private
56019     handleDragableRowClick :  function(grid, rowIndex, e) 
56020     {
56021         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56022             this.selectRow(rowIndex, false);
56023             grid.view.focusRow(rowIndex);
56024              this.fireEvent("afterselectionchange", this);
56025         }
56026     },
56027     
56028     /**
56029      * Selects multiple rows.
56030      * @param {Array} rows Array of the indexes of the row to select
56031      * @param {Boolean} keepExisting (optional) True to keep existing selections
56032      */
56033     selectRows : function(rows, keepExisting){
56034         if(!keepExisting){
56035             this.clearSelections();
56036         }
56037         for(var i = 0, len = rows.length; i < len; i++){
56038             this.selectRow(rows[i], true);
56039         }
56040     },
56041
56042     /**
56043      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56044      * @param {Number} startRow The index of the first row in the range
56045      * @param {Number} endRow The index of the last row in the range
56046      * @param {Boolean} keepExisting (optional) True to retain existing selections
56047      */
56048     selectRange : function(startRow, endRow, keepExisting){
56049         if(this.locked) return;
56050         if(!keepExisting){
56051             this.clearSelections();
56052         }
56053         if(startRow <= endRow){
56054             for(var i = startRow; i <= endRow; i++){
56055                 this.selectRow(i, true);
56056             }
56057         }else{
56058             for(var i = startRow; i >= endRow; i--){
56059                 this.selectRow(i, true);
56060             }
56061         }
56062     },
56063
56064     /**
56065      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56066      * @param {Number} startRow The index of the first row in the range
56067      * @param {Number} endRow The index of the last row in the range
56068      */
56069     deselectRange : function(startRow, endRow, preventViewNotify){
56070         if(this.locked) return;
56071         for(var i = startRow; i <= endRow; i++){
56072             this.deselectRow(i, preventViewNotify);
56073         }
56074     },
56075
56076     /**
56077      * Selects a row.
56078      * @param {Number} row The index of the row to select
56079      * @param {Boolean} keepExisting (optional) True to keep existing selections
56080      */
56081     selectRow : function(index, keepExisting, preventViewNotify){
56082         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56083         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56084             if(!keepExisting || this.singleSelect){
56085                 this.clearSelections();
56086             }
56087             var r = this.grid.dataSource.getAt(index);
56088             this.selections.add(r);
56089             this.last = this.lastActive = index;
56090             if(!preventViewNotify){
56091                 this.grid.getView().onRowSelect(index);
56092             }
56093             this.fireEvent("rowselect", this, index, r);
56094             this.fireEvent("selectionchange", this);
56095         }
56096     },
56097
56098     /**
56099      * Deselects a row.
56100      * @param {Number} row The index of the row to deselect
56101      */
56102     deselectRow : function(index, preventViewNotify){
56103         if(this.locked) return;
56104         if(this.last == index){
56105             this.last = false;
56106         }
56107         if(this.lastActive == index){
56108             this.lastActive = false;
56109         }
56110         var r = this.grid.dataSource.getAt(index);
56111         this.selections.remove(r);
56112         if(!preventViewNotify){
56113             this.grid.getView().onRowDeselect(index);
56114         }
56115         this.fireEvent("rowdeselect", this, index);
56116         this.fireEvent("selectionchange", this);
56117     },
56118
56119     // private
56120     restoreLast : function(){
56121         if(this._last){
56122             this.last = this._last;
56123         }
56124     },
56125
56126     // private
56127     acceptsNav : function(row, col, cm){
56128         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56129     },
56130
56131     // private
56132     onEditorKey : function(field, e){
56133         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56134         if(k == e.TAB){
56135             e.stopEvent();
56136             ed.completeEdit();
56137             if(e.shiftKey){
56138                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56139             }else{
56140                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56141             }
56142         }else if(k == e.ENTER && !e.ctrlKey){
56143             e.stopEvent();
56144             ed.completeEdit();
56145             if(e.shiftKey){
56146                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56147             }else{
56148                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56149             }
56150         }else if(k == e.ESC){
56151             ed.cancelEdit();
56152         }
56153         if(newCell){
56154             g.startEditing(newCell[0], newCell[1]);
56155         }
56156     }
56157 });/*
56158  * Based on:
56159  * Ext JS Library 1.1.1
56160  * Copyright(c) 2006-2007, Ext JS, LLC.
56161  *
56162  * Originally Released Under LGPL - original licence link has changed is not relivant.
56163  *
56164  * Fork - LGPL
56165  * <script type="text/javascript">
56166  */
56167 /**
56168  * @class Roo.grid.CellSelectionModel
56169  * @extends Roo.grid.AbstractSelectionModel
56170  * This class provides the basic implementation for cell selection in a grid.
56171  * @constructor
56172  * @param {Object} config The object containing the configuration of this model.
56173  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56174  */
56175 Roo.grid.CellSelectionModel = function(config){
56176     Roo.apply(this, config);
56177
56178     this.selection = null;
56179
56180     this.addEvents({
56181         /**
56182              * @event beforerowselect
56183              * Fires before a cell is selected.
56184              * @param {SelectionModel} this
56185              * @param {Number} rowIndex The selected row index
56186              * @param {Number} colIndex The selected cell index
56187              */
56188             "beforecellselect" : true,
56189         /**
56190              * @event cellselect
56191              * Fires when a cell is selected.
56192              * @param {SelectionModel} this
56193              * @param {Number} rowIndex The selected row index
56194              * @param {Number} colIndex The selected cell index
56195              */
56196             "cellselect" : true,
56197         /**
56198              * @event selectionchange
56199              * Fires when the active selection changes.
56200              * @param {SelectionModel} this
56201              * @param {Object} selection null for no selection or an object (o) with two properties
56202                 <ul>
56203                 <li>o.record: the record object for the row the selection is in</li>
56204                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56205                 </ul>
56206              */
56207             "selectionchange" : true,
56208         /**
56209              * @event tabend
56210              * Fires when the tab (or enter) was pressed on the last editable cell
56211              * You can use this to trigger add new row.
56212              * @param {SelectionModel} this
56213              */
56214             "tabend" : true,
56215          /**
56216              * @event beforeeditnext
56217              * Fires before the next editable sell is made active
56218              * You can use this to skip to another cell or fire the tabend
56219              *    if you set cell to false
56220              * @param {Object} eventdata object : { cell : [ row, col ] } 
56221              */
56222             "beforeeditnext" : true
56223     });
56224     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56225 };
56226
56227 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56228     
56229     enter_is_tab: false,
56230
56231     /** @ignore */
56232     initEvents : function(){
56233         this.grid.on("mousedown", this.handleMouseDown, this);
56234         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56235         var view = this.grid.view;
56236         view.on("refresh", this.onViewChange, this);
56237         view.on("rowupdated", this.onRowUpdated, this);
56238         view.on("beforerowremoved", this.clearSelections, this);
56239         view.on("beforerowsinserted", this.clearSelections, this);
56240         if(this.grid.isEditor){
56241             this.grid.on("beforeedit", this.beforeEdit,  this);
56242         }
56243     },
56244
56245         //private
56246     beforeEdit : function(e){
56247         this.select(e.row, e.column, false, true, e.record);
56248     },
56249
56250         //private
56251     onRowUpdated : function(v, index, r){
56252         if(this.selection && this.selection.record == r){
56253             v.onCellSelect(index, this.selection.cell[1]);
56254         }
56255     },
56256
56257         //private
56258     onViewChange : function(){
56259         this.clearSelections(true);
56260     },
56261
56262         /**
56263          * Returns the currently selected cell,.
56264          * @return {Array} The selected cell (row, column) or null if none selected.
56265          */
56266     getSelectedCell : function(){
56267         return this.selection ? this.selection.cell : null;
56268     },
56269
56270     /**
56271      * Clears all selections.
56272      * @param {Boolean} true to prevent the gridview from being notified about the change.
56273      */
56274     clearSelections : function(preventNotify){
56275         var s = this.selection;
56276         if(s){
56277             if(preventNotify !== true){
56278                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56279             }
56280             this.selection = null;
56281             this.fireEvent("selectionchange", this, null);
56282         }
56283     },
56284
56285     /**
56286      * Returns true if there is a selection.
56287      * @return {Boolean}
56288      */
56289     hasSelection : function(){
56290         return this.selection ? true : false;
56291     },
56292
56293     /** @ignore */
56294     handleMouseDown : function(e, t){
56295         var v = this.grid.getView();
56296         if(this.isLocked()){
56297             return;
56298         };
56299         var row = v.findRowIndex(t);
56300         var cell = v.findCellIndex(t);
56301         if(row !== false && cell !== false){
56302             this.select(row, cell);
56303         }
56304     },
56305
56306     /**
56307      * Selects a cell.
56308      * @param {Number} rowIndex
56309      * @param {Number} collIndex
56310      */
56311     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56312         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56313             this.clearSelections();
56314             r = r || this.grid.dataSource.getAt(rowIndex);
56315             this.selection = {
56316                 record : r,
56317                 cell : [rowIndex, colIndex]
56318             };
56319             if(!preventViewNotify){
56320                 var v = this.grid.getView();
56321                 v.onCellSelect(rowIndex, colIndex);
56322                 if(preventFocus !== true){
56323                     v.focusCell(rowIndex, colIndex);
56324                 }
56325             }
56326             this.fireEvent("cellselect", this, rowIndex, colIndex);
56327             this.fireEvent("selectionchange", this, this.selection);
56328         }
56329     },
56330
56331         //private
56332     isSelectable : function(rowIndex, colIndex, cm){
56333         return !cm.isHidden(colIndex);
56334     },
56335
56336     /** @ignore */
56337     handleKeyDown : function(e){
56338         //Roo.log('Cell Sel Model handleKeyDown');
56339         if(!e.isNavKeyPress()){
56340             return;
56341         }
56342         var g = this.grid, s = this.selection;
56343         if(!s){
56344             e.stopEvent();
56345             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56346             if(cell){
56347                 this.select(cell[0], cell[1]);
56348             }
56349             return;
56350         }
56351         var sm = this;
56352         var walk = function(row, col, step){
56353             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56354         };
56355         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56356         var newCell;
56357
56358       
56359
56360         switch(k){
56361             case e.TAB:
56362                 // handled by onEditorKey
56363                 if (g.isEditor && g.editing) {
56364                     return;
56365                 }
56366                 if(e.shiftKey) {
56367                     newCell = walk(r, c-1, -1);
56368                 } else {
56369                     newCell = walk(r, c+1, 1);
56370                 }
56371                 break;
56372             
56373             case e.DOWN:
56374                newCell = walk(r+1, c, 1);
56375                 break;
56376             
56377             case e.UP:
56378                 newCell = walk(r-1, c, -1);
56379                 break;
56380             
56381             case e.RIGHT:
56382                 newCell = walk(r, c+1, 1);
56383                 break;
56384             
56385             case e.LEFT:
56386                 newCell = walk(r, c-1, -1);
56387                 break;
56388             
56389             case e.ENTER:
56390                 
56391                 if(g.isEditor && !g.editing){
56392                    g.startEditing(r, c);
56393                    e.stopEvent();
56394                    return;
56395                 }
56396                 
56397                 
56398              break;
56399         };
56400         if(newCell){
56401             this.select(newCell[0], newCell[1]);
56402             e.stopEvent();
56403             
56404         }
56405     },
56406
56407     acceptsNav : function(row, col, cm){
56408         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56409     },
56410     /**
56411      * Selects a cell.
56412      * @param {Number} field (not used) - as it's normally used as a listener
56413      * @param {Number} e - event - fake it by using
56414      *
56415      * var e = Roo.EventObjectImpl.prototype;
56416      * e.keyCode = e.TAB
56417      *
56418      * 
56419      */
56420     onEditorKey : function(field, e){
56421         
56422         var k = e.getKey(),
56423             newCell,
56424             g = this.grid,
56425             ed = g.activeEditor,
56426             forward = false;
56427         ///Roo.log('onEditorKey' + k);
56428         
56429         
56430         if (this.enter_is_tab && k == e.ENTER) {
56431             k = e.TAB;
56432         }
56433         
56434         if(k == e.TAB){
56435             if(e.shiftKey){
56436                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56437             }else{
56438                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56439                 forward = true;
56440             }
56441             
56442             e.stopEvent();
56443             
56444         } else if(k == e.ENTER &&  !e.ctrlKey){
56445             ed.completeEdit();
56446             e.stopEvent();
56447             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56448         
56449                 } else if(k == e.ESC){
56450             ed.cancelEdit();
56451         }
56452                 
56453         if (newCell) {
56454             var ecall = { cell : newCell, forward : forward };
56455             this.fireEvent('beforeeditnext', ecall );
56456             newCell = ecall.cell;
56457                         forward = ecall.forward;
56458         }
56459                 
56460         if(newCell){
56461             //Roo.log('next cell after edit');
56462             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56463         } else if (forward) {
56464             // tabbed past last
56465             this.fireEvent.defer(100, this, ['tabend',this]);
56466         }
56467     }
56468 });/*
56469  * Based on:
56470  * Ext JS Library 1.1.1
56471  * Copyright(c) 2006-2007, Ext JS, LLC.
56472  *
56473  * Originally Released Under LGPL - original licence link has changed is not relivant.
56474  *
56475  * Fork - LGPL
56476  * <script type="text/javascript">
56477  */
56478  
56479 /**
56480  * @class Roo.grid.EditorGrid
56481  * @extends Roo.grid.Grid
56482  * Class for creating and editable grid.
56483  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56484  * The container MUST have some type of size defined for the grid to fill. The container will be 
56485  * automatically set to position relative if it isn't already.
56486  * @param {Object} dataSource The data model to bind to
56487  * @param {Object} colModel The column model with info about this grid's columns
56488  */
56489 Roo.grid.EditorGrid = function(container, config){
56490     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56491     this.getGridEl().addClass("xedit-grid");
56492
56493     if(!this.selModel){
56494         this.selModel = new Roo.grid.CellSelectionModel();
56495     }
56496
56497     this.activeEditor = null;
56498
56499         this.addEvents({
56500             /**
56501              * @event beforeedit
56502              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56503              * <ul style="padding:5px;padding-left:16px;">
56504              * <li>grid - This grid</li>
56505              * <li>record - The record being edited</li>
56506              * <li>field - The field name being edited</li>
56507              * <li>value - The value for the field being edited.</li>
56508              * <li>row - The grid row index</li>
56509              * <li>column - The grid column index</li>
56510              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56511              * </ul>
56512              * @param {Object} e An edit event (see above for description)
56513              */
56514             "beforeedit" : true,
56515             /**
56516              * @event afteredit
56517              * Fires after a cell is edited. <br />
56518              * <ul style="padding:5px;padding-left:16px;">
56519              * <li>grid - This grid</li>
56520              * <li>record - The record being edited</li>
56521              * <li>field - The field name being edited</li>
56522              * <li>value - The value being set</li>
56523              * <li>originalValue - The original value for the field, before the edit.</li>
56524              * <li>row - The grid row index</li>
56525              * <li>column - The grid column index</li>
56526              * </ul>
56527              * @param {Object} e An edit event (see above for description)
56528              */
56529             "afteredit" : true,
56530             /**
56531              * @event validateedit
56532              * Fires after a cell is edited, but before the value is set in the record. 
56533          * You can use this to modify the value being set in the field, Return false
56534              * to cancel the change. The edit event object has the following properties <br />
56535              * <ul style="padding:5px;padding-left:16px;">
56536          * <li>editor - This editor</li>
56537              * <li>grid - This grid</li>
56538              * <li>record - The record being edited</li>
56539              * <li>field - The field name being edited</li>
56540              * <li>value - The value being set</li>
56541              * <li>originalValue - The original value for the field, before the edit.</li>
56542              * <li>row - The grid row index</li>
56543              * <li>column - The grid column index</li>
56544              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56545              * </ul>
56546              * @param {Object} e An edit event (see above for description)
56547              */
56548             "validateedit" : true
56549         });
56550     this.on("bodyscroll", this.stopEditing,  this);
56551     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56552 };
56553
56554 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56555     /**
56556      * @cfg {Number} clicksToEdit
56557      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56558      */
56559     clicksToEdit: 2,
56560
56561     // private
56562     isEditor : true,
56563     // private
56564     trackMouseOver: false, // causes very odd FF errors
56565
56566     onCellDblClick : function(g, row, col){
56567         this.startEditing(row, col);
56568     },
56569
56570     onEditComplete : function(ed, value, startValue){
56571         this.editing = false;
56572         this.activeEditor = null;
56573         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56574         var r = ed.record;
56575         var field = this.colModel.getDataIndex(ed.col);
56576         var e = {
56577             grid: this,
56578             record: r,
56579             field: field,
56580             originalValue: startValue,
56581             value: value,
56582             row: ed.row,
56583             column: ed.col,
56584             cancel:false,
56585             editor: ed
56586         };
56587         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56588         cell.show();
56589           
56590         if(String(value) !== String(startValue)){
56591             
56592             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56593                 r.set(field, e.value);
56594                 // if we are dealing with a combo box..
56595                 // then we also set the 'name' colum to be the displayField
56596                 if (ed.field.displayField && ed.field.name) {
56597                     r.set(ed.field.name, ed.field.el.dom.value);
56598                 }
56599                 
56600                 delete e.cancel; //?? why!!!
56601                 this.fireEvent("afteredit", e);
56602             }
56603         } else {
56604             this.fireEvent("afteredit", e); // always fire it!
56605         }
56606         this.view.focusCell(ed.row, ed.col);
56607     },
56608
56609     /**
56610      * Starts editing the specified for the specified row/column
56611      * @param {Number} rowIndex
56612      * @param {Number} colIndex
56613      */
56614     startEditing : function(row, col){
56615         this.stopEditing();
56616         if(this.colModel.isCellEditable(col, row)){
56617             this.view.ensureVisible(row, col, true);
56618           
56619             var r = this.dataSource.getAt(row);
56620             var field = this.colModel.getDataIndex(col);
56621             var cell = Roo.get(this.view.getCell(row,col));
56622             var e = {
56623                 grid: this,
56624                 record: r,
56625                 field: field,
56626                 value: r.data[field],
56627                 row: row,
56628                 column: col,
56629                 cancel:false 
56630             };
56631             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56632                 this.editing = true;
56633                 var ed = this.colModel.getCellEditor(col, row);
56634                 
56635                 if (!ed) {
56636                     return;
56637                 }
56638                 if(!ed.rendered){
56639                     ed.render(ed.parentEl || document.body);
56640                 }
56641                 ed.field.reset();
56642                
56643                 cell.hide();
56644                 
56645                 (function(){ // complex but required for focus issues in safari, ie and opera
56646                     ed.row = row;
56647                     ed.col = col;
56648                     ed.record = r;
56649                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56650                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56651                     this.activeEditor = ed;
56652                     var v = r.data[field];
56653                     ed.startEdit(this.view.getCell(row, col), v);
56654                     // combo's with 'displayField and name set
56655                     if (ed.field.displayField && ed.field.name) {
56656                         ed.field.el.dom.value = r.data[ed.field.name];
56657                     }
56658                     
56659                     
56660                 }).defer(50, this);
56661             }
56662         }
56663     },
56664         
56665     /**
56666      * Stops any active editing
56667      */
56668     stopEditing : function(){
56669         if(this.activeEditor){
56670             this.activeEditor.completeEdit();
56671         }
56672         this.activeEditor = null;
56673     },
56674         
56675          /**
56676      * Called to get grid's drag proxy text, by default returns this.ddText.
56677      * @return {String}
56678      */
56679     getDragDropText : function(){
56680         var count = this.selModel.getSelectedCell() ? 1 : 0;
56681         return String.format(this.ddText, count, count == 1 ? '' : 's');
56682     }
56683         
56684 });/*
56685  * Based on:
56686  * Ext JS Library 1.1.1
56687  * Copyright(c) 2006-2007, Ext JS, LLC.
56688  *
56689  * Originally Released Under LGPL - original licence link has changed is not relivant.
56690  *
56691  * Fork - LGPL
56692  * <script type="text/javascript">
56693  */
56694
56695 // private - not really -- you end up using it !
56696 // This is a support class used internally by the Grid components
56697
56698 /**
56699  * @class Roo.grid.GridEditor
56700  * @extends Roo.Editor
56701  * Class for creating and editable grid elements.
56702  * @param {Object} config any settings (must include field)
56703  */
56704 Roo.grid.GridEditor = function(field, config){
56705     if (!config && field.field) {
56706         config = field;
56707         field = Roo.factory(config.field, Roo.form);
56708     }
56709     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56710     field.monitorTab = false;
56711 };
56712
56713 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56714     
56715     /**
56716      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56717      */
56718     
56719     alignment: "tl-tl",
56720     autoSize: "width",
56721     hideEl : false,
56722     cls: "x-small-editor x-grid-editor",
56723     shim:false,
56724     shadow:"frame"
56725 });/*
56726  * Based on:
56727  * Ext JS Library 1.1.1
56728  * Copyright(c) 2006-2007, Ext JS, LLC.
56729  *
56730  * Originally Released Under LGPL - original licence link has changed is not relivant.
56731  *
56732  * Fork - LGPL
56733  * <script type="text/javascript">
56734  */
56735   
56736
56737   
56738 Roo.grid.PropertyRecord = Roo.data.Record.create([
56739     {name:'name',type:'string'},  'value'
56740 ]);
56741
56742
56743 Roo.grid.PropertyStore = function(grid, source){
56744     this.grid = grid;
56745     this.store = new Roo.data.Store({
56746         recordType : Roo.grid.PropertyRecord
56747     });
56748     this.store.on('update', this.onUpdate,  this);
56749     if(source){
56750         this.setSource(source);
56751     }
56752     Roo.grid.PropertyStore.superclass.constructor.call(this);
56753 };
56754
56755
56756
56757 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56758     setSource : function(o){
56759         this.source = o;
56760         this.store.removeAll();
56761         var data = [];
56762         for(var k in o){
56763             if(this.isEditableValue(o[k])){
56764                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56765             }
56766         }
56767         this.store.loadRecords({records: data}, {}, true);
56768     },
56769
56770     onUpdate : function(ds, record, type){
56771         if(type == Roo.data.Record.EDIT){
56772             var v = record.data['value'];
56773             var oldValue = record.modified['value'];
56774             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56775                 this.source[record.id] = v;
56776                 record.commit();
56777                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56778             }else{
56779                 record.reject();
56780             }
56781         }
56782     },
56783
56784     getProperty : function(row){
56785        return this.store.getAt(row);
56786     },
56787
56788     isEditableValue: function(val){
56789         if(val && val instanceof Date){
56790             return true;
56791         }else if(typeof val == 'object' || typeof val == 'function'){
56792             return false;
56793         }
56794         return true;
56795     },
56796
56797     setValue : function(prop, value){
56798         this.source[prop] = value;
56799         this.store.getById(prop).set('value', value);
56800     },
56801
56802     getSource : function(){
56803         return this.source;
56804     }
56805 });
56806
56807 Roo.grid.PropertyColumnModel = function(grid, store){
56808     this.grid = grid;
56809     var g = Roo.grid;
56810     g.PropertyColumnModel.superclass.constructor.call(this, [
56811         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56812         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56813     ]);
56814     this.store = store;
56815     this.bselect = Roo.DomHelper.append(document.body, {
56816         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56817             {tag: 'option', value: 'true', html: 'true'},
56818             {tag: 'option', value: 'false', html: 'false'}
56819         ]
56820     });
56821     Roo.id(this.bselect);
56822     var f = Roo.form;
56823     this.editors = {
56824         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56825         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56826         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56827         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56828         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56829     };
56830     this.renderCellDelegate = this.renderCell.createDelegate(this);
56831     this.renderPropDelegate = this.renderProp.createDelegate(this);
56832 };
56833
56834 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56835     
56836     
56837     nameText : 'Name',
56838     valueText : 'Value',
56839     
56840     dateFormat : 'm/j/Y',
56841     
56842     
56843     renderDate : function(dateVal){
56844         return dateVal.dateFormat(this.dateFormat);
56845     },
56846
56847     renderBool : function(bVal){
56848         return bVal ? 'true' : 'false';
56849     },
56850
56851     isCellEditable : function(colIndex, rowIndex){
56852         return colIndex == 1;
56853     },
56854
56855     getRenderer : function(col){
56856         return col == 1 ?
56857             this.renderCellDelegate : this.renderPropDelegate;
56858     },
56859
56860     renderProp : function(v){
56861         return this.getPropertyName(v);
56862     },
56863
56864     renderCell : function(val){
56865         var rv = val;
56866         if(val instanceof Date){
56867             rv = this.renderDate(val);
56868         }else if(typeof val == 'boolean'){
56869             rv = this.renderBool(val);
56870         }
56871         return Roo.util.Format.htmlEncode(rv);
56872     },
56873
56874     getPropertyName : function(name){
56875         var pn = this.grid.propertyNames;
56876         return pn && pn[name] ? pn[name] : name;
56877     },
56878
56879     getCellEditor : function(colIndex, rowIndex){
56880         var p = this.store.getProperty(rowIndex);
56881         var n = p.data['name'], val = p.data['value'];
56882         
56883         if(typeof(this.grid.customEditors[n]) == 'string'){
56884             return this.editors[this.grid.customEditors[n]];
56885         }
56886         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56887             return this.grid.customEditors[n];
56888         }
56889         if(val instanceof Date){
56890             return this.editors['date'];
56891         }else if(typeof val == 'number'){
56892             return this.editors['number'];
56893         }else if(typeof val == 'boolean'){
56894             return this.editors['boolean'];
56895         }else{
56896             return this.editors['string'];
56897         }
56898     }
56899 });
56900
56901 /**
56902  * @class Roo.grid.PropertyGrid
56903  * @extends Roo.grid.EditorGrid
56904  * This class represents the  interface of a component based property grid control.
56905  * <br><br>Usage:<pre><code>
56906  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56907       
56908  });
56909  // set any options
56910  grid.render();
56911  * </code></pre>
56912   
56913  * @constructor
56914  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56915  * The container MUST have some type of size defined for the grid to fill. The container will be
56916  * automatically set to position relative if it isn't already.
56917  * @param {Object} config A config object that sets properties on this grid.
56918  */
56919 Roo.grid.PropertyGrid = function(container, config){
56920     config = config || {};
56921     var store = new Roo.grid.PropertyStore(this);
56922     this.store = store;
56923     var cm = new Roo.grid.PropertyColumnModel(this, store);
56924     store.store.sort('name', 'ASC');
56925     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56926         ds: store.store,
56927         cm: cm,
56928         enableColLock:false,
56929         enableColumnMove:false,
56930         stripeRows:false,
56931         trackMouseOver: false,
56932         clicksToEdit:1
56933     }, config));
56934     this.getGridEl().addClass('x-props-grid');
56935     this.lastEditRow = null;
56936     this.on('columnresize', this.onColumnResize, this);
56937     this.addEvents({
56938          /**
56939              * @event beforepropertychange
56940              * Fires before a property changes (return false to stop?)
56941              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56942              * @param {String} id Record Id
56943              * @param {String} newval New Value
56944          * @param {String} oldval Old Value
56945              */
56946         "beforepropertychange": true,
56947         /**
56948              * @event propertychange
56949              * Fires after a property changes
56950              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56951              * @param {String} id Record Id
56952              * @param {String} newval New Value
56953          * @param {String} oldval Old Value
56954              */
56955         "propertychange": true
56956     });
56957     this.customEditors = this.customEditors || {};
56958 };
56959 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56960     
56961      /**
56962      * @cfg {Object} customEditors map of colnames=> custom editors.
56963      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56964      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56965      * false disables editing of the field.
56966          */
56967     
56968       /**
56969      * @cfg {Object} propertyNames map of property Names to their displayed value
56970          */
56971     
56972     render : function(){
56973         Roo.grid.PropertyGrid.superclass.render.call(this);
56974         this.autoSize.defer(100, this);
56975     },
56976
56977     autoSize : function(){
56978         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56979         if(this.view){
56980             this.view.fitColumns();
56981         }
56982     },
56983
56984     onColumnResize : function(){
56985         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56986         this.autoSize();
56987     },
56988     /**
56989      * Sets the data for the Grid
56990      * accepts a Key => Value object of all the elements avaiable.
56991      * @param {Object} data  to appear in grid.
56992      */
56993     setSource : function(source){
56994         this.store.setSource(source);
56995         //this.autoSize();
56996     },
56997     /**
56998      * Gets all the data from the grid.
56999      * @return {Object} data  data stored in grid
57000      */
57001     getSource : function(){
57002         return this.store.getSource();
57003     }
57004 });/*
57005   
57006  * Licence LGPL
57007  
57008  */
57009  
57010 /**
57011  * @class Roo.grid.Calendar
57012  * @extends Roo.util.Grid
57013  * This class extends the Grid to provide a calendar widget
57014  * <br><br>Usage:<pre><code>
57015  var grid = new Roo.grid.Calendar("my-container-id", {
57016      ds: myDataStore,
57017      cm: myColModel,
57018      selModel: mySelectionModel,
57019      autoSizeColumns: true,
57020      monitorWindowResize: false,
57021      trackMouseOver: true
57022      eventstore : real data store..
57023  });
57024  // set any options
57025  grid.render();
57026   
57027   * @constructor
57028  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57029  * The container MUST have some type of size defined for the grid to fill. The container will be
57030  * automatically set to position relative if it isn't already.
57031  * @param {Object} config A config object that sets properties on this grid.
57032  */
57033 Roo.grid.Calendar = function(container, config){
57034         // initialize the container
57035         this.container = Roo.get(container);
57036         this.container.update("");
57037         this.container.setStyle("overflow", "hidden");
57038     this.container.addClass('x-grid-container');
57039
57040     this.id = this.container.id;
57041
57042     Roo.apply(this, config);
57043     // check and correct shorthanded configs
57044     
57045     var rows = [];
57046     var d =1;
57047     for (var r = 0;r < 6;r++) {
57048         
57049         rows[r]=[];
57050         for (var c =0;c < 7;c++) {
57051             rows[r][c]= '';
57052         }
57053     }
57054     if (this.eventStore) {
57055         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57056         this.eventStore.on('load',this.onLoad, this);
57057         this.eventStore.on('beforeload',this.clearEvents, this);
57058          
57059     }
57060     
57061     this.dataSource = new Roo.data.Store({
57062             proxy: new Roo.data.MemoryProxy(rows),
57063             reader: new Roo.data.ArrayReader({}, [
57064                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57065     });
57066
57067     this.dataSource.load();
57068     this.ds = this.dataSource;
57069     this.ds.xmodule = this.xmodule || false;
57070     
57071     
57072     var cellRender = function(v,x,r)
57073     {
57074         return String.format(
57075             '<div class="fc-day  fc-widget-content"><div>' +
57076                 '<div class="fc-event-container"></div>' +
57077                 '<div class="fc-day-number">{0}</div>'+
57078                 
57079                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57080             '</div></div>', v);
57081     
57082     }
57083     
57084     
57085     this.colModel = new Roo.grid.ColumnModel( [
57086         {
57087             xtype: 'ColumnModel',
57088             xns: Roo.grid,
57089             dataIndex : 'weekday0',
57090             header : 'Sunday',
57091             renderer : cellRender
57092         },
57093         {
57094             xtype: 'ColumnModel',
57095             xns: Roo.grid,
57096             dataIndex : 'weekday1',
57097             header : 'Monday',
57098             renderer : cellRender
57099         },
57100         {
57101             xtype: 'ColumnModel',
57102             xns: Roo.grid,
57103             dataIndex : 'weekday2',
57104             header : 'Tuesday',
57105             renderer : cellRender
57106         },
57107         {
57108             xtype: 'ColumnModel',
57109             xns: Roo.grid,
57110             dataIndex : 'weekday3',
57111             header : 'Wednesday',
57112             renderer : cellRender
57113         },
57114         {
57115             xtype: 'ColumnModel',
57116             xns: Roo.grid,
57117             dataIndex : 'weekday4',
57118             header : 'Thursday',
57119             renderer : cellRender
57120         },
57121         {
57122             xtype: 'ColumnModel',
57123             xns: Roo.grid,
57124             dataIndex : 'weekday5',
57125             header : 'Friday',
57126             renderer : cellRender
57127         },
57128         {
57129             xtype: 'ColumnModel',
57130             xns: Roo.grid,
57131             dataIndex : 'weekday6',
57132             header : 'Saturday',
57133             renderer : cellRender
57134         }
57135     ]);
57136     this.cm = this.colModel;
57137     this.cm.xmodule = this.xmodule || false;
57138  
57139         
57140           
57141     //this.selModel = new Roo.grid.CellSelectionModel();
57142     //this.sm = this.selModel;
57143     //this.selModel.init(this);
57144     
57145     
57146     if(this.width){
57147         this.container.setWidth(this.width);
57148     }
57149
57150     if(this.height){
57151         this.container.setHeight(this.height);
57152     }
57153     /** @private */
57154         this.addEvents({
57155         // raw events
57156         /**
57157          * @event click
57158          * The raw click event for the entire grid.
57159          * @param {Roo.EventObject} e
57160          */
57161         "click" : true,
57162         /**
57163          * @event dblclick
57164          * The raw dblclick event for the entire grid.
57165          * @param {Roo.EventObject} e
57166          */
57167         "dblclick" : true,
57168         /**
57169          * @event contextmenu
57170          * The raw contextmenu event for the entire grid.
57171          * @param {Roo.EventObject} e
57172          */
57173         "contextmenu" : true,
57174         /**
57175          * @event mousedown
57176          * The raw mousedown event for the entire grid.
57177          * @param {Roo.EventObject} e
57178          */
57179         "mousedown" : true,
57180         /**
57181          * @event mouseup
57182          * The raw mouseup event for the entire grid.
57183          * @param {Roo.EventObject} e
57184          */
57185         "mouseup" : true,
57186         /**
57187          * @event mouseover
57188          * The raw mouseover event for the entire grid.
57189          * @param {Roo.EventObject} e
57190          */
57191         "mouseover" : true,
57192         /**
57193          * @event mouseout
57194          * The raw mouseout event for the entire grid.
57195          * @param {Roo.EventObject} e
57196          */
57197         "mouseout" : true,
57198         /**
57199          * @event keypress
57200          * The raw keypress event for the entire grid.
57201          * @param {Roo.EventObject} e
57202          */
57203         "keypress" : true,
57204         /**
57205          * @event keydown
57206          * The raw keydown event for the entire grid.
57207          * @param {Roo.EventObject} e
57208          */
57209         "keydown" : true,
57210
57211         // custom events
57212
57213         /**
57214          * @event cellclick
57215          * Fires when a cell is clicked
57216          * @param {Grid} this
57217          * @param {Number} rowIndex
57218          * @param {Number} columnIndex
57219          * @param {Roo.EventObject} e
57220          */
57221         "cellclick" : true,
57222         /**
57223          * @event celldblclick
57224          * Fires when a cell is double clicked
57225          * @param {Grid} this
57226          * @param {Number} rowIndex
57227          * @param {Number} columnIndex
57228          * @param {Roo.EventObject} e
57229          */
57230         "celldblclick" : true,
57231         /**
57232          * @event rowclick
57233          * Fires when a row is clicked
57234          * @param {Grid} this
57235          * @param {Number} rowIndex
57236          * @param {Roo.EventObject} e
57237          */
57238         "rowclick" : true,
57239         /**
57240          * @event rowdblclick
57241          * Fires when a row is double clicked
57242          * @param {Grid} this
57243          * @param {Number} rowIndex
57244          * @param {Roo.EventObject} e
57245          */
57246         "rowdblclick" : true,
57247         /**
57248          * @event headerclick
57249          * Fires when a header is clicked
57250          * @param {Grid} this
57251          * @param {Number} columnIndex
57252          * @param {Roo.EventObject} e
57253          */
57254         "headerclick" : true,
57255         /**
57256          * @event headerdblclick
57257          * Fires when a header cell is double clicked
57258          * @param {Grid} this
57259          * @param {Number} columnIndex
57260          * @param {Roo.EventObject} e
57261          */
57262         "headerdblclick" : true,
57263         /**
57264          * @event rowcontextmenu
57265          * Fires when a row is right clicked
57266          * @param {Grid} this
57267          * @param {Number} rowIndex
57268          * @param {Roo.EventObject} e
57269          */
57270         "rowcontextmenu" : true,
57271         /**
57272          * @event cellcontextmenu
57273          * Fires when a cell is right clicked
57274          * @param {Grid} this
57275          * @param {Number} rowIndex
57276          * @param {Number} cellIndex
57277          * @param {Roo.EventObject} e
57278          */
57279          "cellcontextmenu" : true,
57280         /**
57281          * @event headercontextmenu
57282          * Fires when a header is right clicked
57283          * @param {Grid} this
57284          * @param {Number} columnIndex
57285          * @param {Roo.EventObject} e
57286          */
57287         "headercontextmenu" : true,
57288         /**
57289          * @event bodyscroll
57290          * Fires when the body element is scrolled
57291          * @param {Number} scrollLeft
57292          * @param {Number} scrollTop
57293          */
57294         "bodyscroll" : true,
57295         /**
57296          * @event columnresize
57297          * Fires when the user resizes a column
57298          * @param {Number} columnIndex
57299          * @param {Number} newSize
57300          */
57301         "columnresize" : true,
57302         /**
57303          * @event columnmove
57304          * Fires when the user moves a column
57305          * @param {Number} oldIndex
57306          * @param {Number} newIndex
57307          */
57308         "columnmove" : true,
57309         /**
57310          * @event startdrag
57311          * Fires when row(s) start being dragged
57312          * @param {Grid} this
57313          * @param {Roo.GridDD} dd The drag drop object
57314          * @param {event} e The raw browser event
57315          */
57316         "startdrag" : true,
57317         /**
57318          * @event enddrag
57319          * Fires when a drag operation is complete
57320          * @param {Grid} this
57321          * @param {Roo.GridDD} dd The drag drop object
57322          * @param {event} e The raw browser event
57323          */
57324         "enddrag" : true,
57325         /**
57326          * @event dragdrop
57327          * Fires when dragged row(s) are dropped on a valid DD target
57328          * @param {Grid} this
57329          * @param {Roo.GridDD} dd The drag drop object
57330          * @param {String} targetId The target drag drop object
57331          * @param {event} e The raw browser event
57332          */
57333         "dragdrop" : true,
57334         /**
57335          * @event dragover
57336          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57337          * @param {Grid} this
57338          * @param {Roo.GridDD} dd The drag drop object
57339          * @param {String} targetId The target drag drop object
57340          * @param {event} e The raw browser event
57341          */
57342         "dragover" : true,
57343         /**
57344          * @event dragenter
57345          *  Fires when the dragged row(s) first cross another DD target while being dragged
57346          * @param {Grid} this
57347          * @param {Roo.GridDD} dd The drag drop object
57348          * @param {String} targetId The target drag drop object
57349          * @param {event} e The raw browser event
57350          */
57351         "dragenter" : true,
57352         /**
57353          * @event dragout
57354          * Fires when the dragged row(s) leave another DD target while being dragged
57355          * @param {Grid} this
57356          * @param {Roo.GridDD} dd The drag drop object
57357          * @param {String} targetId The target drag drop object
57358          * @param {event} e The raw browser event
57359          */
57360         "dragout" : true,
57361         /**
57362          * @event rowclass
57363          * Fires when a row is rendered, so you can change add a style to it.
57364          * @param {GridView} gridview   The grid view
57365          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57366          */
57367         'rowclass' : true,
57368
57369         /**
57370          * @event render
57371          * Fires when the grid is rendered
57372          * @param {Grid} grid
57373          */
57374         'render' : true,
57375             /**
57376              * @event select
57377              * Fires when a date is selected
57378              * @param {DatePicker} this
57379              * @param {Date} date The selected date
57380              */
57381         'select': true,
57382         /**
57383              * @event monthchange
57384              * Fires when the displayed month changes 
57385              * @param {DatePicker} this
57386              * @param {Date} date The selected month
57387              */
57388         'monthchange': true,
57389         /**
57390              * @event evententer
57391              * Fires when mouse over an event
57392              * @param {Calendar} this
57393              * @param {event} Event
57394              */
57395         'evententer': true,
57396         /**
57397              * @event eventleave
57398              * Fires when the mouse leaves an
57399              * @param {Calendar} this
57400              * @param {event}
57401              */
57402         'eventleave': true,
57403         /**
57404              * @event eventclick
57405              * Fires when the mouse click an
57406              * @param {Calendar} this
57407              * @param {event}
57408              */
57409         'eventclick': true,
57410         /**
57411              * @event eventrender
57412              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57413              * @param {Calendar} this
57414              * @param {data} data to be modified
57415              */
57416         'eventrender': true
57417         
57418     });
57419
57420     Roo.grid.Grid.superclass.constructor.call(this);
57421     this.on('render', function() {
57422         this.view.el.addClass('x-grid-cal'); 
57423         
57424         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57425
57426     },this);
57427     
57428     if (!Roo.grid.Calendar.style) {
57429         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57430             
57431             
57432             '.x-grid-cal .x-grid-col' :  {
57433                 height: 'auto !important',
57434                 'vertical-align': 'top'
57435             },
57436             '.x-grid-cal  .fc-event-hori' : {
57437                 height: '14px'
57438             }
57439              
57440             
57441         }, Roo.id());
57442     }
57443
57444     
57445     
57446 };
57447 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57448     /**
57449      * @cfg {Store} eventStore The store that loads events.
57450      */
57451     eventStore : 25,
57452
57453      
57454     activeDate : false,
57455     startDay : 0,
57456     autoWidth : true,
57457     monitorWindowResize : false,
57458
57459     
57460     resizeColumns : function() {
57461         var col = (this.view.el.getWidth() / 7) - 3;
57462         // loop through cols, and setWidth
57463         for(var i =0 ; i < 7 ; i++){
57464             this.cm.setColumnWidth(i, col);
57465         }
57466     },
57467      setDate :function(date) {
57468         
57469         Roo.log('setDate?');
57470         
57471         this.resizeColumns();
57472         var vd = this.activeDate;
57473         this.activeDate = date;
57474 //        if(vd && this.el){
57475 //            var t = date.getTime();
57476 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57477 //                Roo.log('using add remove');
57478 //                
57479 //                this.fireEvent('monthchange', this, date);
57480 //                
57481 //                this.cells.removeClass("fc-state-highlight");
57482 //                this.cells.each(function(c){
57483 //                   if(c.dateValue == t){
57484 //                       c.addClass("fc-state-highlight");
57485 //                       setTimeout(function(){
57486 //                            try{c.dom.firstChild.focus();}catch(e){}
57487 //                       }, 50);
57488 //                       return false;
57489 //                   }
57490 //                   return true;
57491 //                });
57492 //                return;
57493 //            }
57494 //        }
57495         
57496         var days = date.getDaysInMonth();
57497         
57498         var firstOfMonth = date.getFirstDateOfMonth();
57499         var startingPos = firstOfMonth.getDay()-this.startDay;
57500         
57501         if(startingPos < this.startDay){
57502             startingPos += 7;
57503         }
57504         
57505         var pm = date.add(Date.MONTH, -1);
57506         var prevStart = pm.getDaysInMonth()-startingPos;
57507 //        
57508         
57509         
57510         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57511         
57512         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57513         //this.cells.addClassOnOver('fc-state-hover');
57514         
57515         var cells = this.cells.elements;
57516         var textEls = this.textNodes;
57517         
57518         //Roo.each(cells, function(cell){
57519         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57520         //});
57521         
57522         days += startingPos;
57523
57524         // convert everything to numbers so it's fast
57525         var day = 86400000;
57526         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57527         //Roo.log(d);
57528         //Roo.log(pm);
57529         //Roo.log(prevStart);
57530         
57531         var today = new Date().clearTime().getTime();
57532         var sel = date.clearTime().getTime();
57533         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57534         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57535         var ddMatch = this.disabledDatesRE;
57536         var ddText = this.disabledDatesText;
57537         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57538         var ddaysText = this.disabledDaysText;
57539         var format = this.format;
57540         
57541         var setCellClass = function(cal, cell){
57542             
57543             //Roo.log('set Cell Class');
57544             cell.title = "";
57545             var t = d.getTime();
57546             
57547             //Roo.log(d);
57548             
57549             
57550             cell.dateValue = t;
57551             if(t == today){
57552                 cell.className += " fc-today";
57553                 cell.className += " fc-state-highlight";
57554                 cell.title = cal.todayText;
57555             }
57556             if(t == sel){
57557                 // disable highlight in other month..
57558                 cell.className += " fc-state-highlight";
57559                 
57560             }
57561             // disabling
57562             if(t < min) {
57563                 //cell.className = " fc-state-disabled";
57564                 cell.title = cal.minText;
57565                 return;
57566             }
57567             if(t > max) {
57568                 //cell.className = " fc-state-disabled";
57569                 cell.title = cal.maxText;
57570                 return;
57571             }
57572             if(ddays){
57573                 if(ddays.indexOf(d.getDay()) != -1){
57574                     // cell.title = ddaysText;
57575                    // cell.className = " fc-state-disabled";
57576                 }
57577             }
57578             if(ddMatch && format){
57579                 var fvalue = d.dateFormat(format);
57580                 if(ddMatch.test(fvalue)){
57581                     cell.title = ddText.replace("%0", fvalue);
57582                    cell.className = " fc-state-disabled";
57583                 }
57584             }
57585             
57586             if (!cell.initialClassName) {
57587                 cell.initialClassName = cell.dom.className;
57588             }
57589             
57590             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57591         };
57592
57593         var i = 0;
57594         
57595         for(; i < startingPos; i++) {
57596             cells[i].dayName =  (++prevStart);
57597             Roo.log(textEls[i]);
57598             d.setDate(d.getDate()+1);
57599             
57600             //cells[i].className = "fc-past fc-other-month";
57601             setCellClass(this, cells[i]);
57602         }
57603         
57604         var intDay = 0;
57605         
57606         for(; i < days; i++){
57607             intDay = i - startingPos + 1;
57608             cells[i].dayName =  (intDay);
57609             d.setDate(d.getDate()+1);
57610             
57611             cells[i].className = ''; // "x-date-active";
57612             setCellClass(this, cells[i]);
57613         }
57614         var extraDays = 0;
57615         
57616         for(; i < 42; i++) {
57617             //textEls[i].innerHTML = (++extraDays);
57618             
57619             d.setDate(d.getDate()+1);
57620             cells[i].dayName = (++extraDays);
57621             cells[i].className = "fc-future fc-other-month";
57622             setCellClass(this, cells[i]);
57623         }
57624         
57625         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57626         
57627         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57628         
57629         // this will cause all the cells to mis
57630         var rows= [];
57631         var i =0;
57632         for (var r = 0;r < 6;r++) {
57633             for (var c =0;c < 7;c++) {
57634                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57635             }    
57636         }
57637         
57638         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57639         for(i=0;i<cells.length;i++) {
57640             
57641             this.cells.elements[i].dayName = cells[i].dayName ;
57642             this.cells.elements[i].className = cells[i].className;
57643             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57644             this.cells.elements[i].title = cells[i].title ;
57645             this.cells.elements[i].dateValue = cells[i].dateValue ;
57646         }
57647         
57648         
57649         
57650         
57651         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57652         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57653         
57654         ////if(totalRows != 6){
57655             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57656            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57657        // }
57658         
57659         this.fireEvent('monthchange', this, date);
57660         
57661         
57662     },
57663  /**
57664      * Returns the grid's SelectionModel.
57665      * @return {SelectionModel}
57666      */
57667     getSelectionModel : function(){
57668         if(!this.selModel){
57669             this.selModel = new Roo.grid.CellSelectionModel();
57670         }
57671         return this.selModel;
57672     },
57673
57674     load: function() {
57675         this.eventStore.load()
57676         
57677         
57678         
57679     },
57680     
57681     findCell : function(dt) {
57682         dt = dt.clearTime().getTime();
57683         var ret = false;
57684         this.cells.each(function(c){
57685             //Roo.log("check " +c.dateValue + '?=' + dt);
57686             if(c.dateValue == dt){
57687                 ret = c;
57688                 return false;
57689             }
57690             return true;
57691         });
57692         
57693         return ret;
57694     },
57695     
57696     findCells : function(rec) {
57697         var s = rec.data.start_dt.clone().clearTime().getTime();
57698        // Roo.log(s);
57699         var e= rec.data.end_dt.clone().clearTime().getTime();
57700        // Roo.log(e);
57701         var ret = [];
57702         this.cells.each(function(c){
57703              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57704             
57705             if(c.dateValue > e){
57706                 return ;
57707             }
57708             if(c.dateValue < s){
57709                 return ;
57710             }
57711             ret.push(c);
57712         });
57713         
57714         return ret;    
57715     },
57716     
57717     findBestRow: function(cells)
57718     {
57719         var ret = 0;
57720         
57721         for (var i =0 ; i < cells.length;i++) {
57722             ret  = Math.max(cells[i].rows || 0,ret);
57723         }
57724         return ret;
57725         
57726     },
57727     
57728     
57729     addItem : function(rec)
57730     {
57731         // look for vertical location slot in
57732         var cells = this.findCells(rec);
57733         
57734         rec.row = this.findBestRow(cells);
57735         
57736         // work out the location.
57737         
57738         var crow = false;
57739         var rows = [];
57740         for(var i =0; i < cells.length; i++) {
57741             if (!crow) {
57742                 crow = {
57743                     start : cells[i],
57744                     end :  cells[i]
57745                 };
57746                 continue;
57747             }
57748             if (crow.start.getY() == cells[i].getY()) {
57749                 // on same row.
57750                 crow.end = cells[i];
57751                 continue;
57752             }
57753             // different row.
57754             rows.push(crow);
57755             crow = {
57756                 start: cells[i],
57757                 end : cells[i]
57758             };
57759             
57760         }
57761         
57762         rows.push(crow);
57763         rec.els = [];
57764         rec.rows = rows;
57765         rec.cells = cells;
57766         for (var i = 0; i < cells.length;i++) {
57767             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57768             
57769         }
57770         
57771         
57772     },
57773     
57774     clearEvents: function() {
57775         
57776         if (!this.eventStore.getCount()) {
57777             return;
57778         }
57779         // reset number of rows in cells.
57780         Roo.each(this.cells.elements, function(c){
57781             c.rows = 0;
57782         });
57783         
57784         this.eventStore.each(function(e) {
57785             this.clearEvent(e);
57786         },this);
57787         
57788     },
57789     
57790     clearEvent : function(ev)
57791     {
57792         if (ev.els) {
57793             Roo.each(ev.els, function(el) {
57794                 el.un('mouseenter' ,this.onEventEnter, this);
57795                 el.un('mouseleave' ,this.onEventLeave, this);
57796                 el.remove();
57797             },this);
57798             ev.els = [];
57799         }
57800     },
57801     
57802     
57803     renderEvent : function(ev,ctr) {
57804         if (!ctr) {
57805              ctr = this.view.el.select('.fc-event-container',true).first();
57806         }
57807         
57808          
57809         this.clearEvent(ev);
57810             //code
57811        
57812         
57813         
57814         ev.els = [];
57815         var cells = ev.cells;
57816         var rows = ev.rows;
57817         this.fireEvent('eventrender', this, ev);
57818         
57819         for(var i =0; i < rows.length; i++) {
57820             
57821             cls = '';
57822             if (i == 0) {
57823                 cls += ' fc-event-start';
57824             }
57825             if ((i+1) == rows.length) {
57826                 cls += ' fc-event-end';
57827             }
57828             
57829             //Roo.log(ev.data);
57830             // how many rows should it span..
57831             var cg = this.eventTmpl.append(ctr,Roo.apply({
57832                 fccls : cls
57833                 
57834             }, ev.data) , true);
57835             
57836             
57837             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57838             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57839             cg.on('click', this.onEventClick, this, ev);
57840             
57841             ev.els.push(cg);
57842             
57843             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57844             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57845             //Roo.log(cg);
57846              
57847             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57848             cg.setWidth(ebox.right - sbox.x -2);
57849         }
57850     },
57851     
57852     renderEvents: function()
57853     {   
57854         // first make sure there is enough space..
57855         
57856         if (!this.eventTmpl) {
57857             this.eventTmpl = new Roo.Template(
57858                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57859                     '<div class="fc-event-inner">' +
57860                         '<span class="fc-event-time">{time}</span>' +
57861                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57862                     '</div>' +
57863                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57864                 '</div>'
57865             );
57866                 
57867         }
57868                
57869         
57870         
57871         this.cells.each(function(c) {
57872             //Roo.log(c.select('.fc-day-content div',true).first());
57873             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57874         });
57875         
57876         var ctr = this.view.el.select('.fc-event-container',true).first();
57877         
57878         var cls;
57879         this.eventStore.each(function(ev){
57880             
57881             this.renderEvent(ev);
57882              
57883              
57884         }, this);
57885         this.view.layout();
57886         
57887     },
57888     
57889     onEventEnter: function (e, el,event,d) {
57890         this.fireEvent('evententer', this, el, event);
57891     },
57892     
57893     onEventLeave: function (e, el,event,d) {
57894         this.fireEvent('eventleave', this, el, event);
57895     },
57896     
57897     onEventClick: function (e, el,event,d) {
57898         this.fireEvent('eventclick', this, el, event);
57899     },
57900     
57901     onMonthChange: function () {
57902         this.store.load();
57903     },
57904     
57905     onLoad: function () {
57906         
57907         //Roo.log('calendar onload');
57908 //         
57909         if(this.eventStore.getCount() > 0){
57910             
57911            
57912             
57913             this.eventStore.each(function(d){
57914                 
57915                 
57916                 // FIXME..
57917                 var add =   d.data;
57918                 if (typeof(add.end_dt) == 'undefined')  {
57919                     Roo.log("Missing End time in calendar data: ");
57920                     Roo.log(d);
57921                     return;
57922                 }
57923                 if (typeof(add.start_dt) == 'undefined')  {
57924                     Roo.log("Missing Start time in calendar data: ");
57925                     Roo.log(d);
57926                     return;
57927                 }
57928                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57929                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57930                 add.id = add.id || d.id;
57931                 add.title = add.title || '??';
57932                 
57933                 this.addItem(d);
57934                 
57935              
57936             },this);
57937         }
57938         
57939         this.renderEvents();
57940     }
57941     
57942
57943 });
57944 /*
57945  grid : {
57946                 xtype: 'Grid',
57947                 xns: Roo.grid,
57948                 listeners : {
57949                     render : function ()
57950                     {
57951                         _this.grid = this;
57952                         
57953                         if (!this.view.el.hasClass('course-timesheet')) {
57954                             this.view.el.addClass('course-timesheet');
57955                         }
57956                         if (this.tsStyle) {
57957                             this.ds.load({});
57958                             return; 
57959                         }
57960                         Roo.log('width');
57961                         Roo.log(_this.grid.view.el.getWidth());
57962                         
57963                         
57964                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57965                             '.course-timesheet .x-grid-row' : {
57966                                 height: '80px'
57967                             },
57968                             '.x-grid-row td' : {
57969                                 'vertical-align' : 0
57970                             },
57971                             '.course-edit-link' : {
57972                                 'color' : 'blue',
57973                                 'text-overflow' : 'ellipsis',
57974                                 'overflow' : 'hidden',
57975                                 'white-space' : 'nowrap',
57976                                 'cursor' : 'pointer'
57977                             },
57978                             '.sub-link' : {
57979                                 'color' : 'green'
57980                             },
57981                             '.de-act-sup-link' : {
57982                                 'color' : 'purple',
57983                                 'text-decoration' : 'line-through'
57984                             },
57985                             '.de-act-link' : {
57986                                 'color' : 'red',
57987                                 'text-decoration' : 'line-through'
57988                             },
57989                             '.course-timesheet .course-highlight' : {
57990                                 'border-top-style': 'dashed !important',
57991                                 'border-bottom-bottom': 'dashed !important'
57992                             },
57993                             '.course-timesheet .course-item' : {
57994                                 'font-family'   : 'tahoma, arial, helvetica',
57995                                 'font-size'     : '11px',
57996                                 'overflow'      : 'hidden',
57997                                 'padding-left'  : '10px',
57998                                 'padding-right' : '10px',
57999                                 'padding-top' : '10px' 
58000                             }
58001                             
58002                         }, Roo.id());
58003                                 this.ds.load({});
58004                     }
58005                 },
58006                 autoWidth : true,
58007                 monitorWindowResize : false,
58008                 cellrenderer : function(v,x,r)
58009                 {
58010                     return v;
58011                 },
58012                 sm : {
58013                     xtype: 'CellSelectionModel',
58014                     xns: Roo.grid
58015                 },
58016                 dataSource : {
58017                     xtype: 'Store',
58018                     xns: Roo.data,
58019                     listeners : {
58020                         beforeload : function (_self, options)
58021                         {
58022                             options.params = options.params || {};
58023                             options.params._month = _this.monthField.getValue();
58024                             options.params.limit = 9999;
58025                             options.params['sort'] = 'when_dt';    
58026                             options.params['dir'] = 'ASC';    
58027                             this.proxy.loadResponse = this.loadResponse;
58028                             Roo.log("load?");
58029                             //this.addColumns();
58030                         },
58031                         load : function (_self, records, options)
58032                         {
58033                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58034                                 // if you click on the translation.. you can edit it...
58035                                 var el = Roo.get(this);
58036                                 var id = el.dom.getAttribute('data-id');
58037                                 var d = el.dom.getAttribute('data-date');
58038                                 var t = el.dom.getAttribute('data-time');
58039                                 //var id = this.child('span').dom.textContent;
58040                                 
58041                                 //Roo.log(this);
58042                                 Pman.Dialog.CourseCalendar.show({
58043                                     id : id,
58044                                     when_d : d,
58045                                     when_t : t,
58046                                     productitem_active : id ? 1 : 0
58047                                 }, function() {
58048                                     _this.grid.ds.load({});
58049                                 });
58050                            
58051                            });
58052                            
58053                            _this.panel.fireEvent('resize', [ '', '' ]);
58054                         }
58055                     },
58056                     loadResponse : function(o, success, response){
58057                             // this is overridden on before load..
58058                             
58059                             Roo.log("our code?");       
58060                             //Roo.log(success);
58061                             //Roo.log(response)
58062                             delete this.activeRequest;
58063                             if(!success){
58064                                 this.fireEvent("loadexception", this, o, response);
58065                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58066                                 return;
58067                             }
58068                             var result;
58069                             try {
58070                                 result = o.reader.read(response);
58071                             }catch(e){
58072                                 Roo.log("load exception?");
58073                                 this.fireEvent("loadexception", this, o, response, e);
58074                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58075                                 return;
58076                             }
58077                             Roo.log("ready...");        
58078                             // loop through result.records;
58079                             // and set this.tdate[date] = [] << array of records..
58080                             _this.tdata  = {};
58081                             Roo.each(result.records, function(r){
58082                                 //Roo.log(r.data);
58083                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58084                                     _this.tdata[r.data.when_dt.format('j')] = [];
58085                                 }
58086                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58087                             });
58088                             
58089                             //Roo.log(_this.tdata);
58090                             
58091                             result.records = [];
58092                             result.totalRecords = 6;
58093                     
58094                             // let's generate some duumy records for the rows.
58095                             //var st = _this.dateField.getValue();
58096                             
58097                             // work out monday..
58098                             //st = st.add(Date.DAY, -1 * st.format('w'));
58099                             
58100                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58101                             
58102                             var firstOfMonth = date.getFirstDayOfMonth();
58103                             var days = date.getDaysInMonth();
58104                             var d = 1;
58105                             var firstAdded = false;
58106                             for (var i = 0; i < result.totalRecords ; i++) {
58107                                 //var d= st.add(Date.DAY, i);
58108                                 var row = {};
58109                                 var added = 0;
58110                                 for(var w = 0 ; w < 7 ; w++){
58111                                     if(!firstAdded && firstOfMonth != w){
58112                                         continue;
58113                                     }
58114                                     if(d > days){
58115                                         continue;
58116                                     }
58117                                     firstAdded = true;
58118                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58119                                     row['weekday'+w] = String.format(
58120                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58121                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58122                                                     d,
58123                                                     date.format('Y-m-')+dd
58124                                                 );
58125                                     added++;
58126                                     if(typeof(_this.tdata[d]) != 'undefined'){
58127                                         Roo.each(_this.tdata[d], function(r){
58128                                             var is_sub = '';
58129                                             var deactive = '';
58130                                             var id = r.id;
58131                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58132                                             if(r.parent_id*1>0){
58133                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58134                                                 id = r.parent_id;
58135                                             }
58136                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58137                                                 deactive = 'de-act-link';
58138                                             }
58139                                             
58140                                             row['weekday'+w] += String.format(
58141                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58142                                                     id, //0
58143                                                     r.product_id_name, //1
58144                                                     r.when_dt.format('h:ia'), //2
58145                                                     is_sub, //3
58146                                                     deactive, //4
58147                                                     desc // 5
58148                                             );
58149                                         });
58150                                     }
58151                                     d++;
58152                                 }
58153                                 
58154                                 // only do this if something added..
58155                                 if(added > 0){ 
58156                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58157                                 }
58158                                 
58159                                 
58160                                 // push it twice. (second one with an hour..
58161                                 
58162                             }
58163                             //Roo.log(result);
58164                             this.fireEvent("load", this, o, o.request.arg);
58165                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58166                         },
58167                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58168                     proxy : {
58169                         xtype: 'HttpProxy',
58170                         xns: Roo.data,
58171                         method : 'GET',
58172                         url : baseURL + '/Roo/Shop_course.php'
58173                     },
58174                     reader : {
58175                         xtype: 'JsonReader',
58176                         xns: Roo.data,
58177                         id : 'id',
58178                         fields : [
58179                             {
58180                                 'name': 'id',
58181                                 'type': 'int'
58182                             },
58183                             {
58184                                 'name': 'when_dt',
58185                                 'type': 'string'
58186                             },
58187                             {
58188                                 'name': 'end_dt',
58189                                 'type': 'string'
58190                             },
58191                             {
58192                                 'name': 'parent_id',
58193                                 'type': 'int'
58194                             },
58195                             {
58196                                 'name': 'product_id',
58197                                 'type': 'int'
58198                             },
58199                             {
58200                                 'name': 'productitem_id',
58201                                 'type': 'int'
58202                             },
58203                             {
58204                                 'name': 'guid',
58205                                 'type': 'int'
58206                             }
58207                         ]
58208                     }
58209                 },
58210                 toolbar : {
58211                     xtype: 'Toolbar',
58212                     xns: Roo,
58213                     items : [
58214                         {
58215                             xtype: 'Button',
58216                             xns: Roo.Toolbar,
58217                             listeners : {
58218                                 click : function (_self, e)
58219                                 {
58220                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58221                                     sd.setMonth(sd.getMonth()-1);
58222                                     _this.monthField.setValue(sd.format('Y-m-d'));
58223                                     _this.grid.ds.load({});
58224                                 }
58225                             },
58226                             text : "Back"
58227                         },
58228                         {
58229                             xtype: 'Separator',
58230                             xns: Roo.Toolbar
58231                         },
58232                         {
58233                             xtype: 'MonthField',
58234                             xns: Roo.form,
58235                             listeners : {
58236                                 render : function (_self)
58237                                 {
58238                                     _this.monthField = _self;
58239                                    // _this.monthField.set  today
58240                                 },
58241                                 select : function (combo, date)
58242                                 {
58243                                     _this.grid.ds.load({});
58244                                 }
58245                             },
58246                             value : (function() { return new Date(); })()
58247                         },
58248                         {
58249                             xtype: 'Separator',
58250                             xns: Roo.Toolbar
58251                         },
58252                         {
58253                             xtype: 'TextItem',
58254                             xns: Roo.Toolbar,
58255                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58256                         },
58257                         {
58258                             xtype: 'Fill',
58259                             xns: Roo.Toolbar
58260                         },
58261                         {
58262                             xtype: 'Button',
58263                             xns: Roo.Toolbar,
58264                             listeners : {
58265                                 click : function (_self, e)
58266                                 {
58267                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58268                                     sd.setMonth(sd.getMonth()+1);
58269                                     _this.monthField.setValue(sd.format('Y-m-d'));
58270                                     _this.grid.ds.load({});
58271                                 }
58272                             },
58273                             text : "Next"
58274                         }
58275                     ]
58276                 },
58277                  
58278             }
58279         };
58280         
58281         *//*
58282  * Based on:
58283  * Ext JS Library 1.1.1
58284  * Copyright(c) 2006-2007, Ext JS, LLC.
58285  *
58286  * Originally Released Under LGPL - original licence link has changed is not relivant.
58287  *
58288  * Fork - LGPL
58289  * <script type="text/javascript">
58290  */
58291  
58292 /**
58293  * @class Roo.LoadMask
58294  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58295  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58296  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58297  * element's UpdateManager load indicator and will be destroyed after the initial load.
58298  * @constructor
58299  * Create a new LoadMask
58300  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58301  * @param {Object} config The config object
58302  */
58303 Roo.LoadMask = function(el, config){
58304     this.el = Roo.get(el);
58305     Roo.apply(this, config);
58306     if(this.store){
58307         this.store.on('beforeload', this.onBeforeLoad, this);
58308         this.store.on('load', this.onLoad, this);
58309         this.store.on('loadexception', this.onLoadException, this);
58310         this.removeMask = false;
58311     }else{
58312         var um = this.el.getUpdateManager();
58313         um.showLoadIndicator = false; // disable the default indicator
58314         um.on('beforeupdate', this.onBeforeLoad, this);
58315         um.on('update', this.onLoad, this);
58316         um.on('failure', this.onLoad, this);
58317         this.removeMask = true;
58318     }
58319 };
58320
58321 Roo.LoadMask.prototype = {
58322     /**
58323      * @cfg {Boolean} removeMask
58324      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58325      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58326      */
58327     /**
58328      * @cfg {String} msg
58329      * The text to display in a centered loading message box (defaults to 'Loading...')
58330      */
58331     msg : 'Loading...',
58332     /**
58333      * @cfg {String} msgCls
58334      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58335      */
58336     msgCls : 'x-mask-loading',
58337
58338     /**
58339      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58340      * @type Boolean
58341      */
58342     disabled: false,
58343
58344     /**
58345      * Disables the mask to prevent it from being displayed
58346      */
58347     disable : function(){
58348        this.disabled = true;
58349     },
58350
58351     /**
58352      * Enables the mask so that it can be displayed
58353      */
58354     enable : function(){
58355         this.disabled = false;
58356     },
58357     
58358     onLoadException : function()
58359     {
58360         Roo.log(arguments);
58361         
58362         if (typeof(arguments[3]) != 'undefined') {
58363             Roo.MessageBox.alert("Error loading",arguments[3]);
58364         } 
58365         /*
58366         try {
58367             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58368                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58369             }   
58370         } catch(e) {
58371             
58372         }
58373         */
58374     
58375         
58376         
58377         this.el.unmask(this.removeMask);
58378     },
58379     // private
58380     onLoad : function()
58381     {
58382         this.el.unmask(this.removeMask);
58383     },
58384
58385     // private
58386     onBeforeLoad : function(){
58387         if(!this.disabled){
58388             this.el.mask(this.msg, this.msgCls);
58389         }
58390     },
58391
58392     // private
58393     destroy : function(){
58394         if(this.store){
58395             this.store.un('beforeload', this.onBeforeLoad, this);
58396             this.store.un('load', this.onLoad, this);
58397             this.store.un('loadexception', this.onLoadException, this);
58398         }else{
58399             var um = this.el.getUpdateManager();
58400             um.un('beforeupdate', this.onBeforeLoad, this);
58401             um.un('update', this.onLoad, this);
58402             um.un('failure', this.onLoad, this);
58403         }
58404     }
58405 };/*
58406  * Based on:
58407  * Ext JS Library 1.1.1
58408  * Copyright(c) 2006-2007, Ext JS, LLC.
58409  *
58410  * Originally Released Under LGPL - original licence link has changed is not relivant.
58411  *
58412  * Fork - LGPL
58413  * <script type="text/javascript">
58414  */
58415
58416
58417 /**
58418  * @class Roo.XTemplate
58419  * @extends Roo.Template
58420  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58421 <pre><code>
58422 var t = new Roo.XTemplate(
58423         '&lt;select name="{name}"&gt;',
58424                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58425         '&lt;/select&gt;'
58426 );
58427  
58428 // then append, applying the master template values
58429  </code></pre>
58430  *
58431  * Supported features:
58432  *
58433  *  Tags:
58434
58435 <pre><code>
58436       {a_variable} - output encoded.
58437       {a_variable.format:("Y-m-d")} - call a method on the variable
58438       {a_variable:raw} - unencoded output
58439       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58440       {a_variable:this.method_on_template(...)} - call a method on the template object.
58441  
58442 </code></pre>
58443  *  The tpl tag:
58444 <pre><code>
58445         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58446         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58447         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58448         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58449   
58450         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58451         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58452 </code></pre>
58453  *      
58454  */
58455 Roo.XTemplate = function()
58456 {
58457     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58458     if (this.html) {
58459         this.compile();
58460     }
58461 };
58462
58463
58464 Roo.extend(Roo.XTemplate, Roo.Template, {
58465
58466     /**
58467      * The various sub templates
58468      */
58469     tpls : false,
58470     /**
58471      *
58472      * basic tag replacing syntax
58473      * WORD:WORD()
58474      *
58475      * // you can fake an object call by doing this
58476      *  x.t:(test,tesT) 
58477      * 
58478      */
58479     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58480
58481     /**
58482      * compile the template
58483      *
58484      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58485      *
58486      */
58487     compile: function()
58488     {
58489         var s = this.html;
58490      
58491         s = ['<tpl>', s, '</tpl>'].join('');
58492     
58493         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58494             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58495             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58496             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58497             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58498             m,
58499             id     = 0,
58500             tpls   = [];
58501     
58502         while(true == !!(m = s.match(re))){
58503             var forMatch   = m[0].match(nameRe),
58504                 ifMatch   = m[0].match(ifRe),
58505                 execMatch   = m[0].match(execRe),
58506                 namedMatch   = m[0].match(namedRe),
58507                 
58508                 exp  = null, 
58509                 fn   = null,
58510                 exec = null,
58511                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58512                 
58513             if (ifMatch) {
58514                 // if - puts fn into test..
58515                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58516                 if(exp){
58517                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58518                 }
58519             }
58520             
58521             if (execMatch) {
58522                 // exec - calls a function... returns empty if true is  returned.
58523                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58524                 if(exp){
58525                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58526                 }
58527             }
58528             
58529             
58530             if (name) {
58531                 // for = 
58532                 switch(name){
58533                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58534                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58535                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58536                 }
58537             }
58538             var uid = namedMatch ? namedMatch[1] : id;
58539             
58540             
58541             tpls.push({
58542                 id:     namedMatch ? namedMatch[1] : id,
58543                 target: name,
58544                 exec:   exec,
58545                 test:   fn,
58546                 body:   m[1] || ''
58547             });
58548             if (namedMatch) {
58549                 s = s.replace(m[0], '');
58550             } else { 
58551                 s = s.replace(m[0], '{xtpl'+ id + '}');
58552             }
58553             ++id;
58554         }
58555         this.tpls = [];
58556         for(var i = tpls.length-1; i >= 0; --i){
58557             this.compileTpl(tpls[i]);
58558             this.tpls[tpls[i].id] = tpls[i];
58559         }
58560         this.master = tpls[tpls.length-1];
58561         return this;
58562     },
58563     /**
58564      * same as applyTemplate, except it's done to one of the subTemplates
58565      * when using named templates, you can do:
58566      *
58567      * var str = pl.applySubTemplate('your-name', values);
58568      *
58569      * 
58570      * @param {Number} id of the template
58571      * @param {Object} values to apply to template
58572      * @param {Object} parent (normaly the instance of this object)
58573      */
58574     applySubTemplate : function(id, values, parent)
58575     {
58576         
58577         
58578         var t = this.tpls[id];
58579         
58580         
58581         try { 
58582             if(t.test && !t.test.call(this, values, parent)){
58583                 return '';
58584             }
58585         } catch(e) {
58586             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58587             Roo.log(e.toString());
58588             Roo.log(t.test);
58589             return ''
58590         }
58591         try { 
58592             
58593             if(t.exec && t.exec.call(this, values, parent)){
58594                 return '';
58595             }
58596         } catch(e) {
58597             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58598             Roo.log(e.toString());
58599             Roo.log(t.exec);
58600             return ''
58601         }
58602         try {
58603             var vs = t.target ? t.target.call(this, values, parent) : values;
58604             parent = t.target ? values : parent;
58605             if(t.target && vs instanceof Array){
58606                 var buf = [];
58607                 for(var i = 0, len = vs.length; i < len; i++){
58608                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58609                 }
58610                 return buf.join('');
58611             }
58612             return t.compiled.call(this, vs, parent);
58613         } catch (e) {
58614             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58615             Roo.log(e.toString());
58616             Roo.log(t.compiled);
58617             return '';
58618         }
58619     },
58620
58621     compileTpl : function(tpl)
58622     {
58623         var fm = Roo.util.Format;
58624         var useF = this.disableFormats !== true;
58625         var sep = Roo.isGecko ? "+" : ",";
58626         var undef = function(str) {
58627             Roo.log("Property not found :"  + str);
58628             return '';
58629         };
58630         
58631         var fn = function(m, name, format, args)
58632         {
58633             //Roo.log(arguments);
58634             args = args ? args.replace(/\\'/g,"'") : args;
58635             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58636             if (typeof(format) == 'undefined') {
58637                 format= 'htmlEncode';
58638             }
58639             if (format == 'raw' ) {
58640                 format = false;
58641             }
58642             
58643             if(name.substr(0, 4) == 'xtpl'){
58644                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58645             }
58646             
58647             // build an array of options to determine if value is undefined..
58648             
58649             // basically get 'xxxx.yyyy' then do
58650             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58651             //    (function () { Roo.log("Property not found"); return ''; })() :
58652             //    ......
58653             
58654             var udef_ar = [];
58655             var lookfor = '';
58656             Roo.each(name.split('.'), function(st) {
58657                 lookfor += (lookfor.length ? '.': '') + st;
58658                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58659             });
58660             
58661             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58662             
58663             
58664             if(format && useF){
58665                 
58666                 args = args ? ',' + args : "";
58667                  
58668                 if(format.substr(0, 5) != "this."){
58669                     format = "fm." + format + '(';
58670                 }else{
58671                     format = 'this.call("'+ format.substr(5) + '", ';
58672                     args = ", values";
58673                 }
58674                 
58675                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58676             }
58677              
58678             if (args.length) {
58679                 // called with xxyx.yuu:(test,test)
58680                 // change to ()
58681                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58682             }
58683             // raw.. - :raw modifier..
58684             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58685             
58686         };
58687         var body;
58688         // branched to use + in gecko and [].join() in others
58689         if(Roo.isGecko){
58690             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58691                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58692                     "';};};";
58693         }else{
58694             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58695             body.push(tpl.body.replace(/(\r\n|\n)/g,
58696                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58697             body.push("'].join('');};};");
58698             body = body.join('');
58699         }
58700         
58701         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58702        
58703         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58704         eval(body);
58705         
58706         return this;
58707     },
58708
58709     applyTemplate : function(values){
58710         return this.master.compiled.call(this, values, {});
58711         //var s = this.subs;
58712     },
58713
58714     apply : function(){
58715         return this.applyTemplate.apply(this, arguments);
58716     }
58717
58718  });
58719
58720 Roo.XTemplate.from = function(el){
58721     el = Roo.getDom(el);
58722     return new Roo.XTemplate(el.value || el.innerHTML);
58723 };