roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         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  * @cfg {Array} fields Array of field definition objects
22618  * @constructor
22619  * Create a new JsonReader
22620  * @param {Object} meta Metadata configuration options
22621  * @param {Object} recordType Either an Array of field definition objects,
22622  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22623  */
22624 Roo.data.JsonReader = function(meta, recordType){
22625     
22626     meta = meta || {};
22627     // set some defaults:
22628     Roo.applyIf(meta, {
22629         totalProperty: 'total',
22630         successProperty : 'success',
22631         root : 'data',
22632         id : 'id'
22633     });
22634     
22635     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22636 };
22637 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22638     
22639     /**
22640      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22641      * Used by Store query builder to append _requestMeta to params.
22642      * 
22643      */
22644     metaFromRemote : false,
22645     /**
22646      * This method is only used by a DataProxy which has retrieved data from a remote server.
22647      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22648      * @return {Object} data A data block which is used by an Roo.data.Store object as
22649      * a cache of Roo.data.Records.
22650      */
22651     read : function(response){
22652         var json = response.responseText;
22653        
22654         var o = /* eval:var:o */ eval("("+json+")");
22655         if(!o) {
22656             throw {message: "JsonReader.read: Json object not found"};
22657         }
22658         
22659         if(o.metaData){
22660             
22661             delete this.ef;
22662             this.metaFromRemote = true;
22663             this.meta = o.metaData;
22664             this.recordType = Roo.data.Record.create(o.metaData.fields);
22665             this.onMetaChange(this.meta, this.recordType, o);
22666         }
22667         return this.readRecords(o);
22668     },
22669
22670     // private function a store will implement
22671     onMetaChange : function(meta, recordType, o){
22672
22673     },
22674
22675     /**
22676          * @ignore
22677          */
22678     simpleAccess: function(obj, subsc) {
22679         return obj[subsc];
22680     },
22681
22682         /**
22683          * @ignore
22684          */
22685     getJsonAccessor: function(){
22686         var re = /[\[\.]/;
22687         return function(expr) {
22688             try {
22689                 return(re.test(expr))
22690                     ? new Function("obj", "return obj." + expr)
22691                     : function(obj){
22692                         return obj[expr];
22693                     };
22694             } catch(e){}
22695             return Roo.emptyFn;
22696         };
22697     }(),
22698
22699     /**
22700      * Create a data block containing Roo.data.Records from an XML document.
22701      * @param {Object} o An object which contains an Array of row objects in the property specified
22702      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22703      * which contains the total size of the dataset.
22704      * @return {Object} data A data block which is used by an Roo.data.Store object as
22705      * a cache of Roo.data.Records.
22706      */
22707     readRecords : function(o){
22708         /**
22709          * After any data loads, the raw JSON data is available for further custom processing.
22710          * @type Object
22711          */
22712         this.o = o;
22713         var s = this.meta, Record = this.recordType,
22714             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22715
22716 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22717         if (!this.ef) {
22718             if(s.totalProperty) {
22719                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22720                 }
22721                 if(s.successProperty) {
22722                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22723                 }
22724                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22725                 if (s.id) {
22726                         var g = this.getJsonAccessor(s.id);
22727                         this.getId = function(rec) {
22728                                 var r = g(rec);  
22729                                 return (r === undefined || r === "") ? null : r;
22730                         };
22731                 } else {
22732                         this.getId = function(){return null;};
22733                 }
22734             this.ef = [];
22735             for(var jj = 0; jj < fl; jj++){
22736                 f = fi[jj];
22737                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22738                 this.ef[jj] = this.getJsonAccessor(map);
22739             }
22740         }
22741
22742         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22743         if(s.totalProperty){
22744             var vt = parseInt(this.getTotal(o), 10);
22745             if(!isNaN(vt)){
22746                 totalRecords = vt;
22747             }
22748         }
22749         if(s.successProperty){
22750             var vs = this.getSuccess(o);
22751             if(vs === false || vs === 'false'){
22752                 success = false;
22753             }
22754         }
22755         var records = [];
22756         for(var i = 0; i < c; i++){
22757                 var n = root[i];
22758             var values = {};
22759             var id = this.getId(n);
22760             for(var j = 0; j < fl; j++){
22761                 f = fi[j];
22762             var v = this.ef[j](n);
22763             if (!f.convert) {
22764                 Roo.log('missing convert for ' + f.name);
22765                 Roo.log(f);
22766                 continue;
22767             }
22768             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22769             }
22770             var record = new Record(values, id);
22771             record.json = n;
22772             records[i] = record;
22773         }
22774         return {
22775             raw : o,
22776             success : success,
22777             records : records,
22778             totalRecords : totalRecords
22779         };
22780     }
22781 });/*
22782  * Based on:
22783  * Ext JS Library 1.1.1
22784  * Copyright(c) 2006-2007, Ext JS, LLC.
22785  *
22786  * Originally Released Under LGPL - original licence link has changed is not relivant.
22787  *
22788  * Fork - LGPL
22789  * <script type="text/javascript">
22790  */
22791
22792 /**
22793  * @class Roo.data.XmlReader
22794  * @extends Roo.data.DataReader
22795  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22796  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22797  * <p>
22798  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22799  * header in the HTTP response must be set to "text/xml".</em>
22800  * <p>
22801  * Example code:
22802  * <pre><code>
22803 var RecordDef = Roo.data.Record.create([
22804    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22805    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22806 ]);
22807 var myReader = new Roo.data.XmlReader({
22808    totalRecords: "results", // The element which contains the total dataset size (optional)
22809    record: "row",           // The repeated element which contains row information
22810    id: "id"                 // The element within the row that provides an ID for the record (optional)
22811 }, RecordDef);
22812 </code></pre>
22813  * <p>
22814  * This would consume an XML file like this:
22815  * <pre><code>
22816 &lt;?xml?>
22817 &lt;dataset>
22818  &lt;results>2&lt;/results>
22819  &lt;row>
22820    &lt;id>1&lt;/id>
22821    &lt;name>Bill&lt;/name>
22822    &lt;occupation>Gardener&lt;/occupation>
22823  &lt;/row>
22824  &lt;row>
22825    &lt;id>2&lt;/id>
22826    &lt;name>Ben&lt;/name>
22827    &lt;occupation>Horticulturalist&lt;/occupation>
22828  &lt;/row>
22829 &lt;/dataset>
22830 </code></pre>
22831  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22832  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22833  * paged from the remote server.
22834  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22835  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22836  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22837  * a record identifier value.
22838  * @constructor
22839  * Create a new XmlReader
22840  * @param {Object} meta Metadata configuration options
22841  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22842  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22843  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22844  */
22845 Roo.data.XmlReader = function(meta, recordType){
22846     meta = meta || {};
22847     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22848 };
22849 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22850     /**
22851      * This method is only used by a DataProxy which has retrieved data from a remote server.
22852          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22853          * to contain a method called 'responseXML' that returns an XML document object.
22854      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22855      * a cache of Roo.data.Records.
22856      */
22857     read : function(response){
22858         var doc = response.responseXML;
22859         if(!doc) {
22860             throw {message: "XmlReader.read: XML Document not available"};
22861         }
22862         return this.readRecords(doc);
22863     },
22864
22865     /**
22866      * Create a data block containing Roo.data.Records from an XML document.
22867          * @param {Object} doc A parsed XML document.
22868      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22869      * a cache of Roo.data.Records.
22870      */
22871     readRecords : function(doc){
22872         /**
22873          * After any data loads/reads, the raw XML Document is available for further custom processing.
22874          * @type XMLDocument
22875          */
22876         this.xmlData = doc;
22877         var root = doc.documentElement || doc;
22878         var q = Roo.DomQuery;
22879         var recordType = this.recordType, fields = recordType.prototype.fields;
22880         var sid = this.meta.id;
22881         var totalRecords = 0, success = true;
22882         if(this.meta.totalRecords){
22883             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22884         }
22885         
22886         if(this.meta.success){
22887             var sv = q.selectValue(this.meta.success, root, true);
22888             success = sv !== false && sv !== 'false';
22889         }
22890         var records = [];
22891         var ns = q.select(this.meta.record, root);
22892         for(var i = 0, len = ns.length; i < len; i++) {
22893                 var n = ns[i];
22894                 var values = {};
22895                 var id = sid ? q.selectValue(sid, n) : undefined;
22896                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22897                     var f = fields.items[j];
22898                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22899                     v = f.convert(v);
22900                     values[f.name] = v;
22901                 }
22902                 var record = new recordType(values, id);
22903                 record.node = n;
22904                 records[records.length] = record;
22905             }
22906
22907             return {
22908                 success : success,
22909                 records : records,
22910                 totalRecords : totalRecords || records.length
22911             };
22912     }
22913 });/*
22914  * Based on:
22915  * Ext JS Library 1.1.1
22916  * Copyright(c) 2006-2007, Ext JS, LLC.
22917  *
22918  * Originally Released Under LGPL - original licence link has changed is not relivant.
22919  *
22920  * Fork - LGPL
22921  * <script type="text/javascript">
22922  */
22923
22924 /**
22925  * @class Roo.data.ArrayReader
22926  * @extends Roo.data.DataReader
22927  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22928  * Each element of that Array represents a row of data fields. The
22929  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22930  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22931  * <p>
22932  * Example code:.
22933  * <pre><code>
22934 var RecordDef = Roo.data.Record.create([
22935     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22936     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22937 ]);
22938 var myReader = new Roo.data.ArrayReader({
22939     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22940 }, RecordDef);
22941 </code></pre>
22942  * <p>
22943  * This would consume an Array like this:
22944  * <pre><code>
22945 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22946   </code></pre>
22947  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22948  * @constructor
22949  * Create a new JsonReader
22950  * @param {Object} meta Metadata configuration options.
22951  * @param {Object} recordType Either an Array of field definition objects
22952  * as specified to {@link Roo.data.Record#create},
22953  * or an {@link Roo.data.Record} object
22954  * created using {@link Roo.data.Record#create}.
22955  */
22956 Roo.data.ArrayReader = function(meta, recordType){
22957     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22958 };
22959
22960 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22961     /**
22962      * Create a data block containing Roo.data.Records from an XML document.
22963      * @param {Object} o An Array of row objects which represents the dataset.
22964      * @return {Object} data A data block which is used by an Roo.data.Store object as
22965      * a cache of Roo.data.Records.
22966      */
22967     readRecords : function(o){
22968         var sid = this.meta ? this.meta.id : null;
22969         var recordType = this.recordType, fields = recordType.prototype.fields;
22970         var records = [];
22971         var root = o;
22972             for(var i = 0; i < root.length; i++){
22973                     var n = root[i];
22974                 var values = {};
22975                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22976                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22977                 var f = fields.items[j];
22978                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22979                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22980                 v = f.convert(v);
22981                 values[f.name] = v;
22982             }
22983                 var record = new recordType(values, id);
22984                 record.json = n;
22985                 records[records.length] = record;
22986             }
22987             return {
22988                 records : records,
22989                 totalRecords : records.length
22990             };
22991     }
22992 });/*
22993  * Based on:
22994  * Ext JS Library 1.1.1
22995  * Copyright(c) 2006-2007, Ext JS, LLC.
22996  *
22997  * Originally Released Under LGPL - original licence link has changed is not relivant.
22998  *
22999  * Fork - LGPL
23000  * <script type="text/javascript">
23001  */
23002
23003
23004 /**
23005  * @class Roo.data.Tree
23006  * @extends Roo.util.Observable
23007  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23008  * in the tree have most standard DOM functionality.
23009  * @constructor
23010  * @param {Node} root (optional) The root node
23011  */
23012 Roo.data.Tree = function(root){
23013    this.nodeHash = {};
23014    /**
23015     * The root node for this tree
23016     * @type Node
23017     */
23018    this.root = null;
23019    if(root){
23020        this.setRootNode(root);
23021    }
23022    this.addEvents({
23023        /**
23024         * @event append
23025         * Fires when a new child node is appended to a node in this tree.
23026         * @param {Tree} tree The owner tree
23027         * @param {Node} parent The parent node
23028         * @param {Node} node The newly appended node
23029         * @param {Number} index The index of the newly appended node
23030         */
23031        "append" : true,
23032        /**
23033         * @event remove
23034         * Fires when a child node is removed from a node in this tree.
23035         * @param {Tree} tree The owner tree
23036         * @param {Node} parent The parent node
23037         * @param {Node} node The child node removed
23038         */
23039        "remove" : true,
23040        /**
23041         * @event move
23042         * Fires when a node is moved to a new location in the tree
23043         * @param {Tree} tree The owner tree
23044         * @param {Node} node The node moved
23045         * @param {Node} oldParent The old parent of this node
23046         * @param {Node} newParent The new parent of this node
23047         * @param {Number} index The index it was moved to
23048         */
23049        "move" : true,
23050        /**
23051         * @event insert
23052         * Fires when a new child node is inserted in a node in this tree.
23053         * @param {Tree} tree The owner tree
23054         * @param {Node} parent The parent node
23055         * @param {Node} node The child node inserted
23056         * @param {Node} refNode The child node the node was inserted before
23057         */
23058        "insert" : true,
23059        /**
23060         * @event beforeappend
23061         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23062         * @param {Tree} tree The owner tree
23063         * @param {Node} parent The parent node
23064         * @param {Node} node The child node to be appended
23065         */
23066        "beforeappend" : true,
23067        /**
23068         * @event beforeremove
23069         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23070         * @param {Tree} tree The owner tree
23071         * @param {Node} parent The parent node
23072         * @param {Node} node The child node to be removed
23073         */
23074        "beforeremove" : true,
23075        /**
23076         * @event beforemove
23077         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23078         * @param {Tree} tree The owner tree
23079         * @param {Node} node The node being moved
23080         * @param {Node} oldParent The parent of the node
23081         * @param {Node} newParent The new parent the node is moving to
23082         * @param {Number} index The index it is being moved to
23083         */
23084        "beforemove" : true,
23085        /**
23086         * @event beforeinsert
23087         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23088         * @param {Tree} tree The owner tree
23089         * @param {Node} parent The parent node
23090         * @param {Node} node The child node to be inserted
23091         * @param {Node} refNode The child node the node is being inserted before
23092         */
23093        "beforeinsert" : true
23094    });
23095
23096     Roo.data.Tree.superclass.constructor.call(this);
23097 };
23098
23099 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23100     pathSeparator: "/",
23101
23102     proxyNodeEvent : function(){
23103         return this.fireEvent.apply(this, arguments);
23104     },
23105
23106     /**
23107      * Returns the root node for this tree.
23108      * @return {Node}
23109      */
23110     getRootNode : function(){
23111         return this.root;
23112     },
23113
23114     /**
23115      * Sets the root node for this tree.
23116      * @param {Node} node
23117      * @return {Node}
23118      */
23119     setRootNode : function(node){
23120         this.root = node;
23121         node.ownerTree = this;
23122         node.isRoot = true;
23123         this.registerNode(node);
23124         return node;
23125     },
23126
23127     /**
23128      * Gets a node in this tree by its id.
23129      * @param {String} id
23130      * @return {Node}
23131      */
23132     getNodeById : function(id){
23133         return this.nodeHash[id];
23134     },
23135
23136     registerNode : function(node){
23137         this.nodeHash[node.id] = node;
23138     },
23139
23140     unregisterNode : function(node){
23141         delete this.nodeHash[node.id];
23142     },
23143
23144     toString : function(){
23145         return "[Tree"+(this.id?" "+this.id:"")+"]";
23146     }
23147 });
23148
23149 /**
23150  * @class Roo.data.Node
23151  * @extends Roo.util.Observable
23152  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23153  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23154  * @constructor
23155  * @param {Object} attributes The attributes/config for the node
23156  */
23157 Roo.data.Node = function(attributes){
23158     /**
23159      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23160      * @type {Object}
23161      */
23162     this.attributes = attributes || {};
23163     this.leaf = this.attributes.leaf;
23164     /**
23165      * The node id. @type String
23166      */
23167     this.id = this.attributes.id;
23168     if(!this.id){
23169         this.id = Roo.id(null, "ynode-");
23170         this.attributes.id = this.id;
23171     }
23172      
23173     
23174     /**
23175      * All child nodes of this node. @type Array
23176      */
23177     this.childNodes = [];
23178     if(!this.childNodes.indexOf){ // indexOf is a must
23179         this.childNodes.indexOf = function(o){
23180             for(var i = 0, len = this.length; i < len; i++){
23181                 if(this[i] == o) {
23182                     return i;
23183                 }
23184             }
23185             return -1;
23186         };
23187     }
23188     /**
23189      * The parent node for this node. @type Node
23190      */
23191     this.parentNode = null;
23192     /**
23193      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23194      */
23195     this.firstChild = null;
23196     /**
23197      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23198      */
23199     this.lastChild = null;
23200     /**
23201      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23202      */
23203     this.previousSibling = null;
23204     /**
23205      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23206      */
23207     this.nextSibling = null;
23208
23209     this.addEvents({
23210        /**
23211         * @event append
23212         * Fires when a new child node is appended
23213         * @param {Tree} tree The owner tree
23214         * @param {Node} this This node
23215         * @param {Node} node The newly appended node
23216         * @param {Number} index The index of the newly appended node
23217         */
23218        "append" : true,
23219        /**
23220         * @event remove
23221         * Fires when a child node is removed
23222         * @param {Tree} tree The owner tree
23223         * @param {Node} this This node
23224         * @param {Node} node The removed node
23225         */
23226        "remove" : true,
23227        /**
23228         * @event move
23229         * Fires when this node is moved to a new location in the tree
23230         * @param {Tree} tree The owner tree
23231         * @param {Node} this This node
23232         * @param {Node} oldParent The old parent of this node
23233         * @param {Node} newParent The new parent of this node
23234         * @param {Number} index The index it was moved to
23235         */
23236        "move" : true,
23237        /**
23238         * @event insert
23239         * Fires when a new child node is inserted.
23240         * @param {Tree} tree The owner tree
23241         * @param {Node} this This node
23242         * @param {Node} node The child node inserted
23243         * @param {Node} refNode The child node the node was inserted before
23244         */
23245        "insert" : true,
23246        /**
23247         * @event beforeappend
23248         * Fires before a new child is appended, return false to cancel the append.
23249         * @param {Tree} tree The owner tree
23250         * @param {Node} this This node
23251         * @param {Node} node The child node to be appended
23252         */
23253        "beforeappend" : true,
23254        /**
23255         * @event beforeremove
23256         * Fires before a child is removed, return false to cancel the remove.
23257         * @param {Tree} tree The owner tree
23258         * @param {Node} this This node
23259         * @param {Node} node The child node to be removed
23260         */
23261        "beforeremove" : true,
23262        /**
23263         * @event beforemove
23264         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23265         * @param {Tree} tree The owner tree
23266         * @param {Node} this This node
23267         * @param {Node} oldParent The parent of this node
23268         * @param {Node} newParent The new parent this node is moving to
23269         * @param {Number} index The index it is being moved to
23270         */
23271        "beforemove" : true,
23272        /**
23273         * @event beforeinsert
23274         * Fires before a new child is inserted, return false to cancel the insert.
23275         * @param {Tree} tree The owner tree
23276         * @param {Node} this This node
23277         * @param {Node} node The child node to be inserted
23278         * @param {Node} refNode The child node the node is being inserted before
23279         */
23280        "beforeinsert" : true
23281    });
23282     this.listeners = this.attributes.listeners;
23283     Roo.data.Node.superclass.constructor.call(this);
23284 };
23285
23286 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23287     fireEvent : function(evtName){
23288         // first do standard event for this node
23289         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23290             return false;
23291         }
23292         // then bubble it up to the tree if the event wasn't cancelled
23293         var ot = this.getOwnerTree();
23294         if(ot){
23295             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23296                 return false;
23297             }
23298         }
23299         return true;
23300     },
23301
23302     /**
23303      * Returns true if this node is a leaf
23304      * @return {Boolean}
23305      */
23306     isLeaf : function(){
23307         return this.leaf === true;
23308     },
23309
23310     // private
23311     setFirstChild : function(node){
23312         this.firstChild = node;
23313     },
23314
23315     //private
23316     setLastChild : function(node){
23317         this.lastChild = node;
23318     },
23319
23320
23321     /**
23322      * Returns true if this node is the last child of its parent
23323      * @return {Boolean}
23324      */
23325     isLast : function(){
23326        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23327     },
23328
23329     /**
23330      * Returns true if this node is the first child of its parent
23331      * @return {Boolean}
23332      */
23333     isFirst : function(){
23334        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23335     },
23336
23337     hasChildNodes : function(){
23338         return !this.isLeaf() && this.childNodes.length > 0;
23339     },
23340
23341     /**
23342      * Insert node(s) as the last child node of this node.
23343      * @param {Node/Array} node The node or Array of nodes to append
23344      * @return {Node} The appended node if single append, or null if an array was passed
23345      */
23346     appendChild : function(node){
23347         var multi = false;
23348         if(node instanceof Array){
23349             multi = node;
23350         }else if(arguments.length > 1){
23351             multi = arguments;
23352         }
23353         // if passed an array or multiple args do them one by one
23354         if(multi){
23355             for(var i = 0, len = multi.length; i < len; i++) {
23356                 this.appendChild(multi[i]);
23357             }
23358         }else{
23359             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23360                 return false;
23361             }
23362             var index = this.childNodes.length;
23363             var oldParent = node.parentNode;
23364             // it's a move, make sure we move it cleanly
23365             if(oldParent){
23366                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23367                     return false;
23368                 }
23369                 oldParent.removeChild(node);
23370             }
23371             index = this.childNodes.length;
23372             if(index == 0){
23373                 this.setFirstChild(node);
23374             }
23375             this.childNodes.push(node);
23376             node.parentNode = this;
23377             var ps = this.childNodes[index-1];
23378             if(ps){
23379                 node.previousSibling = ps;
23380                 ps.nextSibling = node;
23381             }else{
23382                 node.previousSibling = null;
23383             }
23384             node.nextSibling = null;
23385             this.setLastChild(node);
23386             node.setOwnerTree(this.getOwnerTree());
23387             this.fireEvent("append", this.ownerTree, this, node, index);
23388             if(oldParent){
23389                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23390             }
23391             return node;
23392         }
23393     },
23394
23395     /**
23396      * Removes a child node from this node.
23397      * @param {Node} node The node to remove
23398      * @return {Node} The removed node
23399      */
23400     removeChild : function(node){
23401         var index = this.childNodes.indexOf(node);
23402         if(index == -1){
23403             return false;
23404         }
23405         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23406             return false;
23407         }
23408
23409         // remove it from childNodes collection
23410         this.childNodes.splice(index, 1);
23411
23412         // update siblings
23413         if(node.previousSibling){
23414             node.previousSibling.nextSibling = node.nextSibling;
23415         }
23416         if(node.nextSibling){
23417             node.nextSibling.previousSibling = node.previousSibling;
23418         }
23419
23420         // update child refs
23421         if(this.firstChild == node){
23422             this.setFirstChild(node.nextSibling);
23423         }
23424         if(this.lastChild == node){
23425             this.setLastChild(node.previousSibling);
23426         }
23427
23428         node.setOwnerTree(null);
23429         // clear any references from the node
23430         node.parentNode = null;
23431         node.previousSibling = null;
23432         node.nextSibling = null;
23433         this.fireEvent("remove", this.ownerTree, this, node);
23434         return node;
23435     },
23436
23437     /**
23438      * Inserts the first node before the second node in this nodes childNodes collection.
23439      * @param {Node} node The node to insert
23440      * @param {Node} refNode The node to insert before (if null the node is appended)
23441      * @return {Node} The inserted node
23442      */
23443     insertBefore : function(node, refNode){
23444         if(!refNode){ // like standard Dom, refNode can be null for append
23445             return this.appendChild(node);
23446         }
23447         // nothing to do
23448         if(node == refNode){
23449             return false;
23450         }
23451
23452         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23453             return false;
23454         }
23455         var index = this.childNodes.indexOf(refNode);
23456         var oldParent = node.parentNode;
23457         var refIndex = index;
23458
23459         // when moving internally, indexes will change after remove
23460         if(oldParent == this && this.childNodes.indexOf(node) < index){
23461             refIndex--;
23462         }
23463
23464         // it's a move, make sure we move it cleanly
23465         if(oldParent){
23466             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23467                 return false;
23468             }
23469             oldParent.removeChild(node);
23470         }
23471         if(refIndex == 0){
23472             this.setFirstChild(node);
23473         }
23474         this.childNodes.splice(refIndex, 0, node);
23475         node.parentNode = this;
23476         var ps = this.childNodes[refIndex-1];
23477         if(ps){
23478             node.previousSibling = ps;
23479             ps.nextSibling = node;
23480         }else{
23481             node.previousSibling = null;
23482         }
23483         node.nextSibling = refNode;
23484         refNode.previousSibling = node;
23485         node.setOwnerTree(this.getOwnerTree());
23486         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23487         if(oldParent){
23488             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23489         }
23490         return node;
23491     },
23492
23493     /**
23494      * Returns the child node at the specified index.
23495      * @param {Number} index
23496      * @return {Node}
23497      */
23498     item : function(index){
23499         return this.childNodes[index];
23500     },
23501
23502     /**
23503      * Replaces one child node in this node with another.
23504      * @param {Node} newChild The replacement node
23505      * @param {Node} oldChild The node to replace
23506      * @return {Node} The replaced node
23507      */
23508     replaceChild : function(newChild, oldChild){
23509         this.insertBefore(newChild, oldChild);
23510         this.removeChild(oldChild);
23511         return oldChild;
23512     },
23513
23514     /**
23515      * Returns the index of a child node
23516      * @param {Node} node
23517      * @return {Number} The index of the node or -1 if it was not found
23518      */
23519     indexOf : function(child){
23520         return this.childNodes.indexOf(child);
23521     },
23522
23523     /**
23524      * Returns the tree this node is in.
23525      * @return {Tree}
23526      */
23527     getOwnerTree : function(){
23528         // if it doesn't have one, look for one
23529         if(!this.ownerTree){
23530             var p = this;
23531             while(p){
23532                 if(p.ownerTree){
23533                     this.ownerTree = p.ownerTree;
23534                     break;
23535                 }
23536                 p = p.parentNode;
23537             }
23538         }
23539         return this.ownerTree;
23540     },
23541
23542     /**
23543      * Returns depth of this node (the root node has a depth of 0)
23544      * @return {Number}
23545      */
23546     getDepth : function(){
23547         var depth = 0;
23548         var p = this;
23549         while(p.parentNode){
23550             ++depth;
23551             p = p.parentNode;
23552         }
23553         return depth;
23554     },
23555
23556     // private
23557     setOwnerTree : function(tree){
23558         // if it's move, we need to update everyone
23559         if(tree != this.ownerTree){
23560             if(this.ownerTree){
23561                 this.ownerTree.unregisterNode(this);
23562             }
23563             this.ownerTree = tree;
23564             var cs = this.childNodes;
23565             for(var i = 0, len = cs.length; i < len; i++) {
23566                 cs[i].setOwnerTree(tree);
23567             }
23568             if(tree){
23569                 tree.registerNode(this);
23570             }
23571         }
23572     },
23573
23574     /**
23575      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23576      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23577      * @return {String} The path
23578      */
23579     getPath : function(attr){
23580         attr = attr || "id";
23581         var p = this.parentNode;
23582         var b = [this.attributes[attr]];
23583         while(p){
23584             b.unshift(p.attributes[attr]);
23585             p = p.parentNode;
23586         }
23587         var sep = this.getOwnerTree().pathSeparator;
23588         return sep + b.join(sep);
23589     },
23590
23591     /**
23592      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23593      * function call will be the scope provided or the current node. The arguments to the function
23594      * will be the args provided or the current node. If the function returns false at any point,
23595      * the bubble is stopped.
23596      * @param {Function} fn The function to call
23597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23599      */
23600     bubble : function(fn, scope, args){
23601         var p = this;
23602         while(p){
23603             if(fn.call(scope || p, args || p) === false){
23604                 break;
23605             }
23606             p = p.parentNode;
23607         }
23608     },
23609
23610     /**
23611      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23612      * function call will be the scope provided or the current node. The arguments to the function
23613      * will be the args provided or the current node. If the function returns false at any point,
23614      * the cascade is stopped on that branch.
23615      * @param {Function} fn The function to call
23616      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23617      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23618      */
23619     cascade : function(fn, scope, args){
23620         if(fn.call(scope || this, args || this) !== false){
23621             var cs = this.childNodes;
23622             for(var i = 0, len = cs.length; i < len; i++) {
23623                 cs[i].cascade(fn, scope, args);
23624             }
23625         }
23626     },
23627
23628     /**
23629      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23630      * function call will be the scope provided or the current node. The arguments to the function
23631      * will be the args provided or the current node. If the function returns false at any point,
23632      * the iteration stops.
23633      * @param {Function} fn The function to call
23634      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23635      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23636      */
23637     eachChild : function(fn, scope, args){
23638         var cs = this.childNodes;
23639         for(var i = 0, len = cs.length; i < len; i++) {
23640                 if(fn.call(scope || this, args || cs[i]) === false){
23641                     break;
23642                 }
23643         }
23644     },
23645
23646     /**
23647      * Finds the first child that has the attribute with the specified value.
23648      * @param {String} attribute The attribute name
23649      * @param {Mixed} value The value to search for
23650      * @return {Node} The found child or null if none was found
23651      */
23652     findChild : function(attribute, value){
23653         var cs = this.childNodes;
23654         for(var i = 0, len = cs.length; i < len; i++) {
23655                 if(cs[i].attributes[attribute] == value){
23656                     return cs[i];
23657                 }
23658         }
23659         return null;
23660     },
23661
23662     /**
23663      * Finds the first child by a custom function. The child matches if the function passed
23664      * returns true.
23665      * @param {Function} fn
23666      * @param {Object} scope (optional)
23667      * @return {Node} The found child or null if none was found
23668      */
23669     findChildBy : function(fn, scope){
23670         var cs = this.childNodes;
23671         for(var i = 0, len = cs.length; i < len; i++) {
23672                 if(fn.call(scope||cs[i], cs[i]) === true){
23673                     return cs[i];
23674                 }
23675         }
23676         return null;
23677     },
23678
23679     /**
23680      * Sorts this nodes children using the supplied sort function
23681      * @param {Function} fn
23682      * @param {Object} scope (optional)
23683      */
23684     sort : function(fn, scope){
23685         var cs = this.childNodes;
23686         var len = cs.length;
23687         if(len > 0){
23688             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23689             cs.sort(sortFn);
23690             for(var i = 0; i < len; i++){
23691                 var n = cs[i];
23692                 n.previousSibling = cs[i-1];
23693                 n.nextSibling = cs[i+1];
23694                 if(i == 0){
23695                     this.setFirstChild(n);
23696                 }
23697                 if(i == len-1){
23698                     this.setLastChild(n);
23699                 }
23700             }
23701         }
23702     },
23703
23704     /**
23705      * Returns true if this node is an ancestor (at any point) of the passed node.
23706      * @param {Node} node
23707      * @return {Boolean}
23708      */
23709     contains : function(node){
23710         return node.isAncestor(this);
23711     },
23712
23713     /**
23714      * Returns true if the passed node is an ancestor (at any point) of this node.
23715      * @param {Node} node
23716      * @return {Boolean}
23717      */
23718     isAncestor : function(node){
23719         var p = this.parentNode;
23720         while(p){
23721             if(p == node){
23722                 return true;
23723             }
23724             p = p.parentNode;
23725         }
23726         return false;
23727     },
23728
23729     toString : function(){
23730         return "[Node"+(this.id?" "+this.id:"")+"]";
23731     }
23732 });/*
23733  * Based on:
23734  * Ext JS Library 1.1.1
23735  * Copyright(c) 2006-2007, Ext JS, LLC.
23736  *
23737  * Originally Released Under LGPL - original licence link has changed is not relivant.
23738  *
23739  * Fork - LGPL
23740  * <script type="text/javascript">
23741  */
23742  (function(){ 
23743 /**
23744  * @class Roo.Layer
23745  * @extends Roo.Element
23746  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23747  * automatic maintaining of shadow/shim positions.
23748  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23749  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23750  * you can pass a string with a CSS class name. False turns off the shadow.
23751  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23752  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23753  * @cfg {String} cls CSS class to add to the element
23754  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23755  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23756  * @constructor
23757  * @param {Object} config An object with config options.
23758  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23759  */
23760
23761 Roo.Layer = function(config, existingEl){
23762     config = config || {};
23763     var dh = Roo.DomHelper;
23764     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23765     if(existingEl){
23766         this.dom = Roo.getDom(existingEl);
23767     }
23768     if(!this.dom){
23769         var o = config.dh || {tag: "div", cls: "x-layer"};
23770         this.dom = dh.append(pel, o);
23771     }
23772     if(config.cls){
23773         this.addClass(config.cls);
23774     }
23775     this.constrain = config.constrain !== false;
23776     this.visibilityMode = Roo.Element.VISIBILITY;
23777     if(config.id){
23778         this.id = this.dom.id = config.id;
23779     }else{
23780         this.id = Roo.id(this.dom);
23781     }
23782     this.zindex = config.zindex || this.getZIndex();
23783     this.position("absolute", this.zindex);
23784     if(config.shadow){
23785         this.shadowOffset = config.shadowOffset || 4;
23786         this.shadow = new Roo.Shadow({
23787             offset : this.shadowOffset,
23788             mode : config.shadow
23789         });
23790     }else{
23791         this.shadowOffset = 0;
23792     }
23793     this.useShim = config.shim !== false && Roo.useShims;
23794     this.useDisplay = config.useDisplay;
23795     this.hide();
23796 };
23797
23798 var supr = Roo.Element.prototype;
23799
23800 // shims are shared among layer to keep from having 100 iframes
23801 var shims = [];
23802
23803 Roo.extend(Roo.Layer, Roo.Element, {
23804
23805     getZIndex : function(){
23806         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23807     },
23808
23809     getShim : function(){
23810         if(!this.useShim){
23811             return null;
23812         }
23813         if(this.shim){
23814             return this.shim;
23815         }
23816         var shim = shims.shift();
23817         if(!shim){
23818             shim = this.createShim();
23819             shim.enableDisplayMode('block');
23820             shim.dom.style.display = 'none';
23821             shim.dom.style.visibility = 'visible';
23822         }
23823         var pn = this.dom.parentNode;
23824         if(shim.dom.parentNode != pn){
23825             pn.insertBefore(shim.dom, this.dom);
23826         }
23827         shim.setStyle('z-index', this.getZIndex()-2);
23828         this.shim = shim;
23829         return shim;
23830     },
23831
23832     hideShim : function(){
23833         if(this.shim){
23834             this.shim.setDisplayed(false);
23835             shims.push(this.shim);
23836             delete this.shim;
23837         }
23838     },
23839
23840     disableShadow : function(){
23841         if(this.shadow){
23842             this.shadowDisabled = true;
23843             this.shadow.hide();
23844             this.lastShadowOffset = this.shadowOffset;
23845             this.shadowOffset = 0;
23846         }
23847     },
23848
23849     enableShadow : function(show){
23850         if(this.shadow){
23851             this.shadowDisabled = false;
23852             this.shadowOffset = this.lastShadowOffset;
23853             delete this.lastShadowOffset;
23854             if(show){
23855                 this.sync(true);
23856             }
23857         }
23858     },
23859
23860     // private
23861     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23862     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23863     sync : function(doShow){
23864         var sw = this.shadow;
23865         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23866             var sh = this.getShim();
23867
23868             var w = this.getWidth(),
23869                 h = this.getHeight();
23870
23871             var l = this.getLeft(true),
23872                 t = this.getTop(true);
23873
23874             if(sw && !this.shadowDisabled){
23875                 if(doShow && !sw.isVisible()){
23876                     sw.show(this);
23877                 }else{
23878                     sw.realign(l, t, w, h);
23879                 }
23880                 if(sh){
23881                     if(doShow){
23882                        sh.show();
23883                     }
23884                     // fit the shim behind the shadow, so it is shimmed too
23885                     var a = sw.adjusts, s = sh.dom.style;
23886                     s.left = (Math.min(l, l+a.l))+"px";
23887                     s.top = (Math.min(t, t+a.t))+"px";
23888                     s.width = (w+a.w)+"px";
23889                     s.height = (h+a.h)+"px";
23890                 }
23891             }else if(sh){
23892                 if(doShow){
23893                    sh.show();
23894                 }
23895                 sh.setSize(w, h);
23896                 sh.setLeftTop(l, t);
23897             }
23898             
23899         }
23900     },
23901
23902     // private
23903     destroy : function(){
23904         this.hideShim();
23905         if(this.shadow){
23906             this.shadow.hide();
23907         }
23908         this.removeAllListeners();
23909         var pn = this.dom.parentNode;
23910         if(pn){
23911             pn.removeChild(this.dom);
23912         }
23913         Roo.Element.uncache(this.id);
23914     },
23915
23916     remove : function(){
23917         this.destroy();
23918     },
23919
23920     // private
23921     beginUpdate : function(){
23922         this.updating = true;
23923     },
23924
23925     // private
23926     endUpdate : function(){
23927         this.updating = false;
23928         this.sync(true);
23929     },
23930
23931     // private
23932     hideUnders : function(negOffset){
23933         if(this.shadow){
23934             this.shadow.hide();
23935         }
23936         this.hideShim();
23937     },
23938
23939     // private
23940     constrainXY : function(){
23941         if(this.constrain){
23942             var vw = Roo.lib.Dom.getViewWidth(),
23943                 vh = Roo.lib.Dom.getViewHeight();
23944             var s = Roo.get(document).getScroll();
23945
23946             var xy = this.getXY();
23947             var x = xy[0], y = xy[1];   
23948             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23949             // only move it if it needs it
23950             var moved = false;
23951             // first validate right/bottom
23952             if((x + w) > vw+s.left){
23953                 x = vw - w - this.shadowOffset;
23954                 moved = true;
23955             }
23956             if((y + h) > vh+s.top){
23957                 y = vh - h - this.shadowOffset;
23958                 moved = true;
23959             }
23960             // then make sure top/left isn't negative
23961             if(x < s.left){
23962                 x = s.left;
23963                 moved = true;
23964             }
23965             if(y < s.top){
23966                 y = s.top;
23967                 moved = true;
23968             }
23969             if(moved){
23970                 if(this.avoidY){
23971                     var ay = this.avoidY;
23972                     if(y <= ay && (y+h) >= ay){
23973                         y = ay-h-5;   
23974                     }
23975                 }
23976                 xy = [x, y];
23977                 this.storeXY(xy);
23978                 supr.setXY.call(this, xy);
23979                 this.sync();
23980             }
23981         }
23982     },
23983
23984     isVisible : function(){
23985         return this.visible;    
23986     },
23987
23988     // private
23989     showAction : function(){
23990         this.visible = true; // track visibility to prevent getStyle calls
23991         if(this.useDisplay === true){
23992             this.setDisplayed("");
23993         }else if(this.lastXY){
23994             supr.setXY.call(this, this.lastXY);
23995         }else if(this.lastLT){
23996             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23997         }
23998     },
23999
24000     // private
24001     hideAction : function(){
24002         this.visible = false;
24003         if(this.useDisplay === true){
24004             this.setDisplayed(false);
24005         }else{
24006             this.setLeftTop(-10000,-10000);
24007         }
24008     },
24009
24010     // overridden Element method
24011     setVisible : function(v, a, d, c, e){
24012         if(v){
24013             this.showAction();
24014         }
24015         if(a && v){
24016             var cb = function(){
24017                 this.sync(true);
24018                 if(c){
24019                     c();
24020                 }
24021             }.createDelegate(this);
24022             supr.setVisible.call(this, true, true, d, cb, e);
24023         }else{
24024             if(!v){
24025                 this.hideUnders(true);
24026             }
24027             var cb = c;
24028             if(a){
24029                 cb = function(){
24030                     this.hideAction();
24031                     if(c){
24032                         c();
24033                     }
24034                 }.createDelegate(this);
24035             }
24036             supr.setVisible.call(this, v, a, d, cb, e);
24037             if(v){
24038                 this.sync(true);
24039             }else if(!a){
24040                 this.hideAction();
24041             }
24042         }
24043     },
24044
24045     storeXY : function(xy){
24046         delete this.lastLT;
24047         this.lastXY = xy;
24048     },
24049
24050     storeLeftTop : function(left, top){
24051         delete this.lastXY;
24052         this.lastLT = [left, top];
24053     },
24054
24055     // private
24056     beforeFx : function(){
24057         this.beforeAction();
24058         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24059     },
24060
24061     // private
24062     afterFx : function(){
24063         Roo.Layer.superclass.afterFx.apply(this, arguments);
24064         this.sync(this.isVisible());
24065     },
24066
24067     // private
24068     beforeAction : function(){
24069         if(!this.updating && this.shadow){
24070             this.shadow.hide();
24071         }
24072     },
24073
24074     // overridden Element method
24075     setLeft : function(left){
24076         this.storeLeftTop(left, this.getTop(true));
24077         supr.setLeft.apply(this, arguments);
24078         this.sync();
24079     },
24080
24081     setTop : function(top){
24082         this.storeLeftTop(this.getLeft(true), top);
24083         supr.setTop.apply(this, arguments);
24084         this.sync();
24085     },
24086
24087     setLeftTop : function(left, top){
24088         this.storeLeftTop(left, top);
24089         supr.setLeftTop.apply(this, arguments);
24090         this.sync();
24091     },
24092
24093     setXY : function(xy, a, d, c, e){
24094         this.fixDisplay();
24095         this.beforeAction();
24096         this.storeXY(xy);
24097         var cb = this.createCB(c);
24098         supr.setXY.call(this, xy, a, d, cb, e);
24099         if(!a){
24100             cb();
24101         }
24102     },
24103
24104     // private
24105     createCB : function(c){
24106         var el = this;
24107         return function(){
24108             el.constrainXY();
24109             el.sync(true);
24110             if(c){
24111                 c();
24112             }
24113         };
24114     },
24115
24116     // overridden Element method
24117     setX : function(x, a, d, c, e){
24118         this.setXY([x, this.getY()], a, d, c, e);
24119     },
24120
24121     // overridden Element method
24122     setY : function(y, a, d, c, e){
24123         this.setXY([this.getX(), y], a, d, c, e);
24124     },
24125
24126     // overridden Element method
24127     setSize : function(w, h, a, d, c, e){
24128         this.beforeAction();
24129         var cb = this.createCB(c);
24130         supr.setSize.call(this, w, h, a, d, cb, e);
24131         if(!a){
24132             cb();
24133         }
24134     },
24135
24136     // overridden Element method
24137     setWidth : function(w, a, d, c, e){
24138         this.beforeAction();
24139         var cb = this.createCB(c);
24140         supr.setWidth.call(this, w, a, d, cb, e);
24141         if(!a){
24142             cb();
24143         }
24144     },
24145
24146     // overridden Element method
24147     setHeight : function(h, a, d, c, e){
24148         this.beforeAction();
24149         var cb = this.createCB(c);
24150         supr.setHeight.call(this, h, a, d, cb, e);
24151         if(!a){
24152             cb();
24153         }
24154     },
24155
24156     // overridden Element method
24157     setBounds : function(x, y, w, h, a, d, c, e){
24158         this.beforeAction();
24159         var cb = this.createCB(c);
24160         if(!a){
24161             this.storeXY([x, y]);
24162             supr.setXY.call(this, [x, y]);
24163             supr.setSize.call(this, w, h, a, d, cb, e);
24164             cb();
24165         }else{
24166             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24167         }
24168         return this;
24169     },
24170     
24171     /**
24172      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24173      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24174      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24175      * @param {Number} zindex The new z-index to set
24176      * @return {this} The Layer
24177      */
24178     setZIndex : function(zindex){
24179         this.zindex = zindex;
24180         this.setStyle("z-index", zindex + 2);
24181         if(this.shadow){
24182             this.shadow.setZIndex(zindex + 1);
24183         }
24184         if(this.shim){
24185             this.shim.setStyle("z-index", zindex);
24186         }
24187     }
24188 });
24189 })();/*
24190  * Based on:
24191  * Ext JS Library 1.1.1
24192  * Copyright(c) 2006-2007, Ext JS, LLC.
24193  *
24194  * Originally Released Under LGPL - original licence link has changed is not relivant.
24195  *
24196  * Fork - LGPL
24197  * <script type="text/javascript">
24198  */
24199
24200
24201 /**
24202  * @class Roo.Shadow
24203  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24204  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24205  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24206  * @constructor
24207  * Create a new Shadow
24208  * @param {Object} config The config object
24209  */
24210 Roo.Shadow = function(config){
24211     Roo.apply(this, config);
24212     if(typeof this.mode != "string"){
24213         this.mode = this.defaultMode;
24214     }
24215     var o = this.offset, a = {h: 0};
24216     var rad = Math.floor(this.offset/2);
24217     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24218         case "drop":
24219             a.w = 0;
24220             a.l = a.t = o;
24221             a.t -= 1;
24222             if(Roo.isIE){
24223                 a.l -= this.offset + rad;
24224                 a.t -= this.offset + rad;
24225                 a.w -= rad;
24226                 a.h -= rad;
24227                 a.t += 1;
24228             }
24229         break;
24230         case "sides":
24231             a.w = (o*2);
24232             a.l = -o;
24233             a.t = o-1;
24234             if(Roo.isIE){
24235                 a.l -= (this.offset - rad);
24236                 a.t -= this.offset + rad;
24237                 a.l += 1;
24238                 a.w -= (this.offset - rad)*2;
24239                 a.w -= rad + 1;
24240                 a.h -= 1;
24241             }
24242         break;
24243         case "frame":
24244             a.w = a.h = (o*2);
24245             a.l = a.t = -o;
24246             a.t += 1;
24247             a.h -= 2;
24248             if(Roo.isIE){
24249                 a.l -= (this.offset - rad);
24250                 a.t -= (this.offset - rad);
24251                 a.l += 1;
24252                 a.w -= (this.offset + rad + 1);
24253                 a.h -= (this.offset + rad);
24254                 a.h += 1;
24255             }
24256         break;
24257     };
24258
24259     this.adjusts = a;
24260 };
24261
24262 Roo.Shadow.prototype = {
24263     /**
24264      * @cfg {String} mode
24265      * The shadow display mode.  Supports the following options:<br />
24266      * sides: Shadow displays on both sides and bottom only<br />
24267      * frame: Shadow displays equally on all four sides<br />
24268      * drop: Traditional bottom-right drop shadow (default)
24269      */
24270     /**
24271      * @cfg {String} offset
24272      * The number of pixels to offset the shadow from the element (defaults to 4)
24273      */
24274     offset: 4,
24275
24276     // private
24277     defaultMode: "drop",
24278
24279     /**
24280      * Displays the shadow under the target element
24281      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24282      */
24283     show : function(target){
24284         target = Roo.get(target);
24285         if(!this.el){
24286             this.el = Roo.Shadow.Pool.pull();
24287             if(this.el.dom.nextSibling != target.dom){
24288                 this.el.insertBefore(target);
24289             }
24290         }
24291         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24292         if(Roo.isIE){
24293             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24294         }
24295         this.realign(
24296             target.getLeft(true),
24297             target.getTop(true),
24298             target.getWidth(),
24299             target.getHeight()
24300         );
24301         this.el.dom.style.display = "block";
24302     },
24303
24304     /**
24305      * Returns true if the shadow is visible, else false
24306      */
24307     isVisible : function(){
24308         return this.el ? true : false;  
24309     },
24310
24311     /**
24312      * Direct alignment when values are already available. Show must be called at least once before
24313      * calling this method to ensure it is initialized.
24314      * @param {Number} left The target element left position
24315      * @param {Number} top The target element top position
24316      * @param {Number} width The target element width
24317      * @param {Number} height The target element height
24318      */
24319     realign : function(l, t, w, h){
24320         if(!this.el){
24321             return;
24322         }
24323         var a = this.adjusts, d = this.el.dom, s = d.style;
24324         var iea = 0;
24325         s.left = (l+a.l)+"px";
24326         s.top = (t+a.t)+"px";
24327         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24328  
24329         if(s.width != sws || s.height != shs){
24330             s.width = sws;
24331             s.height = shs;
24332             if(!Roo.isIE){
24333                 var cn = d.childNodes;
24334                 var sww = Math.max(0, (sw-12))+"px";
24335                 cn[0].childNodes[1].style.width = sww;
24336                 cn[1].childNodes[1].style.width = sww;
24337                 cn[2].childNodes[1].style.width = sww;
24338                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24339             }
24340         }
24341     },
24342
24343     /**
24344      * Hides this shadow
24345      */
24346     hide : function(){
24347         if(this.el){
24348             this.el.dom.style.display = "none";
24349             Roo.Shadow.Pool.push(this.el);
24350             delete this.el;
24351         }
24352     },
24353
24354     /**
24355      * Adjust the z-index of this shadow
24356      * @param {Number} zindex The new z-index
24357      */
24358     setZIndex : function(z){
24359         this.zIndex = z;
24360         if(this.el){
24361             this.el.setStyle("z-index", z);
24362         }
24363     }
24364 };
24365
24366 // Private utility class that manages the internal Shadow cache
24367 Roo.Shadow.Pool = function(){
24368     var p = [];
24369     var markup = Roo.isIE ?
24370                  '<div class="x-ie-shadow"></div>' :
24371                  '<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>';
24372     return {
24373         pull : function(){
24374             var sh = p.shift();
24375             if(!sh){
24376                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24377                 sh.autoBoxAdjust = false;
24378             }
24379             return sh;
24380         },
24381
24382         push : function(sh){
24383             p.push(sh);
24384         }
24385     };
24386 }();/*
24387  * Based on:
24388  * Ext JS Library 1.1.1
24389  * Copyright(c) 2006-2007, Ext JS, LLC.
24390  *
24391  * Originally Released Under LGPL - original licence link has changed is not relivant.
24392  *
24393  * Fork - LGPL
24394  * <script type="text/javascript">
24395  */
24396
24397
24398 /**
24399  * @class Roo.SplitBar
24400  * @extends Roo.util.Observable
24401  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24402  * <br><br>
24403  * Usage:
24404  * <pre><code>
24405 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24406                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24407 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24408 split.minSize = 100;
24409 split.maxSize = 600;
24410 split.animate = true;
24411 split.on('moved', splitterMoved);
24412 </code></pre>
24413  * @constructor
24414  * Create a new SplitBar
24415  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24416  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24417  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24418  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24419                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24420                         position of the SplitBar).
24421  */
24422 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24423     
24424     /** @private */
24425     this.el = Roo.get(dragElement, true);
24426     this.el.dom.unselectable = "on";
24427     /** @private */
24428     this.resizingEl = Roo.get(resizingElement, true);
24429
24430     /**
24431      * @private
24432      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24433      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24434      * @type Number
24435      */
24436     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24437     
24438     /**
24439      * The minimum size of the resizing element. (Defaults to 0)
24440      * @type Number
24441      */
24442     this.minSize = 0;
24443     
24444     /**
24445      * The maximum size of the resizing element. (Defaults to 2000)
24446      * @type Number
24447      */
24448     this.maxSize = 2000;
24449     
24450     /**
24451      * Whether to animate the transition to the new size
24452      * @type Boolean
24453      */
24454     this.animate = false;
24455     
24456     /**
24457      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24458      * @type Boolean
24459      */
24460     this.useShim = false;
24461     
24462     /** @private */
24463     this.shim = null;
24464     
24465     if(!existingProxy){
24466         /** @private */
24467         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24468     }else{
24469         this.proxy = Roo.get(existingProxy).dom;
24470     }
24471     /** @private */
24472     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24473     
24474     /** @private */
24475     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24476     
24477     /** @private */
24478     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24479     
24480     /** @private */
24481     this.dragSpecs = {};
24482     
24483     /**
24484      * @private The adapter to use to positon and resize elements
24485      */
24486     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24487     this.adapter.init(this);
24488     
24489     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24490         /** @private */
24491         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24492         this.el.addClass("x-splitbar-h");
24493     }else{
24494         /** @private */
24495         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24496         this.el.addClass("x-splitbar-v");
24497     }
24498     
24499     this.addEvents({
24500         /**
24501          * @event resize
24502          * Fires when the splitter is moved (alias for {@link #event-moved})
24503          * @param {Roo.SplitBar} this
24504          * @param {Number} newSize the new width or height
24505          */
24506         "resize" : true,
24507         /**
24508          * @event moved
24509          * Fires when the splitter is moved
24510          * @param {Roo.SplitBar} this
24511          * @param {Number} newSize the new width or height
24512          */
24513         "moved" : true,
24514         /**
24515          * @event beforeresize
24516          * Fires before the splitter is dragged
24517          * @param {Roo.SplitBar} this
24518          */
24519         "beforeresize" : true,
24520
24521         "beforeapply" : true
24522     });
24523
24524     Roo.util.Observable.call(this);
24525 };
24526
24527 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24528     onStartProxyDrag : function(x, y){
24529         this.fireEvent("beforeresize", this);
24530         if(!this.overlay){
24531             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24532             o.unselectable();
24533             o.enableDisplayMode("block");
24534             // all splitbars share the same overlay
24535             Roo.SplitBar.prototype.overlay = o;
24536         }
24537         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24538         this.overlay.show();
24539         Roo.get(this.proxy).setDisplayed("block");
24540         var size = this.adapter.getElementSize(this);
24541         this.activeMinSize = this.getMinimumSize();;
24542         this.activeMaxSize = this.getMaximumSize();;
24543         var c1 = size - this.activeMinSize;
24544         var c2 = Math.max(this.activeMaxSize - size, 0);
24545         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24546             this.dd.resetConstraints();
24547             this.dd.setXConstraint(
24548                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24549                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24550             );
24551             this.dd.setYConstraint(0, 0);
24552         }else{
24553             this.dd.resetConstraints();
24554             this.dd.setXConstraint(0, 0);
24555             this.dd.setYConstraint(
24556                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24557                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24558             );
24559          }
24560         this.dragSpecs.startSize = size;
24561         this.dragSpecs.startPoint = [x, y];
24562         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24563     },
24564     
24565     /** 
24566      * @private Called after the drag operation by the DDProxy
24567      */
24568     onEndProxyDrag : function(e){
24569         Roo.get(this.proxy).setDisplayed(false);
24570         var endPoint = Roo.lib.Event.getXY(e);
24571         if(this.overlay){
24572             this.overlay.hide();
24573         }
24574         var newSize;
24575         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24576             newSize = this.dragSpecs.startSize + 
24577                 (this.placement == Roo.SplitBar.LEFT ?
24578                     endPoint[0] - this.dragSpecs.startPoint[0] :
24579                     this.dragSpecs.startPoint[0] - endPoint[0]
24580                 );
24581         }else{
24582             newSize = this.dragSpecs.startSize + 
24583                 (this.placement == Roo.SplitBar.TOP ?
24584                     endPoint[1] - this.dragSpecs.startPoint[1] :
24585                     this.dragSpecs.startPoint[1] - endPoint[1]
24586                 );
24587         }
24588         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24589         if(newSize != this.dragSpecs.startSize){
24590             if(this.fireEvent('beforeapply', this, newSize) !== false){
24591                 this.adapter.setElementSize(this, newSize);
24592                 this.fireEvent("moved", this, newSize);
24593                 this.fireEvent("resize", this, newSize);
24594             }
24595         }
24596     },
24597     
24598     /**
24599      * Get the adapter this SplitBar uses
24600      * @return The adapter object
24601      */
24602     getAdapter : function(){
24603         return this.adapter;
24604     },
24605     
24606     /**
24607      * Set the adapter this SplitBar uses
24608      * @param {Object} adapter A SplitBar adapter object
24609      */
24610     setAdapter : function(adapter){
24611         this.adapter = adapter;
24612         this.adapter.init(this);
24613     },
24614     
24615     /**
24616      * Gets the minimum size for the resizing element
24617      * @return {Number} The minimum size
24618      */
24619     getMinimumSize : function(){
24620         return this.minSize;
24621     },
24622     
24623     /**
24624      * Sets the minimum size for the resizing element
24625      * @param {Number} minSize The minimum size
24626      */
24627     setMinimumSize : function(minSize){
24628         this.minSize = minSize;
24629     },
24630     
24631     /**
24632      * Gets the maximum size for the resizing element
24633      * @return {Number} The maximum size
24634      */
24635     getMaximumSize : function(){
24636         return this.maxSize;
24637     },
24638     
24639     /**
24640      * Sets the maximum size for the resizing element
24641      * @param {Number} maxSize The maximum size
24642      */
24643     setMaximumSize : function(maxSize){
24644         this.maxSize = maxSize;
24645     },
24646     
24647     /**
24648      * Sets the initialize size for the resizing element
24649      * @param {Number} size The initial size
24650      */
24651     setCurrentSize : function(size){
24652         var oldAnimate = this.animate;
24653         this.animate = false;
24654         this.adapter.setElementSize(this, size);
24655         this.animate = oldAnimate;
24656     },
24657     
24658     /**
24659      * Destroy this splitbar. 
24660      * @param {Boolean} removeEl True to remove the element
24661      */
24662     destroy : function(removeEl){
24663         if(this.shim){
24664             this.shim.remove();
24665         }
24666         this.dd.unreg();
24667         this.proxy.parentNode.removeChild(this.proxy);
24668         if(removeEl){
24669             this.el.remove();
24670         }
24671     }
24672 });
24673
24674 /**
24675  * @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.
24676  */
24677 Roo.SplitBar.createProxy = function(dir){
24678     var proxy = new Roo.Element(document.createElement("div"));
24679     proxy.unselectable();
24680     var cls = 'x-splitbar-proxy';
24681     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24682     document.body.appendChild(proxy.dom);
24683     return proxy.dom;
24684 };
24685
24686 /** 
24687  * @class Roo.SplitBar.BasicLayoutAdapter
24688  * Default Adapter. It assumes the splitter and resizing element are not positioned
24689  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24690  */
24691 Roo.SplitBar.BasicLayoutAdapter = function(){
24692 };
24693
24694 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24695     // do nothing for now
24696     init : function(s){
24697     
24698     },
24699     /**
24700      * Called before drag operations to get the current size of the resizing element. 
24701      * @param {Roo.SplitBar} s The SplitBar using this adapter
24702      */
24703      getElementSize : function(s){
24704         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24705             return s.resizingEl.getWidth();
24706         }else{
24707             return s.resizingEl.getHeight();
24708         }
24709     },
24710     
24711     /**
24712      * Called after drag operations to set the size of the resizing element.
24713      * @param {Roo.SplitBar} s The SplitBar using this adapter
24714      * @param {Number} newSize The new size to set
24715      * @param {Function} onComplete A function to be invoked when resizing is complete
24716      */
24717     setElementSize : function(s, newSize, onComplete){
24718         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24719             if(!s.animate){
24720                 s.resizingEl.setWidth(newSize);
24721                 if(onComplete){
24722                     onComplete(s, newSize);
24723                 }
24724             }else{
24725                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24726             }
24727         }else{
24728             
24729             if(!s.animate){
24730                 s.resizingEl.setHeight(newSize);
24731                 if(onComplete){
24732                     onComplete(s, newSize);
24733                 }
24734             }else{
24735                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24736             }
24737         }
24738     }
24739 };
24740
24741 /** 
24742  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24743  * @extends Roo.SplitBar.BasicLayoutAdapter
24744  * Adapter that  moves the splitter element to align with the resized sizing element. 
24745  * Used with an absolute positioned SplitBar.
24746  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24747  * document.body, make sure you assign an id to the body element.
24748  */
24749 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24750     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24751     this.container = Roo.get(container);
24752 };
24753
24754 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24755     init : function(s){
24756         this.basic.init(s);
24757     },
24758     
24759     getElementSize : function(s){
24760         return this.basic.getElementSize(s);
24761     },
24762     
24763     setElementSize : function(s, newSize, onComplete){
24764         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24765     },
24766     
24767     moveSplitter : function(s){
24768         var yes = Roo.SplitBar;
24769         switch(s.placement){
24770             case yes.LEFT:
24771                 s.el.setX(s.resizingEl.getRight());
24772                 break;
24773             case yes.RIGHT:
24774                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24775                 break;
24776             case yes.TOP:
24777                 s.el.setY(s.resizingEl.getBottom());
24778                 break;
24779             case yes.BOTTOM:
24780                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24781                 break;
24782         }
24783     }
24784 };
24785
24786 /**
24787  * Orientation constant - Create a vertical SplitBar
24788  * @static
24789  * @type Number
24790  */
24791 Roo.SplitBar.VERTICAL = 1;
24792
24793 /**
24794  * Orientation constant - Create a horizontal SplitBar
24795  * @static
24796  * @type Number
24797  */
24798 Roo.SplitBar.HORIZONTAL = 2;
24799
24800 /**
24801  * Placement constant - The resizing element is to the left of the splitter element
24802  * @static
24803  * @type Number
24804  */
24805 Roo.SplitBar.LEFT = 1;
24806
24807 /**
24808  * Placement constant - The resizing element is to the right of the splitter element
24809  * @static
24810  * @type Number
24811  */
24812 Roo.SplitBar.RIGHT = 2;
24813
24814 /**
24815  * Placement constant - The resizing element is positioned above the splitter element
24816  * @static
24817  * @type Number
24818  */
24819 Roo.SplitBar.TOP = 3;
24820
24821 /**
24822  * Placement constant - The resizing element is positioned under splitter element
24823  * @static
24824  * @type Number
24825  */
24826 Roo.SplitBar.BOTTOM = 4;
24827 /*
24828  * Based on:
24829  * Ext JS Library 1.1.1
24830  * Copyright(c) 2006-2007, Ext JS, LLC.
24831  *
24832  * Originally Released Under LGPL - original licence link has changed is not relivant.
24833  *
24834  * Fork - LGPL
24835  * <script type="text/javascript">
24836  */
24837
24838 /**
24839  * @class Roo.View
24840  * @extends Roo.util.Observable
24841  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24842  * This class also supports single and multi selection modes. <br>
24843  * Create a data model bound view:
24844  <pre><code>
24845  var store = new Roo.data.Store(...);
24846
24847  var view = new Roo.View({
24848     el : "my-element",
24849     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24850  
24851     singleSelect: true,
24852     selectedClass: "ydataview-selected",
24853     store: store
24854  });
24855
24856  // listen for node click?
24857  view.on("click", function(vw, index, node, e){
24858  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24859  });
24860
24861  // load XML data
24862  dataModel.load("foobar.xml");
24863  </code></pre>
24864  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24865  * <br><br>
24866  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24867  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24868  * 
24869  * Note: old style constructor is still suported (container, template, config)
24870  * 
24871  * @constructor
24872  * Create a new View
24873  * @param {Object} config The config object
24874  * 
24875  */
24876 Roo.View = function(config, depreciated_tpl, depreciated_config){
24877     
24878     this.parent = false;
24879     
24880     if (typeof(depreciated_tpl) == 'undefined') {
24881         // new way.. - universal constructor.
24882         Roo.apply(this, config);
24883         this.el  = Roo.get(this.el);
24884     } else {
24885         // old format..
24886         this.el  = Roo.get(config);
24887         this.tpl = depreciated_tpl;
24888         Roo.apply(this, depreciated_config);
24889     }
24890     this.wrapEl  = this.el.wrap().wrap();
24891     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24892     
24893     
24894     if(typeof(this.tpl) == "string"){
24895         this.tpl = new Roo.Template(this.tpl);
24896     } else {
24897         // support xtype ctors..
24898         this.tpl = new Roo.factory(this.tpl, Roo);
24899     }
24900     
24901     
24902     this.tpl.compile();
24903     
24904     /** @private */
24905     this.addEvents({
24906         /**
24907          * @event beforeclick
24908          * Fires before a click is processed. Returns false to cancel the default action.
24909          * @param {Roo.View} this
24910          * @param {Number} index The index of the target node
24911          * @param {HTMLElement} node The target node
24912          * @param {Roo.EventObject} e The raw event object
24913          */
24914             "beforeclick" : true,
24915         /**
24916          * @event click
24917          * Fires when a template node is clicked.
24918          * @param {Roo.View} this
24919          * @param {Number} index The index of the target node
24920          * @param {HTMLElement} node The target node
24921          * @param {Roo.EventObject} e The raw event object
24922          */
24923             "click" : true,
24924         /**
24925          * @event dblclick
24926          * Fires when a template node is double clicked.
24927          * @param {Roo.View} this
24928          * @param {Number} index The index of the target node
24929          * @param {HTMLElement} node The target node
24930          * @param {Roo.EventObject} e The raw event object
24931          */
24932             "dblclick" : true,
24933         /**
24934          * @event contextmenu
24935          * Fires when a template node is right clicked.
24936          * @param {Roo.View} this
24937          * @param {Number} index The index of the target node
24938          * @param {HTMLElement} node The target node
24939          * @param {Roo.EventObject} e The raw event object
24940          */
24941             "contextmenu" : true,
24942         /**
24943          * @event selectionchange
24944          * Fires when the selected nodes change.
24945          * @param {Roo.View} this
24946          * @param {Array} selections Array of the selected nodes
24947          */
24948             "selectionchange" : true,
24949     
24950         /**
24951          * @event beforeselect
24952          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24953          * @param {Roo.View} this
24954          * @param {HTMLElement} node The node to be selected
24955          * @param {Array} selections Array of currently selected nodes
24956          */
24957             "beforeselect" : true,
24958         /**
24959          * @event preparedata
24960          * Fires on every row to render, to allow you to change the data.
24961          * @param {Roo.View} this
24962          * @param {Object} data to be rendered (change this)
24963          */
24964           "preparedata" : true
24965           
24966           
24967         });
24968
24969
24970
24971     this.el.on({
24972         "click": this.onClick,
24973         "dblclick": this.onDblClick,
24974         "contextmenu": this.onContextMenu,
24975         scope:this
24976     });
24977
24978     this.selections = [];
24979     this.nodes = [];
24980     this.cmp = new Roo.CompositeElementLite([]);
24981     if(this.store){
24982         this.store = Roo.factory(this.store, Roo.data);
24983         this.setStore(this.store, true);
24984     }
24985     
24986     if ( this.footer && this.footer.xtype) {
24987            
24988          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24989         
24990         this.footer.dataSource = this.store
24991         this.footer.container = fctr;
24992         this.footer = Roo.factory(this.footer, Roo);
24993         fctr.insertFirst(this.el);
24994         
24995         // this is a bit insane - as the paging toolbar seems to detach the el..
24996 //        dom.parentNode.parentNode.parentNode
24997          // they get detached?
24998     }
24999     
25000     
25001     Roo.View.superclass.constructor.call(this);
25002     
25003     
25004 };
25005
25006 Roo.extend(Roo.View, Roo.util.Observable, {
25007     
25008      /**
25009      * @cfg {Roo.data.Store} store Data store to load data from.
25010      */
25011     store : false,
25012     
25013     /**
25014      * @cfg {String|Roo.Element} el The container element.
25015      */
25016     el : '',
25017     
25018     /**
25019      * @cfg {String|Roo.Template} tpl The template used by this View 
25020      */
25021     tpl : false,
25022     /**
25023      * @cfg {String} dataName the named area of the template to use as the data area
25024      *                          Works with domtemplates roo-name="name"
25025      */
25026     dataName: false,
25027     /**
25028      * @cfg {String} selectedClass The css class to add to selected nodes
25029      */
25030     selectedClass : "x-view-selected",
25031      /**
25032      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25033      */
25034     emptyText : "",
25035     
25036     /**
25037      * @cfg {String} text to display on mask (default Loading)
25038      */
25039     mask : false,
25040     /**
25041      * @cfg {Boolean} multiSelect Allow multiple selection
25042      */
25043     multiSelect : false,
25044     /**
25045      * @cfg {Boolean} singleSelect Allow single selection
25046      */
25047     singleSelect:  false,
25048     
25049     /**
25050      * @cfg {Boolean} toggleSelect - selecting 
25051      */
25052     toggleSelect : false,
25053     
25054     /**
25055      * @cfg {Boolean} tickable - selecting 
25056      */
25057     tickable : false,
25058     
25059     /**
25060      * Returns the element this view is bound to.
25061      * @return {Roo.Element}
25062      */
25063     getEl : function(){
25064         return this.wrapEl;
25065     },
25066     
25067     
25068
25069     /**
25070      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25071      */
25072     refresh : function(){
25073         //Roo.log('refresh');
25074         var t = this.tpl;
25075         
25076         // if we are using something like 'domtemplate', then
25077         // the what gets used is:
25078         // t.applySubtemplate(NAME, data, wrapping data..)
25079         // the outer template then get' applied with
25080         //     the store 'extra data'
25081         // and the body get's added to the
25082         //      roo-name="data" node?
25083         //      <span class='roo-tpl-{name}'></span> ?????
25084         
25085         
25086         
25087         this.clearSelections();
25088         this.el.update("");
25089         var html = [];
25090         var records = this.store.getRange();
25091         if(records.length < 1) {
25092             
25093             // is this valid??  = should it render a template??
25094             
25095             this.el.update(this.emptyText);
25096             return;
25097         }
25098         var el = this.el;
25099         if (this.dataName) {
25100             this.el.update(t.apply(this.store.meta)); //????
25101             el = this.el.child('.roo-tpl-' + this.dataName);
25102         }
25103         
25104         for(var i = 0, len = records.length; i < len; i++){
25105             var data = this.prepareData(records[i].data, i, records[i]);
25106             this.fireEvent("preparedata", this, data, i, records[i]);
25107             
25108             var d = Roo.apply({}, data);
25109             
25110             if(this.tickable){
25111                 Roo.apply(d, {'roo-id' : Roo.id()});
25112                 
25113                 var _this = this;
25114             
25115                 Roo.each(this.parent.item, function(item){
25116                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25117                         return;
25118                     }
25119                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25120                 });
25121             }
25122             
25123             html[html.length] = Roo.util.Format.trim(
25124                 this.dataName ?
25125                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25126                     t.apply(d)
25127             );
25128         }
25129         
25130         
25131         
25132         el.update(html.join(""));
25133         this.nodes = el.dom.childNodes;
25134         this.updateIndexes(0);
25135     },
25136     
25137
25138     /**
25139      * Function to override to reformat the data that is sent to
25140      * the template for each node.
25141      * DEPRICATED - use the preparedata event handler.
25142      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25143      * a JSON object for an UpdateManager bound view).
25144      */
25145     prepareData : function(data, index, record)
25146     {
25147         this.fireEvent("preparedata", this, data, index, record);
25148         return data;
25149     },
25150
25151     onUpdate : function(ds, record){
25152         // Roo.log('on update');   
25153         this.clearSelections();
25154         var index = this.store.indexOf(record);
25155         var n = this.nodes[index];
25156         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25157         n.parentNode.removeChild(n);
25158         this.updateIndexes(index, index);
25159     },
25160
25161     
25162     
25163 // --------- FIXME     
25164     onAdd : function(ds, records, index)
25165     {
25166         //Roo.log(['on Add', ds, records, index] );        
25167         this.clearSelections();
25168         if(this.nodes.length == 0){
25169             this.refresh();
25170             return;
25171         }
25172         var n = this.nodes[index];
25173         for(var i = 0, len = records.length; i < len; i++){
25174             var d = this.prepareData(records[i].data, i, records[i]);
25175             if(n){
25176                 this.tpl.insertBefore(n, d);
25177             }else{
25178                 
25179                 this.tpl.append(this.el, d);
25180             }
25181         }
25182         this.updateIndexes(index);
25183     },
25184
25185     onRemove : function(ds, record, index){
25186        // Roo.log('onRemove');
25187         this.clearSelections();
25188         var el = this.dataName  ?
25189             this.el.child('.roo-tpl-' + this.dataName) :
25190             this.el; 
25191         
25192         el.dom.removeChild(this.nodes[index]);
25193         this.updateIndexes(index);
25194     },
25195
25196     /**
25197      * Refresh an individual node.
25198      * @param {Number} index
25199      */
25200     refreshNode : function(index){
25201         this.onUpdate(this.store, this.store.getAt(index));
25202     },
25203
25204     updateIndexes : function(startIndex, endIndex){
25205         var ns = this.nodes;
25206         startIndex = startIndex || 0;
25207         endIndex = endIndex || ns.length - 1;
25208         for(var i = startIndex; i <= endIndex; i++){
25209             ns[i].nodeIndex = i;
25210         }
25211     },
25212
25213     /**
25214      * Changes the data store this view uses and refresh the view.
25215      * @param {Store} store
25216      */
25217     setStore : function(store, initial){
25218         if(!initial && this.store){
25219             this.store.un("datachanged", this.refresh);
25220             this.store.un("add", this.onAdd);
25221             this.store.un("remove", this.onRemove);
25222             this.store.un("update", this.onUpdate);
25223             this.store.un("clear", this.refresh);
25224             this.store.un("beforeload", this.onBeforeLoad);
25225             this.store.un("load", this.onLoad);
25226             this.store.un("loadexception", this.onLoad);
25227         }
25228         if(store){
25229           
25230             store.on("datachanged", this.refresh, this);
25231             store.on("add", this.onAdd, this);
25232             store.on("remove", this.onRemove, this);
25233             store.on("update", this.onUpdate, this);
25234             store.on("clear", this.refresh, this);
25235             store.on("beforeload", this.onBeforeLoad, this);
25236             store.on("load", this.onLoad, this);
25237             store.on("loadexception", this.onLoad, this);
25238         }
25239         
25240         if(store){
25241             this.refresh();
25242         }
25243     },
25244     /**
25245      * onbeforeLoad - masks the loading area.
25246      *
25247      */
25248     onBeforeLoad : function(store,opts)
25249     {
25250          //Roo.log('onBeforeLoad');   
25251         if (!opts.add) {
25252             this.el.update("");
25253         }
25254         this.el.mask(this.mask ? this.mask : "Loading" ); 
25255     },
25256     onLoad : function ()
25257     {
25258         this.el.unmask();
25259     },
25260     
25261
25262     /**
25263      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25264      * @param {HTMLElement} node
25265      * @return {HTMLElement} The template node
25266      */
25267     findItemFromChild : function(node){
25268         var el = this.dataName  ?
25269             this.el.child('.roo-tpl-' + this.dataName,true) :
25270             this.el.dom; 
25271         
25272         if(!node || node.parentNode == el){
25273                     return node;
25274             }
25275             var p = node.parentNode;
25276             while(p && p != el){
25277             if(p.parentNode == el){
25278                 return p;
25279             }
25280             p = p.parentNode;
25281         }
25282             return null;
25283     },
25284
25285     /** @ignore */
25286     onClick : function(e){
25287         var item = this.findItemFromChild(e.getTarget());
25288         if(item){
25289             var index = this.indexOf(item);
25290             if(this.onItemClick(item, index, e) !== false){
25291                 this.fireEvent("click", this, index, item, e);
25292             }
25293         }else{
25294             this.clearSelections();
25295         }
25296     },
25297
25298     /** @ignore */
25299     onContextMenu : function(e){
25300         var item = this.findItemFromChild(e.getTarget());
25301         if(item){
25302             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25303         }
25304     },
25305
25306     /** @ignore */
25307     onDblClick : function(e){
25308         var item = this.findItemFromChild(e.getTarget());
25309         if(item){
25310             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25311         }
25312     },
25313
25314     onItemClick : function(item, index, e)
25315     {
25316         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25317             return false;
25318         }
25319         if (this.toggleSelect) {
25320             var m = this.isSelected(item) ? 'unselect' : 'select';
25321             //Roo.log(m);
25322             var _t = this;
25323             _t[m](item, true, false);
25324             return true;
25325         }
25326         if(this.multiSelect || this.singleSelect){
25327             if(this.multiSelect && e.shiftKey && this.lastSelection){
25328                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25329             }else{
25330                 this.select(item, this.multiSelect && e.ctrlKey);
25331                 this.lastSelection = item;
25332             }
25333             
25334             if(!this.tickable){
25335                 e.preventDefault();
25336             }
25337             
25338         }
25339         return true;
25340     },
25341
25342     /**
25343      * Get the number of selected nodes.
25344      * @return {Number}
25345      */
25346     getSelectionCount : function(){
25347         return this.selections.length;
25348     },
25349
25350     /**
25351      * Get the currently selected nodes.
25352      * @return {Array} An array of HTMLElements
25353      */
25354     getSelectedNodes : function(){
25355         return this.selections;
25356     },
25357
25358     /**
25359      * Get the indexes of the selected nodes.
25360      * @return {Array}
25361      */
25362     getSelectedIndexes : function(){
25363         var indexes = [], s = this.selections;
25364         for(var i = 0, len = s.length; i < len; i++){
25365             indexes.push(s[i].nodeIndex);
25366         }
25367         return indexes;
25368     },
25369
25370     /**
25371      * Clear all selections
25372      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25373      */
25374     clearSelections : function(suppressEvent){
25375         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25376             this.cmp.elements = this.selections;
25377             this.cmp.removeClass(this.selectedClass);
25378             this.selections = [];
25379             if(!suppressEvent){
25380                 this.fireEvent("selectionchange", this, this.selections);
25381             }
25382         }
25383     },
25384
25385     /**
25386      * Returns true if the passed node is selected
25387      * @param {HTMLElement/Number} node The node or node index
25388      * @return {Boolean}
25389      */
25390     isSelected : function(node){
25391         var s = this.selections;
25392         if(s.length < 1){
25393             return false;
25394         }
25395         node = this.getNode(node);
25396         return s.indexOf(node) !== -1;
25397     },
25398
25399     /**
25400      * Selects nodes.
25401      * @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
25402      * @param {Boolean} keepExisting (optional) true to keep existing selections
25403      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25404      */
25405     select : function(nodeInfo, keepExisting, suppressEvent){
25406         if(nodeInfo instanceof Array){
25407             if(!keepExisting){
25408                 this.clearSelections(true);
25409             }
25410             for(var i = 0, len = nodeInfo.length; i < len; i++){
25411                 this.select(nodeInfo[i], true, true);
25412             }
25413             return;
25414         } 
25415         var node = this.getNode(nodeInfo);
25416         if(!node || this.isSelected(node)){
25417             return; // already selected.
25418         }
25419         if(!keepExisting){
25420             this.clearSelections(true);
25421         }
25422         
25423         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25424             Roo.fly(node).addClass(this.selectedClass);
25425             this.selections.push(node);
25426             if(!suppressEvent){
25427                 this.fireEvent("selectionchange", this, this.selections);
25428             }
25429         }
25430         
25431         
25432     },
25433       /**
25434      * Unselects nodes.
25435      * @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
25436      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25437      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25438      */
25439     unselect : function(nodeInfo, keepExisting, suppressEvent)
25440     {
25441         if(nodeInfo instanceof Array){
25442             Roo.each(this.selections, function(s) {
25443                 this.unselect(s, nodeInfo);
25444             }, this);
25445             return;
25446         }
25447         var node = this.getNode(nodeInfo);
25448         if(!node || !this.isSelected(node)){
25449             //Roo.log("not selected");
25450             return; // not selected.
25451         }
25452         // fireevent???
25453         var ns = [];
25454         Roo.each(this.selections, function(s) {
25455             if (s == node ) {
25456                 Roo.fly(node).removeClass(this.selectedClass);
25457
25458                 return;
25459             }
25460             ns.push(s);
25461         },this);
25462         
25463         this.selections= ns;
25464         this.fireEvent("selectionchange", this, this.selections);
25465     },
25466
25467     /**
25468      * Gets a template node.
25469      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25470      * @return {HTMLElement} The node or null if it wasn't found
25471      */
25472     getNode : function(nodeInfo){
25473         if(typeof nodeInfo == "string"){
25474             return document.getElementById(nodeInfo);
25475         }else if(typeof nodeInfo == "number"){
25476             return this.nodes[nodeInfo];
25477         }
25478         return nodeInfo;
25479     },
25480
25481     /**
25482      * Gets a range template nodes.
25483      * @param {Number} startIndex
25484      * @param {Number} endIndex
25485      * @return {Array} An array of nodes
25486      */
25487     getNodes : function(start, end){
25488         var ns = this.nodes;
25489         start = start || 0;
25490         end = typeof end == "undefined" ? ns.length - 1 : end;
25491         var nodes = [];
25492         if(start <= end){
25493             for(var i = start; i <= end; i++){
25494                 nodes.push(ns[i]);
25495             }
25496         } else{
25497             for(var i = start; i >= end; i--){
25498                 nodes.push(ns[i]);
25499             }
25500         }
25501         return nodes;
25502     },
25503
25504     /**
25505      * Finds the index of the passed node
25506      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25507      * @return {Number} The index of the node or -1
25508      */
25509     indexOf : function(node){
25510         node = this.getNode(node);
25511         if(typeof node.nodeIndex == "number"){
25512             return node.nodeIndex;
25513         }
25514         var ns = this.nodes;
25515         for(var i = 0, len = ns.length; i < len; i++){
25516             if(ns[i] == node){
25517                 return i;
25518             }
25519         }
25520         return -1;
25521     }
25522 });
25523 /*
25524  * Based on:
25525  * Ext JS Library 1.1.1
25526  * Copyright(c) 2006-2007, Ext JS, LLC.
25527  *
25528  * Originally Released Under LGPL - original licence link has changed is not relivant.
25529  *
25530  * Fork - LGPL
25531  * <script type="text/javascript">
25532  */
25533
25534 /**
25535  * @class Roo.JsonView
25536  * @extends Roo.View
25537  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25538 <pre><code>
25539 var view = new Roo.JsonView({
25540     container: "my-element",
25541     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25542     multiSelect: true, 
25543     jsonRoot: "data" 
25544 });
25545
25546 // listen for node click?
25547 view.on("click", function(vw, index, node, e){
25548     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25549 });
25550
25551 // direct load of JSON data
25552 view.load("foobar.php");
25553
25554 // Example from my blog list
25555 var tpl = new Roo.Template(
25556     '&lt;div class="entry"&gt;' +
25557     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25558     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25559     "&lt;/div&gt;&lt;hr /&gt;"
25560 );
25561
25562 var moreView = new Roo.JsonView({
25563     container :  "entry-list", 
25564     template : tpl,
25565     jsonRoot: "posts"
25566 });
25567 moreView.on("beforerender", this.sortEntries, this);
25568 moreView.load({
25569     url: "/blog/get-posts.php",
25570     params: "allposts=true",
25571     text: "Loading Blog Entries..."
25572 });
25573 </code></pre>
25574
25575 * Note: old code is supported with arguments : (container, template, config)
25576
25577
25578  * @constructor
25579  * Create a new JsonView
25580  * 
25581  * @param {Object} config The config object
25582  * 
25583  */
25584 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25585     
25586     
25587     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25588
25589     var um = this.el.getUpdateManager();
25590     um.setRenderer(this);
25591     um.on("update", this.onLoad, this);
25592     um.on("failure", this.onLoadException, this);
25593
25594     /**
25595      * @event beforerender
25596      * Fires before rendering of the downloaded JSON data.
25597      * @param {Roo.JsonView} this
25598      * @param {Object} data The JSON data loaded
25599      */
25600     /**
25601      * @event load
25602      * Fires when data is loaded.
25603      * @param {Roo.JsonView} this
25604      * @param {Object} data The JSON data loaded
25605      * @param {Object} response The raw Connect response object
25606      */
25607     /**
25608      * @event loadexception
25609      * Fires when loading fails.
25610      * @param {Roo.JsonView} this
25611      * @param {Object} response The raw Connect response object
25612      */
25613     this.addEvents({
25614         'beforerender' : true,
25615         'load' : true,
25616         'loadexception' : true
25617     });
25618 };
25619 Roo.extend(Roo.JsonView, Roo.View, {
25620     /**
25621      * @type {String} The root property in the loaded JSON object that contains the data
25622      */
25623     jsonRoot : "",
25624
25625     /**
25626      * Refreshes the view.
25627      */
25628     refresh : function(){
25629         this.clearSelections();
25630         this.el.update("");
25631         var html = [];
25632         var o = this.jsonData;
25633         if(o && o.length > 0){
25634             for(var i = 0, len = o.length; i < len; i++){
25635                 var data = this.prepareData(o[i], i, o);
25636                 html[html.length] = this.tpl.apply(data);
25637             }
25638         }else{
25639             html.push(this.emptyText);
25640         }
25641         this.el.update(html.join(""));
25642         this.nodes = this.el.dom.childNodes;
25643         this.updateIndexes(0);
25644     },
25645
25646     /**
25647      * 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.
25648      * @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:
25649      <pre><code>
25650      view.load({
25651          url: "your-url.php",
25652          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25653          callback: yourFunction,
25654          scope: yourObject, //(optional scope)
25655          discardUrl: false,
25656          nocache: false,
25657          text: "Loading...",
25658          timeout: 30,
25659          scripts: false
25660      });
25661      </code></pre>
25662      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25663      * 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.
25664      * @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}
25665      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25666      * @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.
25667      */
25668     load : function(){
25669         var um = this.el.getUpdateManager();
25670         um.update.apply(um, arguments);
25671     },
25672
25673     render : function(el, response){
25674         this.clearSelections();
25675         this.el.update("");
25676         var o;
25677         try{
25678             o = Roo.util.JSON.decode(response.responseText);
25679             if(this.jsonRoot){
25680                 
25681                 o = o[this.jsonRoot];
25682             }
25683         } catch(e){
25684         }
25685         /**
25686          * The current JSON data or null
25687          */
25688         this.jsonData = o;
25689         this.beforeRender();
25690         this.refresh();
25691     },
25692
25693 /**
25694  * Get the number of records in the current JSON dataset
25695  * @return {Number}
25696  */
25697     getCount : function(){
25698         return this.jsonData ? this.jsonData.length : 0;
25699     },
25700
25701 /**
25702  * Returns the JSON object for the specified node(s)
25703  * @param {HTMLElement/Array} node The node or an array of nodes
25704  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25705  * you get the JSON object for the node
25706  */
25707     getNodeData : function(node){
25708         if(node instanceof Array){
25709             var data = [];
25710             for(var i = 0, len = node.length; i < len; i++){
25711                 data.push(this.getNodeData(node[i]));
25712             }
25713             return data;
25714         }
25715         return this.jsonData[this.indexOf(node)] || null;
25716     },
25717
25718     beforeRender : function(){
25719         this.snapshot = this.jsonData;
25720         if(this.sortInfo){
25721             this.sort.apply(this, this.sortInfo);
25722         }
25723         this.fireEvent("beforerender", this, this.jsonData);
25724     },
25725
25726     onLoad : function(el, o){
25727         this.fireEvent("load", this, this.jsonData, o);
25728     },
25729
25730     onLoadException : function(el, o){
25731         this.fireEvent("loadexception", this, o);
25732     },
25733
25734 /**
25735  * Filter the data by a specific property.
25736  * @param {String} property A property on your JSON objects
25737  * @param {String/RegExp} value Either string that the property values
25738  * should start with, or a RegExp to test against the property
25739  */
25740     filter : function(property, value){
25741         if(this.jsonData){
25742             var data = [];
25743             var ss = this.snapshot;
25744             if(typeof value == "string"){
25745                 var vlen = value.length;
25746                 if(vlen == 0){
25747                     this.clearFilter();
25748                     return;
25749                 }
25750                 value = value.toLowerCase();
25751                 for(var i = 0, len = ss.length; i < len; i++){
25752                     var o = ss[i];
25753                     if(o[property].substr(0, vlen).toLowerCase() == value){
25754                         data.push(o);
25755                     }
25756                 }
25757             } else if(value.exec){ // regex?
25758                 for(var i = 0, len = ss.length; i < len; i++){
25759                     var o = ss[i];
25760                     if(value.test(o[property])){
25761                         data.push(o);
25762                     }
25763                 }
25764             } else{
25765                 return;
25766             }
25767             this.jsonData = data;
25768             this.refresh();
25769         }
25770     },
25771
25772 /**
25773  * Filter by a function. The passed function will be called with each
25774  * object in the current dataset. If the function returns true the value is kept,
25775  * otherwise it is filtered.
25776  * @param {Function} fn
25777  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25778  */
25779     filterBy : function(fn, scope){
25780         if(this.jsonData){
25781             var data = [];
25782             var ss = this.snapshot;
25783             for(var i = 0, len = ss.length; i < len; i++){
25784                 var o = ss[i];
25785                 if(fn.call(scope || this, o)){
25786                     data.push(o);
25787                 }
25788             }
25789             this.jsonData = data;
25790             this.refresh();
25791         }
25792     },
25793
25794 /**
25795  * Clears the current filter.
25796  */
25797     clearFilter : function(){
25798         if(this.snapshot && this.jsonData != this.snapshot){
25799             this.jsonData = this.snapshot;
25800             this.refresh();
25801         }
25802     },
25803
25804
25805 /**
25806  * Sorts the data for this view and refreshes it.
25807  * @param {String} property A property on your JSON objects to sort on
25808  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25809  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25810  */
25811     sort : function(property, dir, sortType){
25812         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25813         if(this.jsonData){
25814             var p = property;
25815             var dsc = dir && dir.toLowerCase() == "desc";
25816             var f = function(o1, o2){
25817                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25818                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25819                 ;
25820                 if(v1 < v2){
25821                     return dsc ? +1 : -1;
25822                 } else if(v1 > v2){
25823                     return dsc ? -1 : +1;
25824                 } else{
25825                     return 0;
25826                 }
25827             };
25828             this.jsonData.sort(f);
25829             this.refresh();
25830             if(this.jsonData != this.snapshot){
25831                 this.snapshot.sort(f);
25832             }
25833         }
25834     }
25835 });/*
25836  * Based on:
25837  * Ext JS Library 1.1.1
25838  * Copyright(c) 2006-2007, Ext JS, LLC.
25839  *
25840  * Originally Released Under LGPL - original licence link has changed is not relivant.
25841  *
25842  * Fork - LGPL
25843  * <script type="text/javascript">
25844  */
25845  
25846
25847 /**
25848  * @class Roo.ColorPalette
25849  * @extends Roo.Component
25850  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25851  * Here's an example of typical usage:
25852  * <pre><code>
25853 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25854 cp.render('my-div');
25855
25856 cp.on('select', function(palette, selColor){
25857     // do something with selColor
25858 });
25859 </code></pre>
25860  * @constructor
25861  * Create a new ColorPalette
25862  * @param {Object} config The config object
25863  */
25864 Roo.ColorPalette = function(config){
25865     Roo.ColorPalette.superclass.constructor.call(this, config);
25866     this.addEvents({
25867         /**
25868              * @event select
25869              * Fires when a color is selected
25870              * @param {ColorPalette} this
25871              * @param {String} color The 6-digit color hex code (without the # symbol)
25872              */
25873         select: true
25874     });
25875
25876     if(this.handler){
25877         this.on("select", this.handler, this.scope, true);
25878     }
25879 };
25880 Roo.extend(Roo.ColorPalette, Roo.Component, {
25881     /**
25882      * @cfg {String} itemCls
25883      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25884      */
25885     itemCls : "x-color-palette",
25886     /**
25887      * @cfg {String} value
25888      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25889      * the hex codes are case-sensitive.
25890      */
25891     value : null,
25892     clickEvent:'click',
25893     // private
25894     ctype: "Roo.ColorPalette",
25895
25896     /**
25897      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25898      */
25899     allowReselect : false,
25900
25901     /**
25902      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25903      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25904      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25905      * of colors with the width setting until the box is symmetrical.</p>
25906      * <p>You can override individual colors if needed:</p>
25907      * <pre><code>
25908 var cp = new Roo.ColorPalette();
25909 cp.colors[0] = "FF0000";  // change the first box to red
25910 </code></pre>
25911
25912 Or you can provide a custom array of your own for complete control:
25913 <pre><code>
25914 var cp = new Roo.ColorPalette();
25915 cp.colors = ["000000", "993300", "333300"];
25916 </code></pre>
25917      * @type Array
25918      */
25919     colors : [
25920         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25921         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25922         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25923         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25924         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25925     ],
25926
25927     // private
25928     onRender : function(container, position){
25929         var t = new Roo.MasterTemplate(
25930             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25931         );
25932         var c = this.colors;
25933         for(var i = 0, len = c.length; i < len; i++){
25934             t.add([c[i]]);
25935         }
25936         var el = document.createElement("div");
25937         el.className = this.itemCls;
25938         t.overwrite(el);
25939         container.dom.insertBefore(el, position);
25940         this.el = Roo.get(el);
25941         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25942         if(this.clickEvent != 'click'){
25943             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25944         }
25945     },
25946
25947     // private
25948     afterRender : function(){
25949         Roo.ColorPalette.superclass.afterRender.call(this);
25950         if(this.value){
25951             var s = this.value;
25952             this.value = null;
25953             this.select(s);
25954         }
25955     },
25956
25957     // private
25958     handleClick : function(e, t){
25959         e.preventDefault();
25960         if(!this.disabled){
25961             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25962             this.select(c.toUpperCase());
25963         }
25964     },
25965
25966     /**
25967      * Selects the specified color in the palette (fires the select event)
25968      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25969      */
25970     select : function(color){
25971         color = color.replace("#", "");
25972         if(color != this.value || this.allowReselect){
25973             var el = this.el;
25974             if(this.value){
25975                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25976             }
25977             el.child("a.color-"+color).addClass("x-color-palette-sel");
25978             this.value = color;
25979             this.fireEvent("select", this, color);
25980         }
25981     }
25982 });/*
25983  * Based on:
25984  * Ext JS Library 1.1.1
25985  * Copyright(c) 2006-2007, Ext JS, LLC.
25986  *
25987  * Originally Released Under LGPL - original licence link has changed is not relivant.
25988  *
25989  * Fork - LGPL
25990  * <script type="text/javascript">
25991  */
25992  
25993 /**
25994  * @class Roo.DatePicker
25995  * @extends Roo.Component
25996  * Simple date picker class.
25997  * @constructor
25998  * Create a new DatePicker
25999  * @param {Object} config The config object
26000  */
26001 Roo.DatePicker = function(config){
26002     Roo.DatePicker.superclass.constructor.call(this, config);
26003
26004     this.value = config && config.value ?
26005                  config.value.clearTime() : new Date().clearTime();
26006
26007     this.addEvents({
26008         /**
26009              * @event select
26010              * Fires when a date is selected
26011              * @param {DatePicker} this
26012              * @param {Date} date The selected date
26013              */
26014         'select': true,
26015         /**
26016              * @event monthchange
26017              * Fires when the displayed month changes 
26018              * @param {DatePicker} this
26019              * @param {Date} date The selected month
26020              */
26021         'monthchange': true
26022     });
26023
26024     if(this.handler){
26025         this.on("select", this.handler,  this.scope || this);
26026     }
26027     // build the disabledDatesRE
26028     if(!this.disabledDatesRE && this.disabledDates){
26029         var dd = this.disabledDates;
26030         var re = "(?:";
26031         for(var i = 0; i < dd.length; i++){
26032             re += dd[i];
26033             if(i != dd.length-1) re += "|";
26034         }
26035         this.disabledDatesRE = new RegExp(re + ")");
26036     }
26037 };
26038
26039 Roo.extend(Roo.DatePicker, Roo.Component, {
26040     /**
26041      * @cfg {String} todayText
26042      * The text to display on the button that selects the current date (defaults to "Today")
26043      */
26044     todayText : "Today",
26045     /**
26046      * @cfg {String} okText
26047      * The text to display on the ok button
26048      */
26049     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26050     /**
26051      * @cfg {String} cancelText
26052      * The text to display on the cancel button
26053      */
26054     cancelText : "Cancel",
26055     /**
26056      * @cfg {String} todayTip
26057      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26058      */
26059     todayTip : "{0} (Spacebar)",
26060     /**
26061      * @cfg {Date} minDate
26062      * Minimum allowable date (JavaScript date object, defaults to null)
26063      */
26064     minDate : null,
26065     /**
26066      * @cfg {Date} maxDate
26067      * Maximum allowable date (JavaScript date object, defaults to null)
26068      */
26069     maxDate : null,
26070     /**
26071      * @cfg {String} minText
26072      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26073      */
26074     minText : "This date is before the minimum date",
26075     /**
26076      * @cfg {String} maxText
26077      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26078      */
26079     maxText : "This date is after the maximum date",
26080     /**
26081      * @cfg {String} format
26082      * The default date format string which can be overriden for localization support.  The format must be
26083      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26084      */
26085     format : "m/d/y",
26086     /**
26087      * @cfg {Array} disabledDays
26088      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26089      */
26090     disabledDays : null,
26091     /**
26092      * @cfg {String} disabledDaysText
26093      * The tooltip to display when the date falls on a disabled day (defaults to "")
26094      */
26095     disabledDaysText : "",
26096     /**
26097      * @cfg {RegExp} disabledDatesRE
26098      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26099      */
26100     disabledDatesRE : null,
26101     /**
26102      * @cfg {String} disabledDatesText
26103      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26104      */
26105     disabledDatesText : "",
26106     /**
26107      * @cfg {Boolean} constrainToViewport
26108      * True to constrain the date picker to the viewport (defaults to true)
26109      */
26110     constrainToViewport : true,
26111     /**
26112      * @cfg {Array} monthNames
26113      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26114      */
26115     monthNames : Date.monthNames,
26116     /**
26117      * @cfg {Array} dayNames
26118      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26119      */
26120     dayNames : Date.dayNames,
26121     /**
26122      * @cfg {String} nextText
26123      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26124      */
26125     nextText: 'Next Month (Control+Right)',
26126     /**
26127      * @cfg {String} prevText
26128      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26129      */
26130     prevText: 'Previous Month (Control+Left)',
26131     /**
26132      * @cfg {String} monthYearText
26133      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26134      */
26135     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26136     /**
26137      * @cfg {Number} startDay
26138      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26139      */
26140     startDay : 0,
26141     /**
26142      * @cfg {Bool} showClear
26143      * Show a clear button (usefull for date form elements that can be blank.)
26144      */
26145     
26146     showClear: false,
26147     
26148     /**
26149      * Sets the value of the date field
26150      * @param {Date} value The date to set
26151      */
26152     setValue : function(value){
26153         var old = this.value;
26154         
26155         if (typeof(value) == 'string') {
26156          
26157             value = Date.parseDate(value, this.format);
26158         }
26159         if (!value) {
26160             value = new Date();
26161         }
26162         
26163         this.value = value.clearTime(true);
26164         if(this.el){
26165             this.update(this.value);
26166         }
26167     },
26168
26169     /**
26170      * Gets the current selected value of the date field
26171      * @return {Date} The selected date
26172      */
26173     getValue : function(){
26174         return this.value;
26175     },
26176
26177     // private
26178     focus : function(){
26179         if(this.el){
26180             this.update(this.activeDate);
26181         }
26182     },
26183
26184     // privateval
26185     onRender : function(container, position){
26186         
26187         var m = [
26188              '<table cellspacing="0">',
26189                 '<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>',
26190                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26191         var dn = this.dayNames;
26192         for(var i = 0; i < 7; i++){
26193             var d = this.startDay+i;
26194             if(d > 6){
26195                 d = d-7;
26196             }
26197             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26198         }
26199         m[m.length] = "</tr></thead><tbody><tr>";
26200         for(var i = 0; i < 42; i++) {
26201             if(i % 7 == 0 && i != 0){
26202                 m[m.length] = "</tr><tr>";
26203             }
26204             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26205         }
26206         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26207             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26208
26209         var el = document.createElement("div");
26210         el.className = "x-date-picker";
26211         el.innerHTML = m.join("");
26212
26213         container.dom.insertBefore(el, position);
26214
26215         this.el = Roo.get(el);
26216         this.eventEl = Roo.get(el.firstChild);
26217
26218         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26219             handler: this.showPrevMonth,
26220             scope: this,
26221             preventDefault:true,
26222             stopDefault:true
26223         });
26224
26225         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26226             handler: this.showNextMonth,
26227             scope: this,
26228             preventDefault:true,
26229             stopDefault:true
26230         });
26231
26232         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26233
26234         this.monthPicker = this.el.down('div.x-date-mp');
26235         this.monthPicker.enableDisplayMode('block');
26236         
26237         var kn = new Roo.KeyNav(this.eventEl, {
26238             "left" : function(e){
26239                 e.ctrlKey ?
26240                     this.showPrevMonth() :
26241                     this.update(this.activeDate.add("d", -1));
26242             },
26243
26244             "right" : function(e){
26245                 e.ctrlKey ?
26246                     this.showNextMonth() :
26247                     this.update(this.activeDate.add("d", 1));
26248             },
26249
26250             "up" : function(e){
26251                 e.ctrlKey ?
26252                     this.showNextYear() :
26253                     this.update(this.activeDate.add("d", -7));
26254             },
26255
26256             "down" : function(e){
26257                 e.ctrlKey ?
26258                     this.showPrevYear() :
26259                     this.update(this.activeDate.add("d", 7));
26260             },
26261
26262             "pageUp" : function(e){
26263                 this.showNextMonth();
26264             },
26265
26266             "pageDown" : function(e){
26267                 this.showPrevMonth();
26268             },
26269
26270             "enter" : function(e){
26271                 e.stopPropagation();
26272                 return true;
26273             },
26274
26275             scope : this
26276         });
26277
26278         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26279
26280         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26281
26282         this.el.unselectable();
26283         
26284         this.cells = this.el.select("table.x-date-inner tbody td");
26285         this.textNodes = this.el.query("table.x-date-inner tbody span");
26286
26287         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26288             text: "&#160;",
26289             tooltip: this.monthYearText
26290         });
26291
26292         this.mbtn.on('click', this.showMonthPicker, this);
26293         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26294
26295
26296         var today = (new Date()).dateFormat(this.format);
26297         
26298         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26299         if (this.showClear) {
26300             baseTb.add( new Roo.Toolbar.Fill());
26301         }
26302         baseTb.add({
26303             text: String.format(this.todayText, today),
26304             tooltip: String.format(this.todayTip, today),
26305             handler: this.selectToday,
26306             scope: this
26307         });
26308         
26309         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26310             
26311         //});
26312         if (this.showClear) {
26313             
26314             baseTb.add( new Roo.Toolbar.Fill());
26315             baseTb.add({
26316                 text: '&#160;',
26317                 cls: 'x-btn-icon x-btn-clear',
26318                 handler: function() {
26319                     //this.value = '';
26320                     this.fireEvent("select", this, '');
26321                 },
26322                 scope: this
26323             });
26324         }
26325         
26326         
26327         if(Roo.isIE){
26328             this.el.repaint();
26329         }
26330         this.update(this.value);
26331     },
26332
26333     createMonthPicker : function(){
26334         if(!this.monthPicker.dom.firstChild){
26335             var buf = ['<table border="0" cellspacing="0">'];
26336             for(var i = 0; i < 6; i++){
26337                 buf.push(
26338                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26339                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26340                     i == 0 ?
26341                     '<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>' :
26342                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26343                 );
26344             }
26345             buf.push(
26346                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26347                     this.okText,
26348                     '</button><button type="button" class="x-date-mp-cancel">',
26349                     this.cancelText,
26350                     '</button></td></tr>',
26351                 '</table>'
26352             );
26353             this.monthPicker.update(buf.join(''));
26354             this.monthPicker.on('click', this.onMonthClick, this);
26355             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26356
26357             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26358             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26359
26360             this.mpMonths.each(function(m, a, i){
26361                 i += 1;
26362                 if((i%2) == 0){
26363                     m.dom.xmonth = 5 + Math.round(i * .5);
26364                 }else{
26365                     m.dom.xmonth = Math.round((i-1) * .5);
26366                 }
26367             });
26368         }
26369     },
26370
26371     showMonthPicker : function(){
26372         this.createMonthPicker();
26373         var size = this.el.getSize();
26374         this.monthPicker.setSize(size);
26375         this.monthPicker.child('table').setSize(size);
26376
26377         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26378         this.updateMPMonth(this.mpSelMonth);
26379         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26380         this.updateMPYear(this.mpSelYear);
26381
26382         this.monthPicker.slideIn('t', {duration:.2});
26383     },
26384
26385     updateMPYear : function(y){
26386         this.mpyear = y;
26387         var ys = this.mpYears.elements;
26388         for(var i = 1; i <= 10; i++){
26389             var td = ys[i-1], y2;
26390             if((i%2) == 0){
26391                 y2 = y + Math.round(i * .5);
26392                 td.firstChild.innerHTML = y2;
26393                 td.xyear = y2;
26394             }else{
26395                 y2 = y - (5-Math.round(i * .5));
26396                 td.firstChild.innerHTML = y2;
26397                 td.xyear = y2;
26398             }
26399             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26400         }
26401     },
26402
26403     updateMPMonth : function(sm){
26404         this.mpMonths.each(function(m, a, i){
26405             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26406         });
26407     },
26408
26409     selectMPMonth: function(m){
26410         
26411     },
26412
26413     onMonthClick : function(e, t){
26414         e.stopEvent();
26415         var el = new Roo.Element(t), pn;
26416         if(el.is('button.x-date-mp-cancel')){
26417             this.hideMonthPicker();
26418         }
26419         else if(el.is('button.x-date-mp-ok')){
26420             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26421             this.hideMonthPicker();
26422         }
26423         else if(pn = el.up('td.x-date-mp-month', 2)){
26424             this.mpMonths.removeClass('x-date-mp-sel');
26425             pn.addClass('x-date-mp-sel');
26426             this.mpSelMonth = pn.dom.xmonth;
26427         }
26428         else if(pn = el.up('td.x-date-mp-year', 2)){
26429             this.mpYears.removeClass('x-date-mp-sel');
26430             pn.addClass('x-date-mp-sel');
26431             this.mpSelYear = pn.dom.xyear;
26432         }
26433         else if(el.is('a.x-date-mp-prev')){
26434             this.updateMPYear(this.mpyear-10);
26435         }
26436         else if(el.is('a.x-date-mp-next')){
26437             this.updateMPYear(this.mpyear+10);
26438         }
26439     },
26440
26441     onMonthDblClick : function(e, t){
26442         e.stopEvent();
26443         var el = new Roo.Element(t), pn;
26444         if(pn = el.up('td.x-date-mp-month', 2)){
26445             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26446             this.hideMonthPicker();
26447         }
26448         else if(pn = el.up('td.x-date-mp-year', 2)){
26449             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26450             this.hideMonthPicker();
26451         }
26452     },
26453
26454     hideMonthPicker : function(disableAnim){
26455         if(this.monthPicker){
26456             if(disableAnim === true){
26457                 this.monthPicker.hide();
26458             }else{
26459                 this.monthPicker.slideOut('t', {duration:.2});
26460             }
26461         }
26462     },
26463
26464     // private
26465     showPrevMonth : function(e){
26466         this.update(this.activeDate.add("mo", -1));
26467     },
26468
26469     // private
26470     showNextMonth : function(e){
26471         this.update(this.activeDate.add("mo", 1));
26472     },
26473
26474     // private
26475     showPrevYear : function(){
26476         this.update(this.activeDate.add("y", -1));
26477     },
26478
26479     // private
26480     showNextYear : function(){
26481         this.update(this.activeDate.add("y", 1));
26482     },
26483
26484     // private
26485     handleMouseWheel : function(e){
26486         var delta = e.getWheelDelta();
26487         if(delta > 0){
26488             this.showPrevMonth();
26489             e.stopEvent();
26490         } else if(delta < 0){
26491             this.showNextMonth();
26492             e.stopEvent();
26493         }
26494     },
26495
26496     // private
26497     handleDateClick : function(e, t){
26498         e.stopEvent();
26499         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26500             this.setValue(new Date(t.dateValue));
26501             this.fireEvent("select", this, this.value);
26502         }
26503     },
26504
26505     // private
26506     selectToday : function(){
26507         this.setValue(new Date().clearTime());
26508         this.fireEvent("select", this, this.value);
26509     },
26510
26511     // private
26512     update : function(date)
26513     {
26514         var vd = this.activeDate;
26515         this.activeDate = date;
26516         if(vd && this.el){
26517             var t = date.getTime();
26518             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26519                 this.cells.removeClass("x-date-selected");
26520                 this.cells.each(function(c){
26521                    if(c.dom.firstChild.dateValue == t){
26522                        c.addClass("x-date-selected");
26523                        setTimeout(function(){
26524                             try{c.dom.firstChild.focus();}catch(e){}
26525                        }, 50);
26526                        return false;
26527                    }
26528                 });
26529                 return;
26530             }
26531         }
26532         
26533         var days = date.getDaysInMonth();
26534         var firstOfMonth = date.getFirstDateOfMonth();
26535         var startingPos = firstOfMonth.getDay()-this.startDay;
26536
26537         if(startingPos <= this.startDay){
26538             startingPos += 7;
26539         }
26540
26541         var pm = date.add("mo", -1);
26542         var prevStart = pm.getDaysInMonth()-startingPos;
26543
26544         var cells = this.cells.elements;
26545         var textEls = this.textNodes;
26546         days += startingPos;
26547
26548         // convert everything to numbers so it's fast
26549         var day = 86400000;
26550         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26551         var today = new Date().clearTime().getTime();
26552         var sel = date.clearTime().getTime();
26553         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26554         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26555         var ddMatch = this.disabledDatesRE;
26556         var ddText = this.disabledDatesText;
26557         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26558         var ddaysText = this.disabledDaysText;
26559         var format = this.format;
26560
26561         var setCellClass = function(cal, cell){
26562             cell.title = "";
26563             var t = d.getTime();
26564             cell.firstChild.dateValue = t;
26565             if(t == today){
26566                 cell.className += " x-date-today";
26567                 cell.title = cal.todayText;
26568             }
26569             if(t == sel){
26570                 cell.className += " x-date-selected";
26571                 setTimeout(function(){
26572                     try{cell.firstChild.focus();}catch(e){}
26573                 }, 50);
26574             }
26575             // disabling
26576             if(t < min) {
26577                 cell.className = " x-date-disabled";
26578                 cell.title = cal.minText;
26579                 return;
26580             }
26581             if(t > max) {
26582                 cell.className = " x-date-disabled";
26583                 cell.title = cal.maxText;
26584                 return;
26585             }
26586             if(ddays){
26587                 if(ddays.indexOf(d.getDay()) != -1){
26588                     cell.title = ddaysText;
26589                     cell.className = " x-date-disabled";
26590                 }
26591             }
26592             if(ddMatch && format){
26593                 var fvalue = d.dateFormat(format);
26594                 if(ddMatch.test(fvalue)){
26595                     cell.title = ddText.replace("%0", fvalue);
26596                     cell.className = " x-date-disabled";
26597                 }
26598             }
26599         };
26600
26601         var i = 0;
26602         for(; i < startingPos; i++) {
26603             textEls[i].innerHTML = (++prevStart);
26604             d.setDate(d.getDate()+1);
26605             cells[i].className = "x-date-prevday";
26606             setCellClass(this, cells[i]);
26607         }
26608         for(; i < days; i++){
26609             intDay = i - startingPos + 1;
26610             textEls[i].innerHTML = (intDay);
26611             d.setDate(d.getDate()+1);
26612             cells[i].className = "x-date-active";
26613             setCellClass(this, cells[i]);
26614         }
26615         var extraDays = 0;
26616         for(; i < 42; i++) {
26617              textEls[i].innerHTML = (++extraDays);
26618              d.setDate(d.getDate()+1);
26619              cells[i].className = "x-date-nextday";
26620              setCellClass(this, cells[i]);
26621         }
26622
26623         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26624         this.fireEvent('monthchange', this, date);
26625         
26626         if(!this.internalRender){
26627             var main = this.el.dom.firstChild;
26628             var w = main.offsetWidth;
26629             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26630             Roo.fly(main).setWidth(w);
26631             this.internalRender = true;
26632             // opera does not respect the auto grow header center column
26633             // then, after it gets a width opera refuses to recalculate
26634             // without a second pass
26635             if(Roo.isOpera && !this.secondPass){
26636                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26637                 this.secondPass = true;
26638                 this.update.defer(10, this, [date]);
26639             }
26640         }
26641         
26642         
26643     }
26644 });        /*
26645  * Based on:
26646  * Ext JS Library 1.1.1
26647  * Copyright(c) 2006-2007, Ext JS, LLC.
26648  *
26649  * Originally Released Under LGPL - original licence link has changed is not relivant.
26650  *
26651  * Fork - LGPL
26652  * <script type="text/javascript">
26653  */
26654 /**
26655  * @class Roo.TabPanel
26656  * @extends Roo.util.Observable
26657  * A lightweight tab container.
26658  * <br><br>
26659  * Usage:
26660  * <pre><code>
26661 // basic tabs 1, built from existing content
26662 var tabs = new Roo.TabPanel("tabs1");
26663 tabs.addTab("script", "View Script");
26664 tabs.addTab("markup", "View Markup");
26665 tabs.activate("script");
26666
26667 // more advanced tabs, built from javascript
26668 var jtabs = new Roo.TabPanel("jtabs");
26669 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26670
26671 // set up the UpdateManager
26672 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26673 var updater = tab2.getUpdateManager();
26674 updater.setDefaultUrl("ajax1.htm");
26675 tab2.on('activate', updater.refresh, updater, true);
26676
26677 // Use setUrl for Ajax loading
26678 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26679 tab3.setUrl("ajax2.htm", null, true);
26680
26681 // Disabled tab
26682 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26683 tab4.disable();
26684
26685 jtabs.activate("jtabs-1");
26686  * </code></pre>
26687  * @constructor
26688  * Create a new TabPanel.
26689  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26690  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26691  */
26692 Roo.TabPanel = function(container, config){
26693     /**
26694     * The container element for this TabPanel.
26695     * @type Roo.Element
26696     */
26697     this.el = Roo.get(container, true);
26698     if(config){
26699         if(typeof config == "boolean"){
26700             this.tabPosition = config ? "bottom" : "top";
26701         }else{
26702             Roo.apply(this, config);
26703         }
26704     }
26705     if(this.tabPosition == "bottom"){
26706         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26707         this.el.addClass("x-tabs-bottom");
26708     }
26709     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26710     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26711     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26712     if(Roo.isIE){
26713         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26714     }
26715     if(this.tabPosition != "bottom"){
26716         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26717          * @type Roo.Element
26718          */
26719         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26720         this.el.addClass("x-tabs-top");
26721     }
26722     this.items = [];
26723
26724     this.bodyEl.setStyle("position", "relative");
26725
26726     this.active = null;
26727     this.activateDelegate = this.activate.createDelegate(this);
26728
26729     this.addEvents({
26730         /**
26731          * @event tabchange
26732          * Fires when the active tab changes
26733          * @param {Roo.TabPanel} this
26734          * @param {Roo.TabPanelItem} activePanel The new active tab
26735          */
26736         "tabchange": true,
26737         /**
26738          * @event beforetabchange
26739          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26740          * @param {Roo.TabPanel} this
26741          * @param {Object} e Set cancel to true on this object to cancel the tab change
26742          * @param {Roo.TabPanelItem} tab The tab being changed to
26743          */
26744         "beforetabchange" : true
26745     });
26746
26747     Roo.EventManager.onWindowResize(this.onResize, this);
26748     this.cpad = this.el.getPadding("lr");
26749     this.hiddenCount = 0;
26750
26751
26752     // toolbar on the tabbar support...
26753     if (this.toolbar) {
26754         var tcfg = this.toolbar;
26755         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26756         this.toolbar = new Roo.Toolbar(tcfg);
26757         if (Roo.isSafari) {
26758             var tbl = tcfg.container.child('table', true);
26759             tbl.setAttribute('width', '100%');
26760         }
26761         
26762     }
26763    
26764
26765
26766     Roo.TabPanel.superclass.constructor.call(this);
26767 };
26768
26769 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26770     /*
26771      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26772      */
26773     tabPosition : "top",
26774     /*
26775      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26776      */
26777     currentTabWidth : 0,
26778     /*
26779      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26780      */
26781     minTabWidth : 40,
26782     /*
26783      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26784      */
26785     maxTabWidth : 250,
26786     /*
26787      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26788      */
26789     preferredTabWidth : 175,
26790     /*
26791      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26792      */
26793     resizeTabs : false,
26794     /*
26795      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26796      */
26797     monitorResize : true,
26798     /*
26799      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26800      */
26801     toolbar : false,
26802
26803     /**
26804      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26805      * @param {String} id The id of the div to use <b>or create</b>
26806      * @param {String} text The text for the tab
26807      * @param {String} content (optional) Content to put in the TabPanelItem body
26808      * @param {Boolean} closable (optional) True to create a close icon on the tab
26809      * @return {Roo.TabPanelItem} The created TabPanelItem
26810      */
26811     addTab : function(id, text, content, closable){
26812         var item = new Roo.TabPanelItem(this, id, text, closable);
26813         this.addTabItem(item);
26814         if(content){
26815             item.setContent(content);
26816         }
26817         return item;
26818     },
26819
26820     /**
26821      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26822      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26823      * @return {Roo.TabPanelItem}
26824      */
26825     getTab : function(id){
26826         return this.items[id];
26827     },
26828
26829     /**
26830      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26831      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26832      */
26833     hideTab : function(id){
26834         var t = this.items[id];
26835         if(!t.isHidden()){
26836            t.setHidden(true);
26837            this.hiddenCount++;
26838            this.autoSizeTabs();
26839         }
26840     },
26841
26842     /**
26843      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26844      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26845      */
26846     unhideTab : function(id){
26847         var t = this.items[id];
26848         if(t.isHidden()){
26849            t.setHidden(false);
26850            this.hiddenCount--;
26851            this.autoSizeTabs();
26852         }
26853     },
26854
26855     /**
26856      * Adds an existing {@link Roo.TabPanelItem}.
26857      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26858      */
26859     addTabItem : function(item){
26860         this.items[item.id] = item;
26861         this.items.push(item);
26862         if(this.resizeTabs){
26863            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26864            this.autoSizeTabs();
26865         }else{
26866             item.autoSize();
26867         }
26868     },
26869
26870     /**
26871      * Removes a {@link Roo.TabPanelItem}.
26872      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26873      */
26874     removeTab : function(id){
26875         var items = this.items;
26876         var tab = items[id];
26877         if(!tab) { return; }
26878         var index = items.indexOf(tab);
26879         if(this.active == tab && items.length > 1){
26880             var newTab = this.getNextAvailable(index);
26881             if(newTab) {
26882                 newTab.activate();
26883             }
26884         }
26885         this.stripEl.dom.removeChild(tab.pnode.dom);
26886         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26887             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26888         }
26889         items.splice(index, 1);
26890         delete this.items[tab.id];
26891         tab.fireEvent("close", tab);
26892         tab.purgeListeners();
26893         this.autoSizeTabs();
26894     },
26895
26896     getNextAvailable : function(start){
26897         var items = this.items;
26898         var index = start;
26899         // look for a next tab that will slide over to
26900         // replace the one being removed
26901         while(index < items.length){
26902             var item = items[++index];
26903             if(item && !item.isHidden()){
26904                 return item;
26905             }
26906         }
26907         // if one isn't found select the previous tab (on the left)
26908         index = start;
26909         while(index >= 0){
26910             var item = items[--index];
26911             if(item && !item.isHidden()){
26912                 return item;
26913             }
26914         }
26915         return null;
26916     },
26917
26918     /**
26919      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26920      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26921      */
26922     disableTab : function(id){
26923         var tab = this.items[id];
26924         if(tab && this.active != tab){
26925             tab.disable();
26926         }
26927     },
26928
26929     /**
26930      * Enables a {@link Roo.TabPanelItem} that is disabled.
26931      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26932      */
26933     enableTab : function(id){
26934         var tab = this.items[id];
26935         tab.enable();
26936     },
26937
26938     /**
26939      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26940      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26941      * @return {Roo.TabPanelItem} The TabPanelItem.
26942      */
26943     activate : function(id){
26944         var tab = this.items[id];
26945         if(!tab){
26946             return null;
26947         }
26948         if(tab == this.active || tab.disabled){
26949             return tab;
26950         }
26951         var e = {};
26952         this.fireEvent("beforetabchange", this, e, tab);
26953         if(e.cancel !== true && !tab.disabled){
26954             if(this.active){
26955                 this.active.hide();
26956             }
26957             this.active = this.items[id];
26958             this.active.show();
26959             this.fireEvent("tabchange", this, this.active);
26960         }
26961         return tab;
26962     },
26963
26964     /**
26965      * Gets the active {@link Roo.TabPanelItem}.
26966      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26967      */
26968     getActiveTab : function(){
26969         return this.active;
26970     },
26971
26972     /**
26973      * Updates the tab body element to fit the height of the container element
26974      * for overflow scrolling
26975      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26976      */
26977     syncHeight : function(targetHeight){
26978         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26979         var bm = this.bodyEl.getMargins();
26980         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26981         this.bodyEl.setHeight(newHeight);
26982         return newHeight;
26983     },
26984
26985     onResize : function(){
26986         if(this.monitorResize){
26987             this.autoSizeTabs();
26988         }
26989     },
26990
26991     /**
26992      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26993      */
26994     beginUpdate : function(){
26995         this.updating = true;
26996     },
26997
26998     /**
26999      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27000      */
27001     endUpdate : function(){
27002         this.updating = false;
27003         this.autoSizeTabs();
27004     },
27005
27006     /**
27007      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27008      */
27009     autoSizeTabs : function(){
27010         var count = this.items.length;
27011         var vcount = count - this.hiddenCount;
27012         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27013         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27014         var availWidth = Math.floor(w / vcount);
27015         var b = this.stripBody;
27016         if(b.getWidth() > w){
27017             var tabs = this.items;
27018             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27019             if(availWidth < this.minTabWidth){
27020                 /*if(!this.sleft){    // incomplete scrolling code
27021                     this.createScrollButtons();
27022                 }
27023                 this.showScroll();
27024                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27025             }
27026         }else{
27027             if(this.currentTabWidth < this.preferredTabWidth){
27028                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27029             }
27030         }
27031     },
27032
27033     /**
27034      * Returns the number of tabs in this TabPanel.
27035      * @return {Number}
27036      */
27037      getCount : function(){
27038          return this.items.length;
27039      },
27040
27041     /**
27042      * Resizes all the tabs to the passed width
27043      * @param {Number} The new width
27044      */
27045     setTabWidth : function(width){
27046         this.currentTabWidth = width;
27047         for(var i = 0, len = this.items.length; i < len; i++) {
27048                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27049         }
27050     },
27051
27052     /**
27053      * Destroys this TabPanel
27054      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27055      */
27056     destroy : function(removeEl){
27057         Roo.EventManager.removeResizeListener(this.onResize, this);
27058         for(var i = 0, len = this.items.length; i < len; i++){
27059             this.items[i].purgeListeners();
27060         }
27061         if(removeEl === true){
27062             this.el.update("");
27063             this.el.remove();
27064         }
27065     }
27066 });
27067
27068 /**
27069  * @class Roo.TabPanelItem
27070  * @extends Roo.util.Observable
27071  * Represents an individual item (tab plus body) in a TabPanel.
27072  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27073  * @param {String} id The id of this TabPanelItem
27074  * @param {String} text The text for the tab of this TabPanelItem
27075  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27076  */
27077 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27078     /**
27079      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27080      * @type Roo.TabPanel
27081      */
27082     this.tabPanel = tabPanel;
27083     /**
27084      * The id for this TabPanelItem
27085      * @type String
27086      */
27087     this.id = id;
27088     /** @private */
27089     this.disabled = false;
27090     /** @private */
27091     this.text = text;
27092     /** @private */
27093     this.loaded = false;
27094     this.closable = closable;
27095
27096     /**
27097      * The body element for this TabPanelItem.
27098      * @type Roo.Element
27099      */
27100     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27101     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27102     this.bodyEl.setStyle("display", "block");
27103     this.bodyEl.setStyle("zoom", "1");
27104     this.hideAction();
27105
27106     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27107     /** @private */
27108     this.el = Roo.get(els.el, true);
27109     this.inner = Roo.get(els.inner, true);
27110     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27111     this.pnode = Roo.get(els.el.parentNode, true);
27112     this.el.on("mousedown", this.onTabMouseDown, this);
27113     this.el.on("click", this.onTabClick, this);
27114     /** @private */
27115     if(closable){
27116         var c = Roo.get(els.close, true);
27117         c.dom.title = this.closeText;
27118         c.addClassOnOver("close-over");
27119         c.on("click", this.closeClick, this);
27120      }
27121
27122     this.addEvents({
27123          /**
27124          * @event activate
27125          * Fires when this tab becomes the active tab.
27126          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27127          * @param {Roo.TabPanelItem} this
27128          */
27129         "activate": true,
27130         /**
27131          * @event beforeclose
27132          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27133          * @param {Roo.TabPanelItem} this
27134          * @param {Object} e Set cancel to true on this object to cancel the close.
27135          */
27136         "beforeclose": true,
27137         /**
27138          * @event close
27139          * Fires when this tab is closed.
27140          * @param {Roo.TabPanelItem} this
27141          */
27142          "close": true,
27143         /**
27144          * @event deactivate
27145          * Fires when this tab is no longer the active tab.
27146          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27147          * @param {Roo.TabPanelItem} this
27148          */
27149          "deactivate" : true
27150     });
27151     this.hidden = false;
27152
27153     Roo.TabPanelItem.superclass.constructor.call(this);
27154 };
27155
27156 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27157     purgeListeners : function(){
27158        Roo.util.Observable.prototype.purgeListeners.call(this);
27159        this.el.removeAllListeners();
27160     },
27161     /**
27162      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27163      */
27164     show : function(){
27165         this.pnode.addClass("on");
27166         this.showAction();
27167         if(Roo.isOpera){
27168             this.tabPanel.stripWrap.repaint();
27169         }
27170         this.fireEvent("activate", this.tabPanel, this);
27171     },
27172
27173     /**
27174      * Returns true if this tab is the active tab.
27175      * @return {Boolean}
27176      */
27177     isActive : function(){
27178         return this.tabPanel.getActiveTab() == this;
27179     },
27180
27181     /**
27182      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27183      */
27184     hide : function(){
27185         this.pnode.removeClass("on");
27186         this.hideAction();
27187         this.fireEvent("deactivate", this.tabPanel, this);
27188     },
27189
27190     hideAction : function(){
27191         this.bodyEl.hide();
27192         this.bodyEl.setStyle("position", "absolute");
27193         this.bodyEl.setLeft("-20000px");
27194         this.bodyEl.setTop("-20000px");
27195     },
27196
27197     showAction : function(){
27198         this.bodyEl.setStyle("position", "relative");
27199         this.bodyEl.setTop("");
27200         this.bodyEl.setLeft("");
27201         this.bodyEl.show();
27202     },
27203
27204     /**
27205      * Set the tooltip for the tab.
27206      * @param {String} tooltip The tab's tooltip
27207      */
27208     setTooltip : function(text){
27209         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27210             this.textEl.dom.qtip = text;
27211             this.textEl.dom.removeAttribute('title');
27212         }else{
27213             this.textEl.dom.title = text;
27214         }
27215     },
27216
27217     onTabClick : function(e){
27218         e.preventDefault();
27219         this.tabPanel.activate(this.id);
27220     },
27221
27222     onTabMouseDown : function(e){
27223         e.preventDefault();
27224         this.tabPanel.activate(this.id);
27225     },
27226
27227     getWidth : function(){
27228         return this.inner.getWidth();
27229     },
27230
27231     setWidth : function(width){
27232         var iwidth = width - this.pnode.getPadding("lr");
27233         this.inner.setWidth(iwidth);
27234         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27235         this.pnode.setWidth(width);
27236     },
27237
27238     /**
27239      * Show or hide the tab
27240      * @param {Boolean} hidden True to hide or false to show.
27241      */
27242     setHidden : function(hidden){
27243         this.hidden = hidden;
27244         this.pnode.setStyle("display", hidden ? "none" : "");
27245     },
27246
27247     /**
27248      * Returns true if this tab is "hidden"
27249      * @return {Boolean}
27250      */
27251     isHidden : function(){
27252         return this.hidden;
27253     },
27254
27255     /**
27256      * Returns the text for this tab
27257      * @return {String}
27258      */
27259     getText : function(){
27260         return this.text;
27261     },
27262
27263     autoSize : function(){
27264         //this.el.beginMeasure();
27265         this.textEl.setWidth(1);
27266         /*
27267          *  #2804 [new] Tabs in Roojs
27268          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27269          */
27270         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27271         //this.el.endMeasure();
27272     },
27273
27274     /**
27275      * Sets the text for the tab (Note: this also sets the tooltip text)
27276      * @param {String} text The tab's text and tooltip
27277      */
27278     setText : function(text){
27279         this.text = text;
27280         this.textEl.update(text);
27281         this.setTooltip(text);
27282         if(!this.tabPanel.resizeTabs){
27283             this.autoSize();
27284         }
27285     },
27286     /**
27287      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27288      */
27289     activate : function(){
27290         this.tabPanel.activate(this.id);
27291     },
27292
27293     /**
27294      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27295      */
27296     disable : function(){
27297         if(this.tabPanel.active != this){
27298             this.disabled = true;
27299             this.pnode.addClass("disabled");
27300         }
27301     },
27302
27303     /**
27304      * Enables this TabPanelItem if it was previously disabled.
27305      */
27306     enable : function(){
27307         this.disabled = false;
27308         this.pnode.removeClass("disabled");
27309     },
27310
27311     /**
27312      * Sets the content for this TabPanelItem.
27313      * @param {String} content The content
27314      * @param {Boolean} loadScripts true to look for and load scripts
27315      */
27316     setContent : function(content, loadScripts){
27317         this.bodyEl.update(content, loadScripts);
27318     },
27319
27320     /**
27321      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27322      * @return {Roo.UpdateManager} The UpdateManager
27323      */
27324     getUpdateManager : function(){
27325         return this.bodyEl.getUpdateManager();
27326     },
27327
27328     /**
27329      * Set a URL to be used to load the content for this TabPanelItem.
27330      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27331      * @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)
27332      * @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)
27333      * @return {Roo.UpdateManager} The UpdateManager
27334      */
27335     setUrl : function(url, params, loadOnce){
27336         if(this.refreshDelegate){
27337             this.un('activate', this.refreshDelegate);
27338         }
27339         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27340         this.on("activate", this.refreshDelegate);
27341         return this.bodyEl.getUpdateManager();
27342     },
27343
27344     /** @private */
27345     _handleRefresh : function(url, params, loadOnce){
27346         if(!loadOnce || !this.loaded){
27347             var updater = this.bodyEl.getUpdateManager();
27348             updater.update(url, params, this._setLoaded.createDelegate(this));
27349         }
27350     },
27351
27352     /**
27353      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27354      *   Will fail silently if the setUrl method has not been called.
27355      *   This does not activate the panel, just updates its content.
27356      */
27357     refresh : function(){
27358         if(this.refreshDelegate){
27359            this.loaded = false;
27360            this.refreshDelegate();
27361         }
27362     },
27363
27364     /** @private */
27365     _setLoaded : function(){
27366         this.loaded = true;
27367     },
27368
27369     /** @private */
27370     closeClick : function(e){
27371         var o = {};
27372         e.stopEvent();
27373         this.fireEvent("beforeclose", this, o);
27374         if(o.cancel !== true){
27375             this.tabPanel.removeTab(this.id);
27376         }
27377     },
27378     /**
27379      * The text displayed in the tooltip for the close icon.
27380      * @type String
27381      */
27382     closeText : "Close this tab"
27383 });
27384
27385 /** @private */
27386 Roo.TabPanel.prototype.createStrip = function(container){
27387     var strip = document.createElement("div");
27388     strip.className = "x-tabs-wrap";
27389     container.appendChild(strip);
27390     return strip;
27391 };
27392 /** @private */
27393 Roo.TabPanel.prototype.createStripList = function(strip){
27394     // div wrapper for retard IE
27395     // returns the "tr" element.
27396     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27397         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27398         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27399     return strip.firstChild.firstChild.firstChild.firstChild;
27400 };
27401 /** @private */
27402 Roo.TabPanel.prototype.createBody = function(container){
27403     var body = document.createElement("div");
27404     Roo.id(body, "tab-body");
27405     Roo.fly(body).addClass("x-tabs-body");
27406     container.appendChild(body);
27407     return body;
27408 };
27409 /** @private */
27410 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27411     var body = Roo.getDom(id);
27412     if(!body){
27413         body = document.createElement("div");
27414         body.id = id;
27415     }
27416     Roo.fly(body).addClass("x-tabs-item-body");
27417     bodyEl.insertBefore(body, bodyEl.firstChild);
27418     return body;
27419 };
27420 /** @private */
27421 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27422     var td = document.createElement("td");
27423     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27424     //stripEl.appendChild(td);
27425     if(closable){
27426         td.className = "x-tabs-closable";
27427         if(!this.closeTpl){
27428             this.closeTpl = new Roo.Template(
27429                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27430                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27431                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27432             );
27433         }
27434         var el = this.closeTpl.overwrite(td, {"text": text});
27435         var close = el.getElementsByTagName("div")[0];
27436         var inner = el.getElementsByTagName("em")[0];
27437         return {"el": el, "close": close, "inner": inner};
27438     } else {
27439         if(!this.tabTpl){
27440             this.tabTpl = new Roo.Template(
27441                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27442                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27443             );
27444         }
27445         var el = this.tabTpl.overwrite(td, {"text": text});
27446         var inner = el.getElementsByTagName("em")[0];
27447         return {"el": el, "inner": inner};
27448     }
27449 };/*
27450  * Based on:
27451  * Ext JS Library 1.1.1
27452  * Copyright(c) 2006-2007, Ext JS, LLC.
27453  *
27454  * Originally Released Under LGPL - original licence link has changed is not relivant.
27455  *
27456  * Fork - LGPL
27457  * <script type="text/javascript">
27458  */
27459
27460 /**
27461  * @class Roo.Button
27462  * @extends Roo.util.Observable
27463  * Simple Button class
27464  * @cfg {String} text The button text
27465  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27466  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27467  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27468  * @cfg {Object} scope The scope of the handler
27469  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27470  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27471  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27472  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27473  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27474  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27475    applies if enableToggle = true)
27476  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27477  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27478   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27479  * @constructor
27480  * Create a new button
27481  * @param {Object} config The config object
27482  */
27483 Roo.Button = function(renderTo, config)
27484 {
27485     if (!config) {
27486         config = renderTo;
27487         renderTo = config.renderTo || false;
27488     }
27489     
27490     Roo.apply(this, config);
27491     this.addEvents({
27492         /**
27493              * @event click
27494              * Fires when this button is clicked
27495              * @param {Button} this
27496              * @param {EventObject} e The click event
27497              */
27498             "click" : true,
27499         /**
27500              * @event toggle
27501              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27502              * @param {Button} this
27503              * @param {Boolean} pressed
27504              */
27505             "toggle" : true,
27506         /**
27507              * @event mouseover
27508              * Fires when the mouse hovers over the button
27509              * @param {Button} this
27510              * @param {Event} e The event object
27511              */
27512         'mouseover' : true,
27513         /**
27514              * @event mouseout
27515              * Fires when the mouse exits the button
27516              * @param {Button} this
27517              * @param {Event} e The event object
27518              */
27519         'mouseout': true,
27520          /**
27521              * @event render
27522              * Fires when the button is rendered
27523              * @param {Button} this
27524              */
27525         'render': true
27526     });
27527     if(this.menu){
27528         this.menu = Roo.menu.MenuMgr.get(this.menu);
27529     }
27530     // register listeners first!!  - so render can be captured..
27531     Roo.util.Observable.call(this);
27532     if(renderTo){
27533         this.render(renderTo);
27534     }
27535     
27536   
27537 };
27538
27539 Roo.extend(Roo.Button, Roo.util.Observable, {
27540     /**
27541      * 
27542      */
27543     
27544     /**
27545      * Read-only. True if this button is hidden
27546      * @type Boolean
27547      */
27548     hidden : false,
27549     /**
27550      * Read-only. True if this button is disabled
27551      * @type Boolean
27552      */
27553     disabled : false,
27554     /**
27555      * Read-only. True if this button is pressed (only if enableToggle = true)
27556      * @type Boolean
27557      */
27558     pressed : false,
27559
27560     /**
27561      * @cfg {Number} tabIndex 
27562      * The DOM tabIndex for this button (defaults to undefined)
27563      */
27564     tabIndex : undefined,
27565
27566     /**
27567      * @cfg {Boolean} enableToggle
27568      * True to enable pressed/not pressed toggling (defaults to false)
27569      */
27570     enableToggle: false,
27571     /**
27572      * @cfg {Mixed} menu
27573      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27574      */
27575     menu : undefined,
27576     /**
27577      * @cfg {String} menuAlign
27578      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27579      */
27580     menuAlign : "tl-bl?",
27581
27582     /**
27583      * @cfg {String} iconCls
27584      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27585      */
27586     iconCls : undefined,
27587     /**
27588      * @cfg {String} type
27589      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27590      */
27591     type : 'button',
27592
27593     // private
27594     menuClassTarget: 'tr',
27595
27596     /**
27597      * @cfg {String} clickEvent
27598      * The type of event to map to the button's event handler (defaults to 'click')
27599      */
27600     clickEvent : 'click',
27601
27602     /**
27603      * @cfg {Boolean} handleMouseEvents
27604      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27605      */
27606     handleMouseEvents : true,
27607
27608     /**
27609      * @cfg {String} tooltipType
27610      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27611      */
27612     tooltipType : 'qtip',
27613
27614     /**
27615      * @cfg {String} cls
27616      * A CSS class to apply to the button's main element.
27617      */
27618     
27619     /**
27620      * @cfg {Roo.Template} template (Optional)
27621      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27622      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27623      * require code modifications if required elements (e.g. a button) aren't present.
27624      */
27625
27626     // private
27627     render : function(renderTo){
27628         var btn;
27629         if(this.hideParent){
27630             this.parentEl = Roo.get(renderTo);
27631         }
27632         if(!this.dhconfig){
27633             if(!this.template){
27634                 if(!Roo.Button.buttonTemplate){
27635                     // hideous table template
27636                     Roo.Button.buttonTemplate = new Roo.Template(
27637                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27638                         '<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>',
27639                         "</tr></tbody></table>");
27640                 }
27641                 this.template = Roo.Button.buttonTemplate;
27642             }
27643             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27644             var btnEl = btn.child("button:first");
27645             btnEl.on('focus', this.onFocus, this);
27646             btnEl.on('blur', this.onBlur, this);
27647             if(this.cls){
27648                 btn.addClass(this.cls);
27649             }
27650             if(this.icon){
27651                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27652             }
27653             if(this.iconCls){
27654                 btnEl.addClass(this.iconCls);
27655                 if(!this.cls){
27656                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27657                 }
27658             }
27659             if(this.tabIndex !== undefined){
27660                 btnEl.dom.tabIndex = this.tabIndex;
27661             }
27662             if(this.tooltip){
27663                 if(typeof this.tooltip == 'object'){
27664                     Roo.QuickTips.tips(Roo.apply({
27665                           target: btnEl.id
27666                     }, this.tooltip));
27667                 } else {
27668                     btnEl.dom[this.tooltipType] = this.tooltip;
27669                 }
27670             }
27671         }else{
27672             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27673         }
27674         this.el = btn;
27675         if(this.id){
27676             this.el.dom.id = this.el.id = this.id;
27677         }
27678         if(this.menu){
27679             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27680             this.menu.on("show", this.onMenuShow, this);
27681             this.menu.on("hide", this.onMenuHide, this);
27682         }
27683         btn.addClass("x-btn");
27684         if(Roo.isIE && !Roo.isIE7){
27685             this.autoWidth.defer(1, this);
27686         }else{
27687             this.autoWidth();
27688         }
27689         if(this.handleMouseEvents){
27690             btn.on("mouseover", this.onMouseOver, this);
27691             btn.on("mouseout", this.onMouseOut, this);
27692             btn.on("mousedown", this.onMouseDown, this);
27693         }
27694         btn.on(this.clickEvent, this.onClick, this);
27695         //btn.on("mouseup", this.onMouseUp, this);
27696         if(this.hidden){
27697             this.hide();
27698         }
27699         if(this.disabled){
27700             this.disable();
27701         }
27702         Roo.ButtonToggleMgr.register(this);
27703         if(this.pressed){
27704             this.el.addClass("x-btn-pressed");
27705         }
27706         if(this.repeat){
27707             var repeater = new Roo.util.ClickRepeater(btn,
27708                 typeof this.repeat == "object" ? this.repeat : {}
27709             );
27710             repeater.on("click", this.onClick,  this);
27711         }
27712         
27713         this.fireEvent('render', this);
27714         
27715     },
27716     /**
27717      * Returns the button's underlying element
27718      * @return {Roo.Element} The element
27719      */
27720     getEl : function(){
27721         return this.el;  
27722     },
27723     
27724     /**
27725      * Destroys this Button and removes any listeners.
27726      */
27727     destroy : function(){
27728         Roo.ButtonToggleMgr.unregister(this);
27729         this.el.removeAllListeners();
27730         this.purgeListeners();
27731         this.el.remove();
27732     },
27733
27734     // private
27735     autoWidth : function(){
27736         if(this.el){
27737             this.el.setWidth("auto");
27738             if(Roo.isIE7 && Roo.isStrict){
27739                 var ib = this.el.child('button');
27740                 if(ib && ib.getWidth() > 20){
27741                     ib.clip();
27742                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27743                 }
27744             }
27745             if(this.minWidth){
27746                 if(this.hidden){
27747                     this.el.beginMeasure();
27748                 }
27749                 if(this.el.getWidth() < this.minWidth){
27750                     this.el.setWidth(this.minWidth);
27751                 }
27752                 if(this.hidden){
27753                     this.el.endMeasure();
27754                 }
27755             }
27756         }
27757     },
27758
27759     /**
27760      * Assigns this button's click handler
27761      * @param {Function} handler The function to call when the button is clicked
27762      * @param {Object} scope (optional) Scope for the function passed in
27763      */
27764     setHandler : function(handler, scope){
27765         this.handler = handler;
27766         this.scope = scope;  
27767     },
27768     
27769     /**
27770      * Sets this button's text
27771      * @param {String} text The button text
27772      */
27773     setText : function(text){
27774         this.text = text;
27775         if(this.el){
27776             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27777         }
27778         this.autoWidth();
27779     },
27780     
27781     /**
27782      * Gets the text for this button
27783      * @return {String} The button text
27784      */
27785     getText : function(){
27786         return this.text;  
27787     },
27788     
27789     /**
27790      * Show this button
27791      */
27792     show: function(){
27793         this.hidden = false;
27794         if(this.el){
27795             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27796         }
27797     },
27798     
27799     /**
27800      * Hide this button
27801      */
27802     hide: function(){
27803         this.hidden = true;
27804         if(this.el){
27805             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27806         }
27807     },
27808     
27809     /**
27810      * Convenience function for boolean show/hide
27811      * @param {Boolean} visible True to show, false to hide
27812      */
27813     setVisible: function(visible){
27814         if(visible) {
27815             this.show();
27816         }else{
27817             this.hide();
27818         }
27819     },
27820     
27821     /**
27822      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27823      * @param {Boolean} state (optional) Force a particular state
27824      */
27825     toggle : function(state){
27826         state = state === undefined ? !this.pressed : state;
27827         if(state != this.pressed){
27828             if(state){
27829                 this.el.addClass("x-btn-pressed");
27830                 this.pressed = true;
27831                 this.fireEvent("toggle", this, true);
27832             }else{
27833                 this.el.removeClass("x-btn-pressed");
27834                 this.pressed = false;
27835                 this.fireEvent("toggle", this, false);
27836             }
27837             if(this.toggleHandler){
27838                 this.toggleHandler.call(this.scope || this, this, state);
27839             }
27840         }
27841     },
27842     
27843     /**
27844      * Focus the button
27845      */
27846     focus : function(){
27847         this.el.child('button:first').focus();
27848     },
27849     
27850     /**
27851      * Disable this button
27852      */
27853     disable : function(){
27854         if(this.el){
27855             this.el.addClass("x-btn-disabled");
27856         }
27857         this.disabled = true;
27858     },
27859     
27860     /**
27861      * Enable this button
27862      */
27863     enable : function(){
27864         if(this.el){
27865             this.el.removeClass("x-btn-disabled");
27866         }
27867         this.disabled = false;
27868     },
27869
27870     /**
27871      * Convenience function for boolean enable/disable
27872      * @param {Boolean} enabled True to enable, false to disable
27873      */
27874     setDisabled : function(v){
27875         this[v !== true ? "enable" : "disable"]();
27876     },
27877
27878     // private
27879     onClick : function(e)
27880     {
27881         if(e){
27882             e.preventDefault();
27883         }
27884         if(e.button != 0){
27885             return;
27886         }
27887         if(!this.disabled){
27888             if(this.enableToggle){
27889                 this.toggle();
27890             }
27891             if(this.menu && !this.menu.isVisible()){
27892                 this.menu.show(this.el, this.menuAlign);
27893             }
27894             this.fireEvent("click", this, e);
27895             if(this.handler){
27896                 this.el.removeClass("x-btn-over");
27897                 this.handler.call(this.scope || this, this, e);
27898             }
27899         }
27900     },
27901     // private
27902     onMouseOver : function(e){
27903         if(!this.disabled){
27904             this.el.addClass("x-btn-over");
27905             this.fireEvent('mouseover', this, e);
27906         }
27907     },
27908     // private
27909     onMouseOut : function(e){
27910         if(!e.within(this.el,  true)){
27911             this.el.removeClass("x-btn-over");
27912             this.fireEvent('mouseout', this, e);
27913         }
27914     },
27915     // private
27916     onFocus : function(e){
27917         if(!this.disabled){
27918             this.el.addClass("x-btn-focus");
27919         }
27920     },
27921     // private
27922     onBlur : function(e){
27923         this.el.removeClass("x-btn-focus");
27924     },
27925     // private
27926     onMouseDown : function(e){
27927         if(!this.disabled && e.button == 0){
27928             this.el.addClass("x-btn-click");
27929             Roo.get(document).on('mouseup', this.onMouseUp, this);
27930         }
27931     },
27932     // private
27933     onMouseUp : function(e){
27934         if(e.button == 0){
27935             this.el.removeClass("x-btn-click");
27936             Roo.get(document).un('mouseup', this.onMouseUp, this);
27937         }
27938     },
27939     // private
27940     onMenuShow : function(e){
27941         this.el.addClass("x-btn-menu-active");
27942     },
27943     // private
27944     onMenuHide : function(e){
27945         this.el.removeClass("x-btn-menu-active");
27946     }   
27947 });
27948
27949 // Private utility class used by Button
27950 Roo.ButtonToggleMgr = function(){
27951    var groups = {};
27952    
27953    function toggleGroup(btn, state){
27954        if(state){
27955            var g = groups[btn.toggleGroup];
27956            for(var i = 0, l = g.length; i < l; i++){
27957                if(g[i] != btn){
27958                    g[i].toggle(false);
27959                }
27960            }
27961        }
27962    }
27963    
27964    return {
27965        register : function(btn){
27966            if(!btn.toggleGroup){
27967                return;
27968            }
27969            var g = groups[btn.toggleGroup];
27970            if(!g){
27971                g = groups[btn.toggleGroup] = [];
27972            }
27973            g.push(btn);
27974            btn.on("toggle", toggleGroup);
27975        },
27976        
27977        unregister : function(btn){
27978            if(!btn.toggleGroup){
27979                return;
27980            }
27981            var g = groups[btn.toggleGroup];
27982            if(g){
27983                g.remove(btn);
27984                btn.un("toggle", toggleGroup);
27985            }
27986        }
27987    };
27988 }();/*
27989  * Based on:
27990  * Ext JS Library 1.1.1
27991  * Copyright(c) 2006-2007, Ext JS, LLC.
27992  *
27993  * Originally Released Under LGPL - original licence link has changed is not relivant.
27994  *
27995  * Fork - LGPL
27996  * <script type="text/javascript">
27997  */
27998  
27999 /**
28000  * @class Roo.SplitButton
28001  * @extends Roo.Button
28002  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28003  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28004  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28005  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28006  * @cfg {String} arrowTooltip The title attribute of the arrow
28007  * @constructor
28008  * Create a new menu button
28009  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28010  * @param {Object} config The config object
28011  */
28012 Roo.SplitButton = function(renderTo, config){
28013     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28014     /**
28015      * @event arrowclick
28016      * Fires when this button's arrow is clicked
28017      * @param {SplitButton} this
28018      * @param {EventObject} e The click event
28019      */
28020     this.addEvents({"arrowclick":true});
28021 };
28022
28023 Roo.extend(Roo.SplitButton, Roo.Button, {
28024     render : function(renderTo){
28025         // this is one sweet looking template!
28026         var tpl = new Roo.Template(
28027             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28028             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28029             '<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>',
28030             "</tbody></table></td><td>",
28031             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28032             '<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>',
28033             "</tbody></table></td></tr></table>"
28034         );
28035         var btn = tpl.append(renderTo, [this.text, this.type], true);
28036         var btnEl = btn.child("button");
28037         if(this.cls){
28038             btn.addClass(this.cls);
28039         }
28040         if(this.icon){
28041             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28042         }
28043         if(this.iconCls){
28044             btnEl.addClass(this.iconCls);
28045             if(!this.cls){
28046                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28047             }
28048         }
28049         this.el = btn;
28050         if(this.handleMouseEvents){
28051             btn.on("mouseover", this.onMouseOver, this);
28052             btn.on("mouseout", this.onMouseOut, this);
28053             btn.on("mousedown", this.onMouseDown, this);
28054             btn.on("mouseup", this.onMouseUp, this);
28055         }
28056         btn.on(this.clickEvent, this.onClick, this);
28057         if(this.tooltip){
28058             if(typeof this.tooltip == 'object'){
28059                 Roo.QuickTips.tips(Roo.apply({
28060                       target: btnEl.id
28061                 }, this.tooltip));
28062             } else {
28063                 btnEl.dom[this.tooltipType] = this.tooltip;
28064             }
28065         }
28066         if(this.arrowTooltip){
28067             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28068         }
28069         if(this.hidden){
28070             this.hide();
28071         }
28072         if(this.disabled){
28073             this.disable();
28074         }
28075         if(this.pressed){
28076             this.el.addClass("x-btn-pressed");
28077         }
28078         if(Roo.isIE && !Roo.isIE7){
28079             this.autoWidth.defer(1, this);
28080         }else{
28081             this.autoWidth();
28082         }
28083         if(this.menu){
28084             this.menu.on("show", this.onMenuShow, this);
28085             this.menu.on("hide", this.onMenuHide, this);
28086         }
28087         this.fireEvent('render', this);
28088     },
28089
28090     // private
28091     autoWidth : function(){
28092         if(this.el){
28093             var tbl = this.el.child("table:first");
28094             var tbl2 = this.el.child("table:last");
28095             this.el.setWidth("auto");
28096             tbl.setWidth("auto");
28097             if(Roo.isIE7 && Roo.isStrict){
28098                 var ib = this.el.child('button:first');
28099                 if(ib && ib.getWidth() > 20){
28100                     ib.clip();
28101                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28102                 }
28103             }
28104             if(this.minWidth){
28105                 if(this.hidden){
28106                     this.el.beginMeasure();
28107                 }
28108                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28109                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28110                 }
28111                 if(this.hidden){
28112                     this.el.endMeasure();
28113                 }
28114             }
28115             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28116         } 
28117     },
28118     /**
28119      * Sets this button's click handler
28120      * @param {Function} handler The function to call when the button is clicked
28121      * @param {Object} scope (optional) Scope for the function passed above
28122      */
28123     setHandler : function(handler, scope){
28124         this.handler = handler;
28125         this.scope = scope;  
28126     },
28127     
28128     /**
28129      * Sets this button's arrow click handler
28130      * @param {Function} handler The function to call when the arrow is clicked
28131      * @param {Object} scope (optional) Scope for the function passed above
28132      */
28133     setArrowHandler : function(handler, scope){
28134         this.arrowHandler = handler;
28135         this.scope = scope;  
28136     },
28137     
28138     /**
28139      * Focus the button
28140      */
28141     focus : function(){
28142         if(this.el){
28143             this.el.child("button:first").focus();
28144         }
28145     },
28146
28147     // private
28148     onClick : function(e){
28149         e.preventDefault();
28150         if(!this.disabled){
28151             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28152                 if(this.menu && !this.menu.isVisible()){
28153                     this.menu.show(this.el, this.menuAlign);
28154                 }
28155                 this.fireEvent("arrowclick", this, e);
28156                 if(this.arrowHandler){
28157                     this.arrowHandler.call(this.scope || this, this, e);
28158                 }
28159             }else{
28160                 this.fireEvent("click", this, e);
28161                 if(this.handler){
28162                     this.handler.call(this.scope || this, this, e);
28163                 }
28164             }
28165         }
28166     },
28167     // private
28168     onMouseDown : function(e){
28169         if(!this.disabled){
28170             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28171         }
28172     },
28173     // private
28174     onMouseUp : function(e){
28175         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28176     }   
28177 });
28178
28179
28180 // backwards compat
28181 Roo.MenuButton = Roo.SplitButton;/*
28182  * Based on:
28183  * Ext JS Library 1.1.1
28184  * Copyright(c) 2006-2007, Ext JS, LLC.
28185  *
28186  * Originally Released Under LGPL - original licence link has changed is not relivant.
28187  *
28188  * Fork - LGPL
28189  * <script type="text/javascript">
28190  */
28191
28192 /**
28193  * @class Roo.Toolbar
28194  * Basic Toolbar class.
28195  * @constructor
28196  * Creates a new Toolbar
28197  * @param {Object} container The config object
28198  */ 
28199 Roo.Toolbar = function(container, buttons, config)
28200 {
28201     /// old consturctor format still supported..
28202     if(container instanceof Array){ // omit the container for later rendering
28203         buttons = container;
28204         config = buttons;
28205         container = null;
28206     }
28207     if (typeof(container) == 'object' && container.xtype) {
28208         config = container;
28209         container = config.container;
28210         buttons = config.buttons || []; // not really - use items!!
28211     }
28212     var xitems = [];
28213     if (config && config.items) {
28214         xitems = config.items;
28215         delete config.items;
28216     }
28217     Roo.apply(this, config);
28218     this.buttons = buttons;
28219     
28220     if(container){
28221         this.render(container);
28222     }
28223     this.xitems = xitems;
28224     Roo.each(xitems, function(b) {
28225         this.add(b);
28226     }, this);
28227     
28228 };
28229
28230 Roo.Toolbar.prototype = {
28231     /**
28232      * @cfg {Array} items
28233      * array of button configs or elements to add (will be converted to a MixedCollection)
28234      */
28235     
28236     /**
28237      * @cfg {String/HTMLElement/Element} container
28238      * The id or element that will contain the toolbar
28239      */
28240     // private
28241     render : function(ct){
28242         this.el = Roo.get(ct);
28243         if(this.cls){
28244             this.el.addClass(this.cls);
28245         }
28246         // using a table allows for vertical alignment
28247         // 100% width is needed by Safari...
28248         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28249         this.tr = this.el.child("tr", true);
28250         var autoId = 0;
28251         this.items = new Roo.util.MixedCollection(false, function(o){
28252             return o.id || ("item" + (++autoId));
28253         });
28254         if(this.buttons){
28255             this.add.apply(this, this.buttons);
28256             delete this.buttons;
28257         }
28258     },
28259
28260     /**
28261      * Adds element(s) to the toolbar -- this function takes a variable number of 
28262      * arguments of mixed type and adds them to the toolbar.
28263      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28264      * <ul>
28265      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28266      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28267      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28268      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28269      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28270      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28271      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28272      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28273      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28274      * </ul>
28275      * @param {Mixed} arg2
28276      * @param {Mixed} etc.
28277      */
28278     add : function(){
28279         var a = arguments, l = a.length;
28280         for(var i = 0; i < l; i++){
28281             this._add(a[i]);
28282         }
28283     },
28284     // private..
28285     _add : function(el) {
28286         
28287         if (el.xtype) {
28288             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28289         }
28290         
28291         if (el.applyTo){ // some kind of form field
28292             return this.addField(el);
28293         } 
28294         if (el.render){ // some kind of Toolbar.Item
28295             return this.addItem(el);
28296         }
28297         if (typeof el == "string"){ // string
28298             if(el == "separator" || el == "-"){
28299                 return this.addSeparator();
28300             }
28301             if (el == " "){
28302                 return this.addSpacer();
28303             }
28304             if(el == "->"){
28305                 return this.addFill();
28306             }
28307             return this.addText(el);
28308             
28309         }
28310         if(el.tagName){ // element
28311             return this.addElement(el);
28312         }
28313         if(typeof el == "object"){ // must be button config?
28314             return this.addButton(el);
28315         }
28316         // and now what?!?!
28317         return false;
28318         
28319     },
28320     
28321     /**
28322      * Add an Xtype element
28323      * @param {Object} xtype Xtype Object
28324      * @return {Object} created Object
28325      */
28326     addxtype : function(e){
28327         return this.add(e);  
28328     },
28329     
28330     /**
28331      * Returns the Element for this toolbar.
28332      * @return {Roo.Element}
28333      */
28334     getEl : function(){
28335         return this.el;  
28336     },
28337     
28338     /**
28339      * Adds a separator
28340      * @return {Roo.Toolbar.Item} The separator item
28341      */
28342     addSeparator : function(){
28343         return this.addItem(new Roo.Toolbar.Separator());
28344     },
28345
28346     /**
28347      * Adds a spacer element
28348      * @return {Roo.Toolbar.Spacer} The spacer item
28349      */
28350     addSpacer : function(){
28351         return this.addItem(new Roo.Toolbar.Spacer());
28352     },
28353
28354     /**
28355      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28356      * @return {Roo.Toolbar.Fill} The fill item
28357      */
28358     addFill : function(){
28359         return this.addItem(new Roo.Toolbar.Fill());
28360     },
28361
28362     /**
28363      * Adds any standard HTML element to the toolbar
28364      * @param {String/HTMLElement/Element} el The element or id of the element to add
28365      * @return {Roo.Toolbar.Item} The element's item
28366      */
28367     addElement : function(el){
28368         return this.addItem(new Roo.Toolbar.Item(el));
28369     },
28370     /**
28371      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28372      * @type Roo.util.MixedCollection  
28373      */
28374     items : false,
28375      
28376     /**
28377      * Adds any Toolbar.Item or subclass
28378      * @param {Roo.Toolbar.Item} item
28379      * @return {Roo.Toolbar.Item} The item
28380      */
28381     addItem : function(item){
28382         var td = this.nextBlock();
28383         item.render(td);
28384         this.items.add(item);
28385         return item;
28386     },
28387     
28388     /**
28389      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28390      * @param {Object/Array} config A button config or array of configs
28391      * @return {Roo.Toolbar.Button/Array}
28392      */
28393     addButton : function(config){
28394         if(config instanceof Array){
28395             var buttons = [];
28396             for(var i = 0, len = config.length; i < len; i++) {
28397                 buttons.push(this.addButton(config[i]));
28398             }
28399             return buttons;
28400         }
28401         var b = config;
28402         if(!(config instanceof Roo.Toolbar.Button)){
28403             b = config.split ?
28404                 new Roo.Toolbar.SplitButton(config) :
28405                 new Roo.Toolbar.Button(config);
28406         }
28407         var td = this.nextBlock();
28408         b.render(td);
28409         this.items.add(b);
28410         return b;
28411     },
28412     
28413     /**
28414      * Adds text to the toolbar
28415      * @param {String} text The text to add
28416      * @return {Roo.Toolbar.Item} The element's item
28417      */
28418     addText : function(text){
28419         return this.addItem(new Roo.Toolbar.TextItem(text));
28420     },
28421     
28422     /**
28423      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28424      * @param {Number} index The index where the item is to be inserted
28425      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28426      * @return {Roo.Toolbar.Button/Item}
28427      */
28428     insertButton : function(index, item){
28429         if(item instanceof Array){
28430             var buttons = [];
28431             for(var i = 0, len = item.length; i < len; i++) {
28432                buttons.push(this.insertButton(index + i, item[i]));
28433             }
28434             return buttons;
28435         }
28436         if (!(item instanceof Roo.Toolbar.Button)){
28437            item = new Roo.Toolbar.Button(item);
28438         }
28439         var td = document.createElement("td");
28440         this.tr.insertBefore(td, this.tr.childNodes[index]);
28441         item.render(td);
28442         this.items.insert(index, item);
28443         return item;
28444     },
28445     
28446     /**
28447      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28448      * @param {Object} config
28449      * @return {Roo.Toolbar.Item} The element's item
28450      */
28451     addDom : function(config, returnEl){
28452         var td = this.nextBlock();
28453         Roo.DomHelper.overwrite(td, config);
28454         var ti = new Roo.Toolbar.Item(td.firstChild);
28455         ti.render(td);
28456         this.items.add(ti);
28457         return ti;
28458     },
28459
28460     /**
28461      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28462      * @type Roo.util.MixedCollection  
28463      */
28464     fields : false,
28465     
28466     /**
28467      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28468      * Note: the field should not have been rendered yet. For a field that has already been
28469      * rendered, use {@link #addElement}.
28470      * @param {Roo.form.Field} field
28471      * @return {Roo.ToolbarItem}
28472      */
28473      
28474       
28475     addField : function(field) {
28476         if (!this.fields) {
28477             var autoId = 0;
28478             this.fields = new Roo.util.MixedCollection(false, function(o){
28479                 return o.id || ("item" + (++autoId));
28480             });
28481
28482         }
28483         
28484         var td = this.nextBlock();
28485         field.render(td);
28486         var ti = new Roo.Toolbar.Item(td.firstChild);
28487         ti.render(td);
28488         this.items.add(ti);
28489         this.fields.add(field);
28490         return ti;
28491     },
28492     /**
28493      * Hide the toolbar
28494      * @method hide
28495      */
28496      
28497       
28498     hide : function()
28499     {
28500         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28501         this.el.child('div').hide();
28502     },
28503     /**
28504      * Show the toolbar
28505      * @method show
28506      */
28507     show : function()
28508     {
28509         this.el.child('div').show();
28510     },
28511       
28512     // private
28513     nextBlock : function(){
28514         var td = document.createElement("td");
28515         this.tr.appendChild(td);
28516         return td;
28517     },
28518
28519     // private
28520     destroy : function(){
28521         if(this.items){ // rendered?
28522             Roo.destroy.apply(Roo, this.items.items);
28523         }
28524         if(this.fields){ // rendered?
28525             Roo.destroy.apply(Roo, this.fields.items);
28526         }
28527         Roo.Element.uncache(this.el, this.tr);
28528     }
28529 };
28530
28531 /**
28532  * @class Roo.Toolbar.Item
28533  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28534  * @constructor
28535  * Creates a new Item
28536  * @param {HTMLElement} el 
28537  */
28538 Roo.Toolbar.Item = function(el){
28539     var cfg = {};
28540     if (typeof (el.xtype) != 'undefined') {
28541         cfg = el;
28542         el = cfg.el;
28543     }
28544     
28545     this.el = Roo.getDom(el);
28546     this.id = Roo.id(this.el);
28547     this.hidden = false;
28548     
28549     this.addEvents({
28550          /**
28551              * @event render
28552              * Fires when the button is rendered
28553              * @param {Button} this
28554              */
28555         'render': true
28556     });
28557     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28558 };
28559 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28560 //Roo.Toolbar.Item.prototype = {
28561     
28562     /**
28563      * Get this item's HTML Element
28564      * @return {HTMLElement}
28565      */
28566     getEl : function(){
28567        return this.el;  
28568     },
28569
28570     // private
28571     render : function(td){
28572         
28573          this.td = td;
28574         td.appendChild(this.el);
28575         
28576         this.fireEvent('render', this);
28577     },
28578     
28579     /**
28580      * Removes and destroys this item.
28581      */
28582     destroy : function(){
28583         this.td.parentNode.removeChild(this.td);
28584     },
28585     
28586     /**
28587      * Shows this item.
28588      */
28589     show: function(){
28590         this.hidden = false;
28591         this.td.style.display = "";
28592     },
28593     
28594     /**
28595      * Hides this item.
28596      */
28597     hide: function(){
28598         this.hidden = true;
28599         this.td.style.display = "none";
28600     },
28601     
28602     /**
28603      * Convenience function for boolean show/hide.
28604      * @param {Boolean} visible true to show/false to hide
28605      */
28606     setVisible: function(visible){
28607         if(visible) {
28608             this.show();
28609         }else{
28610             this.hide();
28611         }
28612     },
28613     
28614     /**
28615      * Try to focus this item.
28616      */
28617     focus : function(){
28618         Roo.fly(this.el).focus();
28619     },
28620     
28621     /**
28622      * Disables this item.
28623      */
28624     disable : function(){
28625         Roo.fly(this.td).addClass("x-item-disabled");
28626         this.disabled = true;
28627         this.el.disabled = true;
28628     },
28629     
28630     /**
28631      * Enables this item.
28632      */
28633     enable : function(){
28634         Roo.fly(this.td).removeClass("x-item-disabled");
28635         this.disabled = false;
28636         this.el.disabled = false;
28637     }
28638 });
28639
28640
28641 /**
28642  * @class Roo.Toolbar.Separator
28643  * @extends Roo.Toolbar.Item
28644  * A simple toolbar separator class
28645  * @constructor
28646  * Creates a new Separator
28647  */
28648 Roo.Toolbar.Separator = function(cfg){
28649     
28650     var s = document.createElement("span");
28651     s.className = "ytb-sep";
28652     if (cfg) {
28653         cfg.el = s;
28654     }
28655     
28656     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28657 };
28658 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28659     enable:Roo.emptyFn,
28660     disable:Roo.emptyFn,
28661     focus:Roo.emptyFn
28662 });
28663
28664 /**
28665  * @class Roo.Toolbar.Spacer
28666  * @extends Roo.Toolbar.Item
28667  * A simple element that adds extra horizontal space to a toolbar.
28668  * @constructor
28669  * Creates a new Spacer
28670  */
28671 Roo.Toolbar.Spacer = function(cfg){
28672     var s = document.createElement("div");
28673     s.className = "ytb-spacer";
28674     if (cfg) {
28675         cfg.el = s;
28676     }
28677     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28678 };
28679 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28680     enable:Roo.emptyFn,
28681     disable:Roo.emptyFn,
28682     focus:Roo.emptyFn
28683 });
28684
28685 /**
28686  * @class Roo.Toolbar.Fill
28687  * @extends Roo.Toolbar.Spacer
28688  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28689  * @constructor
28690  * Creates a new Spacer
28691  */
28692 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28693     // private
28694     render : function(td){
28695         td.style.width = '100%';
28696         Roo.Toolbar.Fill.superclass.render.call(this, td);
28697     }
28698 });
28699
28700 /**
28701  * @class Roo.Toolbar.TextItem
28702  * @extends Roo.Toolbar.Item
28703  * A simple class that renders text directly into a toolbar.
28704  * @constructor
28705  * Creates a new TextItem
28706  * @param {String} text
28707  */
28708 Roo.Toolbar.TextItem = function(cfg){
28709     var  text = cfg || "";
28710     if (typeof(cfg) == 'object') {
28711         text = cfg.text || "";
28712     }  else {
28713         cfg = null;
28714     }
28715     var s = document.createElement("span");
28716     s.className = "ytb-text";
28717     s.innerHTML = text;
28718     if (cfg) {
28719         cfg.el  = s;
28720     }
28721     
28722     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28723 };
28724 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28725     
28726      
28727     enable:Roo.emptyFn,
28728     disable:Roo.emptyFn,
28729     focus:Roo.emptyFn
28730 });
28731
28732 /**
28733  * @class Roo.Toolbar.Button
28734  * @extends Roo.Button
28735  * A button that renders into a toolbar.
28736  * @constructor
28737  * Creates a new Button
28738  * @param {Object} config A standard {@link Roo.Button} config object
28739  */
28740 Roo.Toolbar.Button = function(config){
28741     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28742 };
28743 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28744     render : function(td){
28745         this.td = td;
28746         Roo.Toolbar.Button.superclass.render.call(this, td);
28747     },
28748     
28749     /**
28750      * Removes and destroys this button
28751      */
28752     destroy : function(){
28753         Roo.Toolbar.Button.superclass.destroy.call(this);
28754         this.td.parentNode.removeChild(this.td);
28755     },
28756     
28757     /**
28758      * Shows this button
28759      */
28760     show: function(){
28761         this.hidden = false;
28762         this.td.style.display = "";
28763     },
28764     
28765     /**
28766      * Hides this button
28767      */
28768     hide: function(){
28769         this.hidden = true;
28770         this.td.style.display = "none";
28771     },
28772
28773     /**
28774      * Disables this item
28775      */
28776     disable : function(){
28777         Roo.fly(this.td).addClass("x-item-disabled");
28778         this.disabled = true;
28779     },
28780
28781     /**
28782      * Enables this item
28783      */
28784     enable : function(){
28785         Roo.fly(this.td).removeClass("x-item-disabled");
28786         this.disabled = false;
28787     }
28788 });
28789 // backwards compat
28790 Roo.ToolbarButton = Roo.Toolbar.Button;
28791
28792 /**
28793  * @class Roo.Toolbar.SplitButton
28794  * @extends Roo.SplitButton
28795  * A menu button that renders into a toolbar.
28796  * @constructor
28797  * Creates a new SplitButton
28798  * @param {Object} config A standard {@link Roo.SplitButton} config object
28799  */
28800 Roo.Toolbar.SplitButton = function(config){
28801     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28802 };
28803 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28804     render : function(td){
28805         this.td = td;
28806         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28807     },
28808     
28809     /**
28810      * Removes and destroys this button
28811      */
28812     destroy : function(){
28813         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28814         this.td.parentNode.removeChild(this.td);
28815     },
28816     
28817     /**
28818      * Shows this button
28819      */
28820     show: function(){
28821         this.hidden = false;
28822         this.td.style.display = "";
28823     },
28824     
28825     /**
28826      * Hides this button
28827      */
28828     hide: function(){
28829         this.hidden = true;
28830         this.td.style.display = "none";
28831     }
28832 });
28833
28834 // backwards compat
28835 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28836  * Based on:
28837  * Ext JS Library 1.1.1
28838  * Copyright(c) 2006-2007, Ext JS, LLC.
28839  *
28840  * Originally Released Under LGPL - original licence link has changed is not relivant.
28841  *
28842  * Fork - LGPL
28843  * <script type="text/javascript">
28844  */
28845  
28846 /**
28847  * @class Roo.PagingToolbar
28848  * @extends Roo.Toolbar
28849  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28850  * @constructor
28851  * Create a new PagingToolbar
28852  * @param {Object} config The config object
28853  */
28854 Roo.PagingToolbar = function(el, ds, config)
28855 {
28856     // old args format still supported... - xtype is prefered..
28857     if (typeof(el) == 'object' && el.xtype) {
28858         // created from xtype...
28859         config = el;
28860         ds = el.dataSource;
28861         el = config.container;
28862     }
28863     var items = [];
28864     if (config.items) {
28865         items = config.items;
28866         config.items = [];
28867     }
28868     
28869     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28870     this.ds = ds;
28871     this.cursor = 0;
28872     this.renderButtons(this.el);
28873     this.bind(ds);
28874     
28875     // supprot items array.
28876    
28877     Roo.each(items, function(e) {
28878         this.add(Roo.factory(e));
28879     },this);
28880     
28881 };
28882
28883 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28884     /**
28885      * @cfg {Roo.data.Store} dataSource
28886      * The underlying data store providing the paged data
28887      */
28888     /**
28889      * @cfg {String/HTMLElement/Element} container
28890      * container The id or element that will contain the toolbar
28891      */
28892     /**
28893      * @cfg {Boolean} displayInfo
28894      * True to display the displayMsg (defaults to false)
28895      */
28896     /**
28897      * @cfg {Number} pageSize
28898      * The number of records to display per page (defaults to 20)
28899      */
28900     pageSize: 20,
28901     /**
28902      * @cfg {String} displayMsg
28903      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28904      */
28905     displayMsg : 'Displaying {0} - {1} of {2}',
28906     /**
28907      * @cfg {String} emptyMsg
28908      * The message to display when no records are found (defaults to "No data to display")
28909      */
28910     emptyMsg : 'No data to display',
28911     /**
28912      * Customizable piece of the default paging text (defaults to "Page")
28913      * @type String
28914      */
28915     beforePageText : "Page",
28916     /**
28917      * Customizable piece of the default paging text (defaults to "of %0")
28918      * @type String
28919      */
28920     afterPageText : "of {0}",
28921     /**
28922      * Customizable piece of the default paging text (defaults to "First Page")
28923      * @type String
28924      */
28925     firstText : "First Page",
28926     /**
28927      * Customizable piece of the default paging text (defaults to "Previous Page")
28928      * @type String
28929      */
28930     prevText : "Previous Page",
28931     /**
28932      * Customizable piece of the default paging text (defaults to "Next Page")
28933      * @type String
28934      */
28935     nextText : "Next Page",
28936     /**
28937      * Customizable piece of the default paging text (defaults to "Last Page")
28938      * @type String
28939      */
28940     lastText : "Last Page",
28941     /**
28942      * Customizable piece of the default paging text (defaults to "Refresh")
28943      * @type String
28944      */
28945     refreshText : "Refresh",
28946
28947     // private
28948     renderButtons : function(el){
28949         Roo.PagingToolbar.superclass.render.call(this, el);
28950         this.first = this.addButton({
28951             tooltip: this.firstText,
28952             cls: "x-btn-icon x-grid-page-first",
28953             disabled: true,
28954             handler: this.onClick.createDelegate(this, ["first"])
28955         });
28956         this.prev = this.addButton({
28957             tooltip: this.prevText,
28958             cls: "x-btn-icon x-grid-page-prev",
28959             disabled: true,
28960             handler: this.onClick.createDelegate(this, ["prev"])
28961         });
28962         //this.addSeparator();
28963         this.add(this.beforePageText);
28964         this.field = Roo.get(this.addDom({
28965            tag: "input",
28966            type: "text",
28967            size: "3",
28968            value: "1",
28969            cls: "x-grid-page-number"
28970         }).el);
28971         this.field.on("keydown", this.onPagingKeydown, this);
28972         this.field.on("focus", function(){this.dom.select();});
28973         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28974         this.field.setHeight(18);
28975         //this.addSeparator();
28976         this.next = this.addButton({
28977             tooltip: this.nextText,
28978             cls: "x-btn-icon x-grid-page-next",
28979             disabled: true,
28980             handler: this.onClick.createDelegate(this, ["next"])
28981         });
28982         this.last = this.addButton({
28983             tooltip: this.lastText,
28984             cls: "x-btn-icon x-grid-page-last",
28985             disabled: true,
28986             handler: this.onClick.createDelegate(this, ["last"])
28987         });
28988         //this.addSeparator();
28989         this.loading = this.addButton({
28990             tooltip: this.refreshText,
28991             cls: "x-btn-icon x-grid-loading",
28992             handler: this.onClick.createDelegate(this, ["refresh"])
28993         });
28994
28995         if(this.displayInfo){
28996             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28997         }
28998     },
28999
29000     // private
29001     updateInfo : function(){
29002         if(this.displayEl){
29003             var count = this.ds.getCount();
29004             var msg = count == 0 ?
29005                 this.emptyMsg :
29006                 String.format(
29007                     this.displayMsg,
29008                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29009                 );
29010             this.displayEl.update(msg);
29011         }
29012     },
29013
29014     // private
29015     onLoad : function(ds, r, o){
29016        this.cursor = o.params ? o.params.start : 0;
29017        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29018
29019        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29020        this.field.dom.value = ap;
29021        this.first.setDisabled(ap == 1);
29022        this.prev.setDisabled(ap == 1);
29023        this.next.setDisabled(ap == ps);
29024        this.last.setDisabled(ap == ps);
29025        this.loading.enable();
29026        this.updateInfo();
29027     },
29028
29029     // private
29030     getPageData : function(){
29031         var total = this.ds.getTotalCount();
29032         return {
29033             total : total,
29034             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29035             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29036         };
29037     },
29038
29039     // private
29040     onLoadError : function(){
29041         this.loading.enable();
29042     },
29043
29044     // private
29045     onPagingKeydown : function(e){
29046         var k = e.getKey();
29047         var d = this.getPageData();
29048         if(k == e.RETURN){
29049             var v = this.field.dom.value, pageNum;
29050             if(!v || isNaN(pageNum = parseInt(v, 10))){
29051                 this.field.dom.value = d.activePage;
29052                 return;
29053             }
29054             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29055             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29056             e.stopEvent();
29057         }
29058         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))
29059         {
29060           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29061           this.field.dom.value = pageNum;
29062           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29063           e.stopEvent();
29064         }
29065         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29066         {
29067           var v = this.field.dom.value, pageNum; 
29068           var increment = (e.shiftKey) ? 10 : 1;
29069           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29070             increment *= -1;
29071           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29072             this.field.dom.value = d.activePage;
29073             return;
29074           }
29075           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29076           {
29077             this.field.dom.value = parseInt(v, 10) + increment;
29078             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29079             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29080           }
29081           e.stopEvent();
29082         }
29083     },
29084
29085     // private
29086     beforeLoad : function(){
29087         if(this.loading){
29088             this.loading.disable();
29089         }
29090     },
29091
29092     // private
29093     onClick : function(which){
29094         var ds = this.ds;
29095         switch(which){
29096             case "first":
29097                 ds.load({params:{start: 0, limit: this.pageSize}});
29098             break;
29099             case "prev":
29100                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29101             break;
29102             case "next":
29103                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29104             break;
29105             case "last":
29106                 var total = ds.getTotalCount();
29107                 var extra = total % this.pageSize;
29108                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29109                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29110             break;
29111             case "refresh":
29112                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29113             break;
29114         }
29115     },
29116
29117     /**
29118      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29119      * @param {Roo.data.Store} store The data store to unbind
29120      */
29121     unbind : function(ds){
29122         ds.un("beforeload", this.beforeLoad, this);
29123         ds.un("load", this.onLoad, this);
29124         ds.un("loadexception", this.onLoadError, this);
29125         ds.un("remove", this.updateInfo, this);
29126         ds.un("add", this.updateInfo, this);
29127         this.ds = undefined;
29128     },
29129
29130     /**
29131      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29132      * @param {Roo.data.Store} store The data store to bind
29133      */
29134     bind : function(ds){
29135         ds.on("beforeload", this.beforeLoad, this);
29136         ds.on("load", this.onLoad, this);
29137         ds.on("loadexception", this.onLoadError, this);
29138         ds.on("remove", this.updateInfo, this);
29139         ds.on("add", this.updateInfo, this);
29140         this.ds = ds;
29141     }
29142 });/*
29143  * Based on:
29144  * Ext JS Library 1.1.1
29145  * Copyright(c) 2006-2007, Ext JS, LLC.
29146  *
29147  * Originally Released Under LGPL - original licence link has changed is not relivant.
29148  *
29149  * Fork - LGPL
29150  * <script type="text/javascript">
29151  */
29152
29153 /**
29154  * @class Roo.Resizable
29155  * @extends Roo.util.Observable
29156  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29157  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29158  * 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
29159  * the element will be wrapped for you automatically.</p>
29160  * <p>Here is the list of valid resize handles:</p>
29161  * <pre>
29162 Value   Description
29163 ------  -------------------
29164  'n'     north
29165  's'     south
29166  'e'     east
29167  'w'     west
29168  'nw'    northwest
29169  'sw'    southwest
29170  'se'    southeast
29171  'ne'    northeast
29172  'hd'    horizontal drag
29173  'all'   all
29174 </pre>
29175  * <p>Here's an example showing the creation of a typical Resizable:</p>
29176  * <pre><code>
29177 var resizer = new Roo.Resizable("element-id", {
29178     handles: 'all',
29179     minWidth: 200,
29180     minHeight: 100,
29181     maxWidth: 500,
29182     maxHeight: 400,
29183     pinned: true
29184 });
29185 resizer.on("resize", myHandler);
29186 </code></pre>
29187  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29188  * resizer.east.setDisplayed(false);</p>
29189  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29190  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29191  * resize operation's new size (defaults to [0, 0])
29192  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29193  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29194  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29195  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29196  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29197  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29198  * @cfg {Number} width The width of the element in pixels (defaults to null)
29199  * @cfg {Number} height The height of the element in pixels (defaults to null)
29200  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29201  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29202  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29203  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29204  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29205  * in favor of the handles config option (defaults to false)
29206  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29207  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29208  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29209  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29210  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29211  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29212  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29213  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29214  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29215  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29216  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29217  * @constructor
29218  * Create a new resizable component
29219  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29220  * @param {Object} config configuration options
29221   */
29222 Roo.Resizable = function(el, config)
29223 {
29224     this.el = Roo.get(el);
29225
29226     if(config && config.wrap){
29227         config.resizeChild = this.el;
29228         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29229         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29230         this.el.setStyle("overflow", "hidden");
29231         this.el.setPositioning(config.resizeChild.getPositioning());
29232         config.resizeChild.clearPositioning();
29233         if(!config.width || !config.height){
29234             var csize = config.resizeChild.getSize();
29235             this.el.setSize(csize.width, csize.height);
29236         }
29237         if(config.pinned && !config.adjustments){
29238             config.adjustments = "auto";
29239         }
29240     }
29241
29242     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29243     this.proxy.unselectable();
29244     this.proxy.enableDisplayMode('block');
29245
29246     Roo.apply(this, config);
29247
29248     if(this.pinned){
29249         this.disableTrackOver = true;
29250         this.el.addClass("x-resizable-pinned");
29251     }
29252     // if the element isn't positioned, make it relative
29253     var position = this.el.getStyle("position");
29254     if(position != "absolute" && position != "fixed"){
29255         this.el.setStyle("position", "relative");
29256     }
29257     if(!this.handles){ // no handles passed, must be legacy style
29258         this.handles = 's,e,se';
29259         if(this.multiDirectional){
29260             this.handles += ',n,w';
29261         }
29262     }
29263     if(this.handles == "all"){
29264         this.handles = "n s e w ne nw se sw";
29265     }
29266     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29267     var ps = Roo.Resizable.positions;
29268     for(var i = 0, len = hs.length; i < len; i++){
29269         if(hs[i] && ps[hs[i]]){
29270             var pos = ps[hs[i]];
29271             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29272         }
29273     }
29274     // legacy
29275     this.corner = this.southeast;
29276     
29277     // updateBox = the box can move..
29278     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29279         this.updateBox = true;
29280     }
29281
29282     this.activeHandle = null;
29283
29284     if(this.resizeChild){
29285         if(typeof this.resizeChild == "boolean"){
29286             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29287         }else{
29288             this.resizeChild = Roo.get(this.resizeChild, true);
29289         }
29290     }
29291     
29292     if(this.adjustments == "auto"){
29293         var rc = this.resizeChild;
29294         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29295         if(rc && (hw || hn)){
29296             rc.position("relative");
29297             rc.setLeft(hw ? hw.el.getWidth() : 0);
29298             rc.setTop(hn ? hn.el.getHeight() : 0);
29299         }
29300         this.adjustments = [
29301             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29302             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29303         ];
29304     }
29305
29306     if(this.draggable){
29307         this.dd = this.dynamic ?
29308             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29309         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29310     }
29311
29312     // public events
29313     this.addEvents({
29314         /**
29315          * @event beforeresize
29316          * Fired before resize is allowed. Set enabled to false to cancel resize.
29317          * @param {Roo.Resizable} this
29318          * @param {Roo.EventObject} e The mousedown event
29319          */
29320         "beforeresize" : true,
29321         /**
29322          * @event resizing
29323          * Fired a resizing.
29324          * @param {Roo.Resizable} this
29325          * @param {Number} x The new x position
29326          * @param {Number} y The new y position
29327          * @param {Number} w The new w width
29328          * @param {Number} h The new h hight
29329          * @param {Roo.EventObject} e The mouseup event
29330          */
29331         "resizing" : true,
29332         /**
29333          * @event resize
29334          * Fired after a resize.
29335          * @param {Roo.Resizable} this
29336          * @param {Number} width The new width
29337          * @param {Number} height The new height
29338          * @param {Roo.EventObject} e The mouseup event
29339          */
29340         "resize" : true
29341     });
29342
29343     if(this.width !== null && this.height !== null){
29344         this.resizeTo(this.width, this.height);
29345     }else{
29346         this.updateChildSize();
29347     }
29348     if(Roo.isIE){
29349         this.el.dom.style.zoom = 1;
29350     }
29351     Roo.Resizable.superclass.constructor.call(this);
29352 };
29353
29354 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29355         resizeChild : false,
29356         adjustments : [0, 0],
29357         minWidth : 5,
29358         minHeight : 5,
29359         maxWidth : 10000,
29360         maxHeight : 10000,
29361         enabled : true,
29362         animate : false,
29363         duration : .35,
29364         dynamic : false,
29365         handles : false,
29366         multiDirectional : false,
29367         disableTrackOver : false,
29368         easing : 'easeOutStrong',
29369         widthIncrement : 0,
29370         heightIncrement : 0,
29371         pinned : false,
29372         width : null,
29373         height : null,
29374         preserveRatio : false,
29375         transparent: false,
29376         minX: 0,
29377         minY: 0,
29378         draggable: false,
29379
29380         /**
29381          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29382          */
29383         constrainTo: undefined,
29384         /**
29385          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29386          */
29387         resizeRegion: undefined,
29388
29389
29390     /**
29391      * Perform a manual resize
29392      * @param {Number} width
29393      * @param {Number} height
29394      */
29395     resizeTo : function(width, height){
29396         this.el.setSize(width, height);
29397         this.updateChildSize();
29398         this.fireEvent("resize", this, width, height, null);
29399     },
29400
29401     // private
29402     startSizing : function(e, handle){
29403         this.fireEvent("beforeresize", this, e);
29404         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29405
29406             if(!this.overlay){
29407                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29408                 this.overlay.unselectable();
29409                 this.overlay.enableDisplayMode("block");
29410                 this.overlay.on("mousemove", this.onMouseMove, this);
29411                 this.overlay.on("mouseup", this.onMouseUp, this);
29412             }
29413             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29414
29415             this.resizing = true;
29416             this.startBox = this.el.getBox();
29417             this.startPoint = e.getXY();
29418             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29419                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29420
29421             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29422             this.overlay.show();
29423
29424             if(this.constrainTo) {
29425                 var ct = Roo.get(this.constrainTo);
29426                 this.resizeRegion = ct.getRegion().adjust(
29427                     ct.getFrameWidth('t'),
29428                     ct.getFrameWidth('l'),
29429                     -ct.getFrameWidth('b'),
29430                     -ct.getFrameWidth('r')
29431                 );
29432             }
29433
29434             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29435             this.proxy.show();
29436             this.proxy.setBox(this.startBox);
29437             if(!this.dynamic){
29438                 this.proxy.setStyle('visibility', 'visible');
29439             }
29440         }
29441     },
29442
29443     // private
29444     onMouseDown : function(handle, e){
29445         if(this.enabled){
29446             e.stopEvent();
29447             this.activeHandle = handle;
29448             this.startSizing(e, handle);
29449         }
29450     },
29451
29452     // private
29453     onMouseUp : function(e){
29454         var size = this.resizeElement();
29455         this.resizing = false;
29456         this.handleOut();
29457         this.overlay.hide();
29458         this.proxy.hide();
29459         this.fireEvent("resize", this, size.width, size.height, e);
29460     },
29461
29462     // private
29463     updateChildSize : function(){
29464         
29465         if(this.resizeChild){
29466             var el = this.el;
29467             var child = this.resizeChild;
29468             var adj = this.adjustments;
29469             if(el.dom.offsetWidth){
29470                 var b = el.getSize(true);
29471                 child.setSize(b.width+adj[0], b.height+adj[1]);
29472             }
29473             // Second call here for IE
29474             // The first call enables instant resizing and
29475             // the second call corrects scroll bars if they
29476             // exist
29477             if(Roo.isIE){
29478                 setTimeout(function(){
29479                     if(el.dom.offsetWidth){
29480                         var b = el.getSize(true);
29481                         child.setSize(b.width+adj[0], b.height+adj[1]);
29482                     }
29483                 }, 10);
29484             }
29485         }
29486     },
29487
29488     // private
29489     snap : function(value, inc, min){
29490         if(!inc || !value) return value;
29491         var newValue = value;
29492         var m = value % inc;
29493         if(m > 0){
29494             if(m > (inc/2)){
29495                 newValue = value + (inc-m);
29496             }else{
29497                 newValue = value - m;
29498             }
29499         }
29500         return Math.max(min, newValue);
29501     },
29502
29503     // private
29504     resizeElement : function(){
29505         var box = this.proxy.getBox();
29506         if(this.updateBox){
29507             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29508         }else{
29509             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29510         }
29511         this.updateChildSize();
29512         if(!this.dynamic){
29513             this.proxy.hide();
29514         }
29515         return box;
29516     },
29517
29518     // private
29519     constrain : function(v, diff, m, mx){
29520         if(v - diff < m){
29521             diff = v - m;
29522         }else if(v - diff > mx){
29523             diff = mx - v;
29524         }
29525         return diff;
29526     },
29527
29528     // private
29529     onMouseMove : function(e){
29530         
29531         if(this.enabled){
29532             try{// try catch so if something goes wrong the user doesn't get hung
29533
29534             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29535                 return;
29536             }
29537
29538             //var curXY = this.startPoint;
29539             var curSize = this.curSize || this.startBox;
29540             var x = this.startBox.x, y = this.startBox.y;
29541             var ox = x, oy = y;
29542             var w = curSize.width, h = curSize.height;
29543             var ow = w, oh = h;
29544             var mw = this.minWidth, mh = this.minHeight;
29545             var mxw = this.maxWidth, mxh = this.maxHeight;
29546             var wi = this.widthIncrement;
29547             var hi = this.heightIncrement;
29548
29549             var eventXY = e.getXY();
29550             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29551             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29552
29553             var pos = this.activeHandle.position;
29554
29555             switch(pos){
29556                 case "east":
29557                     w += diffX;
29558                     w = Math.min(Math.max(mw, w), mxw);
29559                     break;
29560              
29561                 case "south":
29562                     h += diffY;
29563                     h = Math.min(Math.max(mh, h), mxh);
29564                     break;
29565                 case "southeast":
29566                     w += diffX;
29567                     h += diffY;
29568                     w = Math.min(Math.max(mw, w), mxw);
29569                     h = Math.min(Math.max(mh, h), mxh);
29570                     break;
29571                 case "north":
29572                     diffY = this.constrain(h, diffY, mh, mxh);
29573                     y += diffY;
29574                     h -= diffY;
29575                     break;
29576                 case "hdrag":
29577                     
29578                     if (wi) {
29579                         var adiffX = Math.abs(diffX);
29580                         var sub = (adiffX % wi); // how much 
29581                         if (sub > (wi/2)) { // far enough to snap
29582                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29583                         } else {
29584                             // remove difference.. 
29585                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29586                         }
29587                     }
29588                     x += diffX;
29589                     x = Math.max(this.minX, x);
29590                     break;
29591                 case "west":
29592                     diffX = this.constrain(w, diffX, mw, mxw);
29593                     x += diffX;
29594                     w -= diffX;
29595                     break;
29596                 case "northeast":
29597                     w += diffX;
29598                     w = Math.min(Math.max(mw, w), mxw);
29599                     diffY = this.constrain(h, diffY, mh, mxh);
29600                     y += diffY;
29601                     h -= diffY;
29602                     break;
29603                 case "northwest":
29604                     diffX = this.constrain(w, diffX, mw, mxw);
29605                     diffY = this.constrain(h, diffY, mh, mxh);
29606                     y += diffY;
29607                     h -= diffY;
29608                     x += diffX;
29609                     w -= diffX;
29610                     break;
29611                case "southwest":
29612                     diffX = this.constrain(w, diffX, mw, mxw);
29613                     h += diffY;
29614                     h = Math.min(Math.max(mh, h), mxh);
29615                     x += diffX;
29616                     w -= diffX;
29617                     break;
29618             }
29619
29620             var sw = this.snap(w, wi, mw);
29621             var sh = this.snap(h, hi, mh);
29622             if(sw != w || sh != h){
29623                 switch(pos){
29624                     case "northeast":
29625                         y -= sh - h;
29626                     break;
29627                     case "north":
29628                         y -= sh - h;
29629                         break;
29630                     case "southwest":
29631                         x -= sw - w;
29632                     break;
29633                     case "west":
29634                         x -= sw - w;
29635                         break;
29636                     case "northwest":
29637                         x -= sw - w;
29638                         y -= sh - h;
29639                     break;
29640                 }
29641                 w = sw;
29642                 h = sh;
29643             }
29644
29645             if(this.preserveRatio){
29646                 switch(pos){
29647                     case "southeast":
29648                     case "east":
29649                         h = oh * (w/ow);
29650                         h = Math.min(Math.max(mh, h), mxh);
29651                         w = ow * (h/oh);
29652                        break;
29653                     case "south":
29654                         w = ow * (h/oh);
29655                         w = Math.min(Math.max(mw, w), mxw);
29656                         h = oh * (w/ow);
29657                         break;
29658                     case "northeast":
29659                         w = ow * (h/oh);
29660                         w = Math.min(Math.max(mw, w), mxw);
29661                         h = oh * (w/ow);
29662                     break;
29663                     case "north":
29664                         var tw = w;
29665                         w = ow * (h/oh);
29666                         w = Math.min(Math.max(mw, w), mxw);
29667                         h = oh * (w/ow);
29668                         x += (tw - w) / 2;
29669                         break;
29670                     case "southwest":
29671                         h = oh * (w/ow);
29672                         h = Math.min(Math.max(mh, h), mxh);
29673                         var tw = w;
29674                         w = ow * (h/oh);
29675                         x += tw - w;
29676                         break;
29677                     case "west":
29678                         var th = h;
29679                         h = oh * (w/ow);
29680                         h = Math.min(Math.max(mh, h), mxh);
29681                         y += (th - h) / 2;
29682                         var tw = w;
29683                         w = ow * (h/oh);
29684                         x += tw - w;
29685                        break;
29686                     case "northwest":
29687                         var tw = w;
29688                         var th = h;
29689                         h = oh * (w/ow);
29690                         h = Math.min(Math.max(mh, h), mxh);
29691                         w = ow * (h/oh);
29692                         y += th - h;
29693                         x += tw - w;
29694                        break;
29695
29696                 }
29697             }
29698             if (pos == 'hdrag') {
29699                 w = ow;
29700             }
29701             this.proxy.setBounds(x, y, w, h);
29702             if(this.dynamic){
29703                 this.resizeElement();
29704             }
29705             }catch(e){}
29706         }
29707         this.fireEvent("resizing", this, x, y, w, h, e);
29708     },
29709
29710     // private
29711     handleOver : function(){
29712         if(this.enabled){
29713             this.el.addClass("x-resizable-over");
29714         }
29715     },
29716
29717     // private
29718     handleOut : function(){
29719         if(!this.resizing){
29720             this.el.removeClass("x-resizable-over");
29721         }
29722     },
29723
29724     /**
29725      * Returns the element this component is bound to.
29726      * @return {Roo.Element}
29727      */
29728     getEl : function(){
29729         return this.el;
29730     },
29731
29732     /**
29733      * Returns the resizeChild element (or null).
29734      * @return {Roo.Element}
29735      */
29736     getResizeChild : function(){
29737         return this.resizeChild;
29738     },
29739     groupHandler : function()
29740     {
29741         
29742     },
29743     /**
29744      * Destroys this resizable. If the element was wrapped and
29745      * removeEl is not true then the element remains.
29746      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29747      */
29748     destroy : function(removeEl){
29749         this.proxy.remove();
29750         if(this.overlay){
29751             this.overlay.removeAllListeners();
29752             this.overlay.remove();
29753         }
29754         var ps = Roo.Resizable.positions;
29755         for(var k in ps){
29756             if(typeof ps[k] != "function" && this[ps[k]]){
29757                 var h = this[ps[k]];
29758                 h.el.removeAllListeners();
29759                 h.el.remove();
29760             }
29761         }
29762         if(removeEl){
29763             this.el.update("");
29764             this.el.remove();
29765         }
29766     }
29767 });
29768
29769 // private
29770 // hash to map config positions to true positions
29771 Roo.Resizable.positions = {
29772     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29773     hd: "hdrag"
29774 };
29775
29776 // private
29777 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29778     if(!this.tpl){
29779         // only initialize the template if resizable is used
29780         var tpl = Roo.DomHelper.createTemplate(
29781             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29782         );
29783         tpl.compile();
29784         Roo.Resizable.Handle.prototype.tpl = tpl;
29785     }
29786     this.position = pos;
29787     this.rz = rz;
29788     // show north drag fro topdra
29789     var handlepos = pos == 'hdrag' ? 'north' : pos;
29790     
29791     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29792     if (pos == 'hdrag') {
29793         this.el.setStyle('cursor', 'pointer');
29794     }
29795     this.el.unselectable();
29796     if(transparent){
29797         this.el.setOpacity(0);
29798     }
29799     this.el.on("mousedown", this.onMouseDown, this);
29800     if(!disableTrackOver){
29801         this.el.on("mouseover", this.onMouseOver, this);
29802         this.el.on("mouseout", this.onMouseOut, this);
29803     }
29804 };
29805
29806 // private
29807 Roo.Resizable.Handle.prototype = {
29808     afterResize : function(rz){
29809         Roo.log('after?');
29810         // do nothing
29811     },
29812     // private
29813     onMouseDown : function(e){
29814         this.rz.onMouseDown(this, e);
29815     },
29816     // private
29817     onMouseOver : function(e){
29818         this.rz.handleOver(this, e);
29819     },
29820     // private
29821     onMouseOut : function(e){
29822         this.rz.handleOut(this, e);
29823     }
29824 };/*
29825  * Based on:
29826  * Ext JS Library 1.1.1
29827  * Copyright(c) 2006-2007, Ext JS, LLC.
29828  *
29829  * Originally Released Under LGPL - original licence link has changed is not relivant.
29830  *
29831  * Fork - LGPL
29832  * <script type="text/javascript">
29833  */
29834
29835 /**
29836  * @class Roo.Editor
29837  * @extends Roo.Component
29838  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29839  * @constructor
29840  * Create a new Editor
29841  * @param {Roo.form.Field} field The Field object (or descendant)
29842  * @param {Object} config The config object
29843  */
29844 Roo.Editor = function(field, config){
29845     Roo.Editor.superclass.constructor.call(this, config);
29846     this.field = field;
29847     this.addEvents({
29848         /**
29849              * @event beforestartedit
29850              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29851              * false from the handler of this event.
29852              * @param {Editor} this
29853              * @param {Roo.Element} boundEl The underlying element bound to this editor
29854              * @param {Mixed} value The field value being set
29855              */
29856         "beforestartedit" : true,
29857         /**
29858              * @event startedit
29859              * Fires when this editor is displayed
29860              * @param {Roo.Element} boundEl The underlying element bound to this editor
29861              * @param {Mixed} value The starting field value
29862              */
29863         "startedit" : true,
29864         /**
29865              * @event beforecomplete
29866              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29867              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29868              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29869              * event will not fire since no edit actually occurred.
29870              * @param {Editor} this
29871              * @param {Mixed} value The current field value
29872              * @param {Mixed} startValue The original field value
29873              */
29874         "beforecomplete" : true,
29875         /**
29876              * @event complete
29877              * Fires after editing is complete and any changed value has been written to the underlying field.
29878              * @param {Editor} this
29879              * @param {Mixed} value The current field value
29880              * @param {Mixed} startValue The original field value
29881              */
29882         "complete" : true,
29883         /**
29884          * @event specialkey
29885          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29886          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29887          * @param {Roo.form.Field} this
29888          * @param {Roo.EventObject} e The event object
29889          */
29890         "specialkey" : true
29891     });
29892 };
29893
29894 Roo.extend(Roo.Editor, Roo.Component, {
29895     /**
29896      * @cfg {Boolean/String} autosize
29897      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29898      * or "height" to adopt the height only (defaults to false)
29899      */
29900     /**
29901      * @cfg {Boolean} revertInvalid
29902      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29903      * validation fails (defaults to true)
29904      */
29905     /**
29906      * @cfg {Boolean} ignoreNoChange
29907      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29908      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29909      * will never be ignored.
29910      */
29911     /**
29912      * @cfg {Boolean} hideEl
29913      * False to keep the bound element visible while the editor is displayed (defaults to true)
29914      */
29915     /**
29916      * @cfg {Mixed} value
29917      * The data value of the underlying field (defaults to "")
29918      */
29919     value : "",
29920     /**
29921      * @cfg {String} alignment
29922      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29923      */
29924     alignment: "c-c?",
29925     /**
29926      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29927      * for bottom-right shadow (defaults to "frame")
29928      */
29929     shadow : "frame",
29930     /**
29931      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29932      */
29933     constrain : false,
29934     /**
29935      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29936      */
29937     completeOnEnter : false,
29938     /**
29939      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29940      */
29941     cancelOnEsc : false,
29942     /**
29943      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29944      */
29945     updateEl : false,
29946
29947     // private
29948     onRender : function(ct, position){
29949         this.el = new Roo.Layer({
29950             shadow: this.shadow,
29951             cls: "x-editor",
29952             parentEl : ct,
29953             shim : this.shim,
29954             shadowOffset:4,
29955             id: this.id,
29956             constrain: this.constrain
29957         });
29958         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29959         if(this.field.msgTarget != 'title'){
29960             this.field.msgTarget = 'qtip';
29961         }
29962         this.field.render(this.el);
29963         if(Roo.isGecko){
29964             this.field.el.dom.setAttribute('autocomplete', 'off');
29965         }
29966         this.field.on("specialkey", this.onSpecialKey, this);
29967         if(this.swallowKeys){
29968             this.field.el.swallowEvent(['keydown','keypress']);
29969         }
29970         this.field.show();
29971         this.field.on("blur", this.onBlur, this);
29972         if(this.field.grow){
29973             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29974         }
29975     },
29976
29977     onSpecialKey : function(field, e)
29978     {
29979         //Roo.log('editor onSpecialKey');
29980         if(this.completeOnEnter && e.getKey() == e.ENTER){
29981             e.stopEvent();
29982             this.completeEdit();
29983             return;
29984         }
29985         // do not fire special key otherwise it might hide close the editor...
29986         if(e.getKey() == e.ENTER){    
29987             return;
29988         }
29989         if(this.cancelOnEsc && e.getKey() == e.ESC){
29990             this.cancelEdit();
29991             return;
29992         } 
29993         this.fireEvent('specialkey', field, e);
29994     
29995     },
29996
29997     /**
29998      * Starts the editing process and shows the editor.
29999      * @param {String/HTMLElement/Element} el The element to edit
30000      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30001       * to the innerHTML of el.
30002      */
30003     startEdit : function(el, value){
30004         if(this.editing){
30005             this.completeEdit();
30006         }
30007         this.boundEl = Roo.get(el);
30008         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30009         if(!this.rendered){
30010             this.render(this.parentEl || document.body);
30011         }
30012         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30013             return;
30014         }
30015         this.startValue = v;
30016         this.field.setValue(v);
30017         if(this.autoSize){
30018             var sz = this.boundEl.getSize();
30019             switch(this.autoSize){
30020                 case "width":
30021                 this.setSize(sz.width,  "");
30022                 break;
30023                 case "height":
30024                 this.setSize("",  sz.height);
30025                 break;
30026                 default:
30027                 this.setSize(sz.width,  sz.height);
30028             }
30029         }
30030         this.el.alignTo(this.boundEl, this.alignment);
30031         this.editing = true;
30032         if(Roo.QuickTips){
30033             Roo.QuickTips.disable();
30034         }
30035         this.show();
30036     },
30037
30038     /**
30039      * Sets the height and width of this editor.
30040      * @param {Number} width The new width
30041      * @param {Number} height The new height
30042      */
30043     setSize : function(w, h){
30044         this.field.setSize(w, h);
30045         if(this.el){
30046             this.el.sync();
30047         }
30048     },
30049
30050     /**
30051      * Realigns the editor to the bound field based on the current alignment config value.
30052      */
30053     realign : function(){
30054         this.el.alignTo(this.boundEl, this.alignment);
30055     },
30056
30057     /**
30058      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30059      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30060      */
30061     completeEdit : function(remainVisible){
30062         if(!this.editing){
30063             return;
30064         }
30065         var v = this.getValue();
30066         if(this.revertInvalid !== false && !this.field.isValid()){
30067             v = this.startValue;
30068             this.cancelEdit(true);
30069         }
30070         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30071             this.editing = false;
30072             this.hide();
30073             return;
30074         }
30075         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30076             this.editing = false;
30077             if(this.updateEl && this.boundEl){
30078                 this.boundEl.update(v);
30079             }
30080             if(remainVisible !== true){
30081                 this.hide();
30082             }
30083             this.fireEvent("complete", this, v, this.startValue);
30084         }
30085     },
30086
30087     // private
30088     onShow : function(){
30089         this.el.show();
30090         if(this.hideEl !== false){
30091             this.boundEl.hide();
30092         }
30093         this.field.show();
30094         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30095             this.fixIEFocus = true;
30096             this.deferredFocus.defer(50, this);
30097         }else{
30098             this.field.focus();
30099         }
30100         this.fireEvent("startedit", this.boundEl, this.startValue);
30101     },
30102
30103     deferredFocus : function(){
30104         if(this.editing){
30105             this.field.focus();
30106         }
30107     },
30108
30109     /**
30110      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30111      * reverted to the original starting value.
30112      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30113      * cancel (defaults to false)
30114      */
30115     cancelEdit : function(remainVisible){
30116         if(this.editing){
30117             this.setValue(this.startValue);
30118             if(remainVisible !== true){
30119                 this.hide();
30120             }
30121         }
30122     },
30123
30124     // private
30125     onBlur : function(){
30126         if(this.allowBlur !== true && this.editing){
30127             this.completeEdit();
30128         }
30129     },
30130
30131     // private
30132     onHide : function(){
30133         if(this.editing){
30134             this.completeEdit();
30135             return;
30136         }
30137         this.field.blur();
30138         if(this.field.collapse){
30139             this.field.collapse();
30140         }
30141         this.el.hide();
30142         if(this.hideEl !== false){
30143             this.boundEl.show();
30144         }
30145         if(Roo.QuickTips){
30146             Roo.QuickTips.enable();
30147         }
30148     },
30149
30150     /**
30151      * Sets the data value of the editor
30152      * @param {Mixed} value Any valid value supported by the underlying field
30153      */
30154     setValue : function(v){
30155         this.field.setValue(v);
30156     },
30157
30158     /**
30159      * Gets the data value of the editor
30160      * @return {Mixed} The data value
30161      */
30162     getValue : function(){
30163         return this.field.getValue();
30164     }
30165 });/*
30166  * Based on:
30167  * Ext JS Library 1.1.1
30168  * Copyright(c) 2006-2007, Ext JS, LLC.
30169  *
30170  * Originally Released Under LGPL - original licence link has changed is not relivant.
30171  *
30172  * Fork - LGPL
30173  * <script type="text/javascript">
30174  */
30175  
30176 /**
30177  * @class Roo.BasicDialog
30178  * @extends Roo.util.Observable
30179  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30180  * <pre><code>
30181 var dlg = new Roo.BasicDialog("my-dlg", {
30182     height: 200,
30183     width: 300,
30184     minHeight: 100,
30185     minWidth: 150,
30186     modal: true,
30187     proxyDrag: true,
30188     shadow: true
30189 });
30190 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30191 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30192 dlg.addButton('Cancel', dlg.hide, dlg);
30193 dlg.show();
30194 </code></pre>
30195   <b>A Dialog should always be a direct child of the body element.</b>
30196  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30197  * @cfg {String} title Default text to display in the title bar (defaults to null)
30198  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30199  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30200  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30201  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30202  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30203  * (defaults to null with no animation)
30204  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30205  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30206  * property for valid values (defaults to 'all')
30207  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30208  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30209  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30210  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30211  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30212  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30213  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30214  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30215  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30216  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30217  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30218  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30219  * draggable = true (defaults to false)
30220  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30221  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30222  * shadow (defaults to false)
30223  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30224  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30225  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30226  * @cfg {Array} buttons Array of buttons
30227  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30228  * @constructor
30229  * Create a new BasicDialog.
30230  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30231  * @param {Object} config Configuration options
30232  */
30233 Roo.BasicDialog = function(el, config){
30234     this.el = Roo.get(el);
30235     var dh = Roo.DomHelper;
30236     if(!this.el && config && config.autoCreate){
30237         if(typeof config.autoCreate == "object"){
30238             if(!config.autoCreate.id){
30239                 config.autoCreate.id = el;
30240             }
30241             this.el = dh.append(document.body,
30242                         config.autoCreate, true);
30243         }else{
30244             this.el = dh.append(document.body,
30245                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30246         }
30247     }
30248     el = this.el;
30249     el.setDisplayed(true);
30250     el.hide = this.hideAction;
30251     this.id = el.id;
30252     el.addClass("x-dlg");
30253
30254     Roo.apply(this, config);
30255
30256     this.proxy = el.createProxy("x-dlg-proxy");
30257     this.proxy.hide = this.hideAction;
30258     this.proxy.setOpacity(.5);
30259     this.proxy.hide();
30260
30261     if(config.width){
30262         el.setWidth(config.width);
30263     }
30264     if(config.height){
30265         el.setHeight(config.height);
30266     }
30267     this.size = el.getSize();
30268     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30269         this.xy = [config.x,config.y];
30270     }else{
30271         this.xy = el.getCenterXY(true);
30272     }
30273     /** The header element @type Roo.Element */
30274     this.header = el.child("> .x-dlg-hd");
30275     /** The body element @type Roo.Element */
30276     this.body = el.child("> .x-dlg-bd");
30277     /** The footer element @type Roo.Element */
30278     this.footer = el.child("> .x-dlg-ft");
30279
30280     if(!this.header){
30281         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30282     }
30283     if(!this.body){
30284         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30285     }
30286
30287     this.header.unselectable();
30288     if(this.title){
30289         this.header.update(this.title);
30290     }
30291     // this element allows the dialog to be focused for keyboard event
30292     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30293     this.focusEl.swallowEvent("click", true);
30294
30295     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30296
30297     // wrap the body and footer for special rendering
30298     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30299     if(this.footer){
30300         this.bwrap.dom.appendChild(this.footer.dom);
30301     }
30302
30303     this.bg = this.el.createChild({
30304         tag: "div", cls:"x-dlg-bg",
30305         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30306     });
30307     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30308
30309
30310     if(this.autoScroll !== false && !this.autoTabs){
30311         this.body.setStyle("overflow", "auto");
30312     }
30313
30314     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30315
30316     if(this.closable !== false){
30317         this.el.addClass("x-dlg-closable");
30318         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30319         this.close.on("click", this.closeClick, this);
30320         this.close.addClassOnOver("x-dlg-close-over");
30321     }
30322     if(this.collapsible !== false){
30323         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30324         this.collapseBtn.on("click", this.collapseClick, this);
30325         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30326         this.header.on("dblclick", this.collapseClick, this);
30327     }
30328     if(this.resizable !== false){
30329         this.el.addClass("x-dlg-resizable");
30330         this.resizer = new Roo.Resizable(el, {
30331             minWidth: this.minWidth || 80,
30332             minHeight:this.minHeight || 80,
30333             handles: this.resizeHandles || "all",
30334             pinned: true
30335         });
30336         this.resizer.on("beforeresize", this.beforeResize, this);
30337         this.resizer.on("resize", this.onResize, this);
30338     }
30339     if(this.draggable !== false){
30340         el.addClass("x-dlg-draggable");
30341         if (!this.proxyDrag) {
30342             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30343         }
30344         else {
30345             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30346         }
30347         dd.setHandleElId(this.header.id);
30348         dd.endDrag = this.endMove.createDelegate(this);
30349         dd.startDrag = this.startMove.createDelegate(this);
30350         dd.onDrag = this.onDrag.createDelegate(this);
30351         dd.scroll = false;
30352         this.dd = dd;
30353     }
30354     if(this.modal){
30355         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30356         this.mask.enableDisplayMode("block");
30357         this.mask.hide();
30358         this.el.addClass("x-dlg-modal");
30359     }
30360     if(this.shadow){
30361         this.shadow = new Roo.Shadow({
30362             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30363             offset : this.shadowOffset
30364         });
30365     }else{
30366         this.shadowOffset = 0;
30367     }
30368     if(Roo.useShims && this.shim !== false){
30369         this.shim = this.el.createShim();
30370         this.shim.hide = this.hideAction;
30371         this.shim.hide();
30372     }else{
30373         this.shim = false;
30374     }
30375     if(this.autoTabs){
30376         this.initTabs();
30377     }
30378     if (this.buttons) { 
30379         var bts= this.buttons;
30380         this.buttons = [];
30381         Roo.each(bts, function(b) {
30382             this.addButton(b);
30383         }, this);
30384     }
30385     
30386     
30387     this.addEvents({
30388         /**
30389          * @event keydown
30390          * Fires when a key is pressed
30391          * @param {Roo.BasicDialog} this
30392          * @param {Roo.EventObject} e
30393          */
30394         "keydown" : true,
30395         /**
30396          * @event move
30397          * Fires when this dialog is moved by the user.
30398          * @param {Roo.BasicDialog} this
30399          * @param {Number} x The new page X
30400          * @param {Number} y The new page Y
30401          */
30402         "move" : true,
30403         /**
30404          * @event resize
30405          * Fires when this dialog is resized by the user.
30406          * @param {Roo.BasicDialog} this
30407          * @param {Number} width The new width
30408          * @param {Number} height The new height
30409          */
30410         "resize" : true,
30411         /**
30412          * @event beforehide
30413          * Fires before this dialog is hidden.
30414          * @param {Roo.BasicDialog} this
30415          */
30416         "beforehide" : true,
30417         /**
30418          * @event hide
30419          * Fires when this dialog is hidden.
30420          * @param {Roo.BasicDialog} this
30421          */
30422         "hide" : true,
30423         /**
30424          * @event beforeshow
30425          * Fires before this dialog is shown.
30426          * @param {Roo.BasicDialog} this
30427          */
30428         "beforeshow" : true,
30429         /**
30430          * @event show
30431          * Fires when this dialog is shown.
30432          * @param {Roo.BasicDialog} this
30433          */
30434         "show" : true
30435     });
30436     el.on("keydown", this.onKeyDown, this);
30437     el.on("mousedown", this.toFront, this);
30438     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30439     this.el.hide();
30440     Roo.DialogManager.register(this);
30441     Roo.BasicDialog.superclass.constructor.call(this);
30442 };
30443
30444 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30445     shadowOffset: Roo.isIE ? 6 : 5,
30446     minHeight: 80,
30447     minWidth: 200,
30448     minButtonWidth: 75,
30449     defaultButton: null,
30450     buttonAlign: "right",
30451     tabTag: 'div',
30452     firstShow: true,
30453
30454     /**
30455      * Sets the dialog title text
30456      * @param {String} text The title text to display
30457      * @return {Roo.BasicDialog} this
30458      */
30459     setTitle : function(text){
30460         this.header.update(text);
30461         return this;
30462     },
30463
30464     // private
30465     closeClick : function(){
30466         this.hide();
30467     },
30468
30469     // private
30470     collapseClick : function(){
30471         this[this.collapsed ? "expand" : "collapse"]();
30472     },
30473
30474     /**
30475      * Collapses the dialog to its minimized state (only the title bar is visible).
30476      * Equivalent to the user clicking the collapse dialog button.
30477      */
30478     collapse : function(){
30479         if(!this.collapsed){
30480             this.collapsed = true;
30481             this.el.addClass("x-dlg-collapsed");
30482             this.restoreHeight = this.el.getHeight();
30483             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30484         }
30485     },
30486
30487     /**
30488      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30489      * clicking the expand dialog button.
30490      */
30491     expand : function(){
30492         if(this.collapsed){
30493             this.collapsed = false;
30494             this.el.removeClass("x-dlg-collapsed");
30495             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30496         }
30497     },
30498
30499     /**
30500      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30501      * @return {Roo.TabPanel} The tabs component
30502      */
30503     initTabs : function(){
30504         var tabs = this.getTabs();
30505         while(tabs.getTab(0)){
30506             tabs.removeTab(0);
30507         }
30508         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30509             var dom = el.dom;
30510             tabs.addTab(Roo.id(dom), dom.title);
30511             dom.title = "";
30512         });
30513         tabs.activate(0);
30514         return tabs;
30515     },
30516
30517     // private
30518     beforeResize : function(){
30519         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30520     },
30521
30522     // private
30523     onResize : function(){
30524         this.refreshSize();
30525         this.syncBodyHeight();
30526         this.adjustAssets();
30527         this.focus();
30528         this.fireEvent("resize", this, this.size.width, this.size.height);
30529     },
30530
30531     // private
30532     onKeyDown : function(e){
30533         if(this.isVisible()){
30534             this.fireEvent("keydown", this, e);
30535         }
30536     },
30537
30538     /**
30539      * Resizes the dialog.
30540      * @param {Number} width
30541      * @param {Number} height
30542      * @return {Roo.BasicDialog} this
30543      */
30544     resizeTo : function(width, height){
30545         this.el.setSize(width, height);
30546         this.size = {width: width, height: height};
30547         this.syncBodyHeight();
30548         if(this.fixedcenter){
30549             this.center();
30550         }
30551         if(this.isVisible()){
30552             this.constrainXY();
30553             this.adjustAssets();
30554         }
30555         this.fireEvent("resize", this, width, height);
30556         return this;
30557     },
30558
30559
30560     /**
30561      * Resizes the dialog to fit the specified content size.
30562      * @param {Number} width
30563      * @param {Number} height
30564      * @return {Roo.BasicDialog} this
30565      */
30566     setContentSize : function(w, h){
30567         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30568         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30569         //if(!this.el.isBorderBox()){
30570             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30571             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30572         //}
30573         if(this.tabs){
30574             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30575             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30576         }
30577         this.resizeTo(w, h);
30578         return this;
30579     },
30580
30581     /**
30582      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30583      * executed in response to a particular key being pressed while the dialog is active.
30584      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30585      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30586      * @param {Function} fn The function to call
30587      * @param {Object} scope (optional) The scope of the function
30588      * @return {Roo.BasicDialog} this
30589      */
30590     addKeyListener : function(key, fn, scope){
30591         var keyCode, shift, ctrl, alt;
30592         if(typeof key == "object" && !(key instanceof Array)){
30593             keyCode = key["key"];
30594             shift = key["shift"];
30595             ctrl = key["ctrl"];
30596             alt = key["alt"];
30597         }else{
30598             keyCode = key;
30599         }
30600         var handler = function(dlg, e){
30601             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30602                 var k = e.getKey();
30603                 if(keyCode instanceof Array){
30604                     for(var i = 0, len = keyCode.length; i < len; i++){
30605                         if(keyCode[i] == k){
30606                           fn.call(scope || window, dlg, k, e);
30607                           return;
30608                         }
30609                     }
30610                 }else{
30611                     if(k == keyCode){
30612                         fn.call(scope || window, dlg, k, e);
30613                     }
30614                 }
30615             }
30616         };
30617         this.on("keydown", handler);
30618         return this;
30619     },
30620
30621     /**
30622      * Returns the TabPanel component (creates it if it doesn't exist).
30623      * Note: If you wish to simply check for the existence of tabs without creating them,
30624      * check for a null 'tabs' property.
30625      * @return {Roo.TabPanel} The tabs component
30626      */
30627     getTabs : function(){
30628         if(!this.tabs){
30629             this.el.addClass("x-dlg-auto-tabs");
30630             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30631             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30632         }
30633         return this.tabs;
30634     },
30635
30636     /**
30637      * Adds a button to the footer section of the dialog.
30638      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30639      * object or a valid Roo.DomHelper element config
30640      * @param {Function} handler The function called when the button is clicked
30641      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30642      * @return {Roo.Button} The new button
30643      */
30644     addButton : function(config, handler, scope){
30645         var dh = Roo.DomHelper;
30646         if(!this.footer){
30647             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30648         }
30649         if(!this.btnContainer){
30650             var tb = this.footer.createChild({
30651
30652                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30653                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30654             }, null, true);
30655             this.btnContainer = tb.firstChild.firstChild.firstChild;
30656         }
30657         var bconfig = {
30658             handler: handler,
30659             scope: scope,
30660             minWidth: this.minButtonWidth,
30661             hideParent:true
30662         };
30663         if(typeof config == "string"){
30664             bconfig.text = config;
30665         }else{
30666             if(config.tag){
30667                 bconfig.dhconfig = config;
30668             }else{
30669                 Roo.apply(bconfig, config);
30670             }
30671         }
30672         var fc = false;
30673         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30674             bconfig.position = Math.max(0, bconfig.position);
30675             fc = this.btnContainer.childNodes[bconfig.position];
30676         }
30677          
30678         var btn = new Roo.Button(
30679             fc ? 
30680                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30681                 : this.btnContainer.appendChild(document.createElement("td")),
30682             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30683             bconfig
30684         );
30685         this.syncBodyHeight();
30686         if(!this.buttons){
30687             /**
30688              * Array of all the buttons that have been added to this dialog via addButton
30689              * @type Array
30690              */
30691             this.buttons = [];
30692         }
30693         this.buttons.push(btn);
30694         return btn;
30695     },
30696
30697     /**
30698      * Sets the default button to be focused when the dialog is displayed.
30699      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30700      * @return {Roo.BasicDialog} this
30701      */
30702     setDefaultButton : function(btn){
30703         this.defaultButton = btn;
30704         return this;
30705     },
30706
30707     // private
30708     getHeaderFooterHeight : function(safe){
30709         var height = 0;
30710         if(this.header){
30711            height += this.header.getHeight();
30712         }
30713         if(this.footer){
30714            var fm = this.footer.getMargins();
30715             height += (this.footer.getHeight()+fm.top+fm.bottom);
30716         }
30717         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30718         height += this.centerBg.getPadding("tb");
30719         return height;
30720     },
30721
30722     // private
30723     syncBodyHeight : function()
30724     {
30725         var bd = this.body, // the text
30726             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30727             bw = this.bwrap;
30728         var height = this.size.height - this.getHeaderFooterHeight(false);
30729         bd.setHeight(height-bd.getMargins("tb"));
30730         var hh = this.header.getHeight();
30731         var h = this.size.height-hh;
30732         cb.setHeight(h);
30733         
30734         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30735         bw.setHeight(h-cb.getPadding("tb"));
30736         
30737         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30738         bd.setWidth(bw.getWidth(true));
30739         if(this.tabs){
30740             this.tabs.syncHeight();
30741             if(Roo.isIE){
30742                 this.tabs.el.repaint();
30743             }
30744         }
30745     },
30746
30747     /**
30748      * Restores the previous state of the dialog if Roo.state is configured.
30749      * @return {Roo.BasicDialog} this
30750      */
30751     restoreState : function(){
30752         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30753         if(box && box.width){
30754             this.xy = [box.x, box.y];
30755             this.resizeTo(box.width, box.height);
30756         }
30757         return this;
30758     },
30759
30760     // private
30761     beforeShow : function(){
30762         this.expand();
30763         if(this.fixedcenter){
30764             this.xy = this.el.getCenterXY(true);
30765         }
30766         if(this.modal){
30767             Roo.get(document.body).addClass("x-body-masked");
30768             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30769             this.mask.show();
30770         }
30771         this.constrainXY();
30772     },
30773
30774     // private
30775     animShow : function(){
30776         var b = Roo.get(this.animateTarget).getBox();
30777         this.proxy.setSize(b.width, b.height);
30778         this.proxy.setLocation(b.x, b.y);
30779         this.proxy.show();
30780         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30781                     true, .35, this.showEl.createDelegate(this));
30782     },
30783
30784     /**
30785      * Shows the dialog.
30786      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30787      * @return {Roo.BasicDialog} this
30788      */
30789     show : function(animateTarget){
30790         if (this.fireEvent("beforeshow", this) === false){
30791             return;
30792         }
30793         if(this.syncHeightBeforeShow){
30794             this.syncBodyHeight();
30795         }else if(this.firstShow){
30796             this.firstShow = false;
30797             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30798         }
30799         this.animateTarget = animateTarget || this.animateTarget;
30800         if(!this.el.isVisible()){
30801             this.beforeShow();
30802             if(this.animateTarget && Roo.get(this.animateTarget)){
30803                 this.animShow();
30804             }else{
30805                 this.showEl();
30806             }
30807         }
30808         return this;
30809     },
30810
30811     // private
30812     showEl : function(){
30813         this.proxy.hide();
30814         this.el.setXY(this.xy);
30815         this.el.show();
30816         this.adjustAssets(true);
30817         this.toFront();
30818         this.focus();
30819         // IE peekaboo bug - fix found by Dave Fenwick
30820         if(Roo.isIE){
30821             this.el.repaint();
30822         }
30823         this.fireEvent("show", this);
30824     },
30825
30826     /**
30827      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30828      * dialog itself will receive focus.
30829      */
30830     focus : function(){
30831         if(this.defaultButton){
30832             this.defaultButton.focus();
30833         }else{
30834             this.focusEl.focus();
30835         }
30836     },
30837
30838     // private
30839     constrainXY : function(){
30840         if(this.constraintoviewport !== false){
30841             if(!this.viewSize){
30842                 if(this.container){
30843                     var s = this.container.getSize();
30844                     this.viewSize = [s.width, s.height];
30845                 }else{
30846                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30847                 }
30848             }
30849             var s = Roo.get(this.container||document).getScroll();
30850
30851             var x = this.xy[0], y = this.xy[1];
30852             var w = this.size.width, h = this.size.height;
30853             var vw = this.viewSize[0], vh = this.viewSize[1];
30854             // only move it if it needs it
30855             var moved = false;
30856             // first validate right/bottom
30857             if(x + w > vw+s.left){
30858                 x = vw - w;
30859                 moved = true;
30860             }
30861             if(y + h > vh+s.top){
30862                 y = vh - h;
30863                 moved = true;
30864             }
30865             // then make sure top/left isn't negative
30866             if(x < s.left){
30867                 x = s.left;
30868                 moved = true;
30869             }
30870             if(y < s.top){
30871                 y = s.top;
30872                 moved = true;
30873             }
30874             if(moved){
30875                 // cache xy
30876                 this.xy = [x, y];
30877                 if(this.isVisible()){
30878                     this.el.setLocation(x, y);
30879                     this.adjustAssets();
30880                 }
30881             }
30882         }
30883     },
30884
30885     // private
30886     onDrag : function(){
30887         if(!this.proxyDrag){
30888             this.xy = this.el.getXY();
30889             this.adjustAssets();
30890         }
30891     },
30892
30893     // private
30894     adjustAssets : function(doShow){
30895         var x = this.xy[0], y = this.xy[1];
30896         var w = this.size.width, h = this.size.height;
30897         if(doShow === true){
30898             if(this.shadow){
30899                 this.shadow.show(this.el);
30900             }
30901             if(this.shim){
30902                 this.shim.show();
30903             }
30904         }
30905         if(this.shadow && this.shadow.isVisible()){
30906             this.shadow.show(this.el);
30907         }
30908         if(this.shim && this.shim.isVisible()){
30909             this.shim.setBounds(x, y, w, h);
30910         }
30911     },
30912
30913     // private
30914     adjustViewport : function(w, h){
30915         if(!w || !h){
30916             w = Roo.lib.Dom.getViewWidth();
30917             h = Roo.lib.Dom.getViewHeight();
30918         }
30919         // cache the size
30920         this.viewSize = [w, h];
30921         if(this.modal && this.mask.isVisible()){
30922             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30923             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30924         }
30925         if(this.isVisible()){
30926             this.constrainXY();
30927         }
30928     },
30929
30930     /**
30931      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30932      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30933      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30934      */
30935     destroy : function(removeEl){
30936         if(this.isVisible()){
30937             this.animateTarget = null;
30938             this.hide();
30939         }
30940         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30941         if(this.tabs){
30942             this.tabs.destroy(removeEl);
30943         }
30944         Roo.destroy(
30945              this.shim,
30946              this.proxy,
30947              this.resizer,
30948              this.close,
30949              this.mask
30950         );
30951         if(this.dd){
30952             this.dd.unreg();
30953         }
30954         if(this.buttons){
30955            for(var i = 0, len = this.buttons.length; i < len; i++){
30956                this.buttons[i].destroy();
30957            }
30958         }
30959         this.el.removeAllListeners();
30960         if(removeEl === true){
30961             this.el.update("");
30962             this.el.remove();
30963         }
30964         Roo.DialogManager.unregister(this);
30965     },
30966
30967     // private
30968     startMove : function(){
30969         if(this.proxyDrag){
30970             this.proxy.show();
30971         }
30972         if(this.constraintoviewport !== false){
30973             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30974         }
30975     },
30976
30977     // private
30978     endMove : function(){
30979         if(!this.proxyDrag){
30980             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30981         }else{
30982             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30983             this.proxy.hide();
30984         }
30985         this.refreshSize();
30986         this.adjustAssets();
30987         this.focus();
30988         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30989     },
30990
30991     /**
30992      * Brings this dialog to the front of any other visible dialogs
30993      * @return {Roo.BasicDialog} this
30994      */
30995     toFront : function(){
30996         Roo.DialogManager.bringToFront(this);
30997         return this;
30998     },
30999
31000     /**
31001      * Sends this dialog to the back (under) of any other visible dialogs
31002      * @return {Roo.BasicDialog} this
31003      */
31004     toBack : function(){
31005         Roo.DialogManager.sendToBack(this);
31006         return this;
31007     },
31008
31009     /**
31010      * Centers this dialog in the viewport
31011      * @return {Roo.BasicDialog} this
31012      */
31013     center : function(){
31014         var xy = this.el.getCenterXY(true);
31015         this.moveTo(xy[0], xy[1]);
31016         return this;
31017     },
31018
31019     /**
31020      * Moves the dialog's top-left corner to the specified point
31021      * @param {Number} x
31022      * @param {Number} y
31023      * @return {Roo.BasicDialog} this
31024      */
31025     moveTo : function(x, y){
31026         this.xy = [x,y];
31027         if(this.isVisible()){
31028             this.el.setXY(this.xy);
31029             this.adjustAssets();
31030         }
31031         return this;
31032     },
31033
31034     /**
31035      * Aligns the dialog to the specified element
31036      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31037      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31038      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31039      * @return {Roo.BasicDialog} this
31040      */
31041     alignTo : function(element, position, offsets){
31042         this.xy = this.el.getAlignToXY(element, position, offsets);
31043         if(this.isVisible()){
31044             this.el.setXY(this.xy);
31045             this.adjustAssets();
31046         }
31047         return this;
31048     },
31049
31050     /**
31051      * Anchors an element to another element and realigns it when the window is resized.
31052      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31053      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31054      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31055      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31056      * is a number, it is used as the buffer delay (defaults to 50ms).
31057      * @return {Roo.BasicDialog} this
31058      */
31059     anchorTo : function(el, alignment, offsets, monitorScroll){
31060         var action = function(){
31061             this.alignTo(el, alignment, offsets);
31062         };
31063         Roo.EventManager.onWindowResize(action, this);
31064         var tm = typeof monitorScroll;
31065         if(tm != 'undefined'){
31066             Roo.EventManager.on(window, 'scroll', action, this,
31067                 {buffer: tm == 'number' ? monitorScroll : 50});
31068         }
31069         action.call(this);
31070         return this;
31071     },
31072
31073     /**
31074      * Returns true if the dialog is visible
31075      * @return {Boolean}
31076      */
31077     isVisible : function(){
31078         return this.el.isVisible();
31079     },
31080
31081     // private
31082     animHide : function(callback){
31083         var b = Roo.get(this.animateTarget).getBox();
31084         this.proxy.show();
31085         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31086         this.el.hide();
31087         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31088                     this.hideEl.createDelegate(this, [callback]));
31089     },
31090
31091     /**
31092      * Hides the dialog.
31093      * @param {Function} callback (optional) Function to call when the dialog is hidden
31094      * @return {Roo.BasicDialog} this
31095      */
31096     hide : function(callback){
31097         if (this.fireEvent("beforehide", this) === false){
31098             return;
31099         }
31100         if(this.shadow){
31101             this.shadow.hide();
31102         }
31103         if(this.shim) {
31104           this.shim.hide();
31105         }
31106         // sometimes animateTarget seems to get set.. causing problems...
31107         // this just double checks..
31108         if(this.animateTarget && Roo.get(this.animateTarget)) {
31109            this.animHide(callback);
31110         }else{
31111             this.el.hide();
31112             this.hideEl(callback);
31113         }
31114         return this;
31115     },
31116
31117     // private
31118     hideEl : function(callback){
31119         this.proxy.hide();
31120         if(this.modal){
31121             this.mask.hide();
31122             Roo.get(document.body).removeClass("x-body-masked");
31123         }
31124         this.fireEvent("hide", this);
31125         if(typeof callback == "function"){
31126             callback();
31127         }
31128     },
31129
31130     // private
31131     hideAction : function(){
31132         this.setLeft("-10000px");
31133         this.setTop("-10000px");
31134         this.setStyle("visibility", "hidden");
31135     },
31136
31137     // private
31138     refreshSize : function(){
31139         this.size = this.el.getSize();
31140         this.xy = this.el.getXY();
31141         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31142     },
31143
31144     // private
31145     // z-index is managed by the DialogManager and may be overwritten at any time
31146     setZIndex : function(index){
31147         if(this.modal){
31148             this.mask.setStyle("z-index", index);
31149         }
31150         if(this.shim){
31151             this.shim.setStyle("z-index", ++index);
31152         }
31153         if(this.shadow){
31154             this.shadow.setZIndex(++index);
31155         }
31156         this.el.setStyle("z-index", ++index);
31157         if(this.proxy){
31158             this.proxy.setStyle("z-index", ++index);
31159         }
31160         if(this.resizer){
31161             this.resizer.proxy.setStyle("z-index", ++index);
31162         }
31163
31164         this.lastZIndex = index;
31165     },
31166
31167     /**
31168      * Returns the element for this dialog
31169      * @return {Roo.Element} The underlying dialog Element
31170      */
31171     getEl : function(){
31172         return this.el;
31173     }
31174 });
31175
31176 /**
31177  * @class Roo.DialogManager
31178  * Provides global access to BasicDialogs that have been created and
31179  * support for z-indexing (layering) multiple open dialogs.
31180  */
31181 Roo.DialogManager = function(){
31182     var list = {};
31183     var accessList = [];
31184     var front = null;
31185
31186     // private
31187     var sortDialogs = function(d1, d2){
31188         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31189     };
31190
31191     // private
31192     var orderDialogs = function(){
31193         accessList.sort(sortDialogs);
31194         var seed = Roo.DialogManager.zseed;
31195         for(var i = 0, len = accessList.length; i < len; i++){
31196             var dlg = accessList[i];
31197             if(dlg){
31198                 dlg.setZIndex(seed + (i*10));
31199             }
31200         }
31201     };
31202
31203     return {
31204         /**
31205          * The starting z-index for BasicDialogs (defaults to 9000)
31206          * @type Number The z-index value
31207          */
31208         zseed : 9000,
31209
31210         // private
31211         register : function(dlg){
31212             list[dlg.id] = dlg;
31213             accessList.push(dlg);
31214         },
31215
31216         // private
31217         unregister : function(dlg){
31218             delete list[dlg.id];
31219             var i=0;
31220             var len=0;
31221             if(!accessList.indexOf){
31222                 for(  i = 0, len = accessList.length; i < len; i++){
31223                     if(accessList[i] == dlg){
31224                         accessList.splice(i, 1);
31225                         return;
31226                     }
31227                 }
31228             }else{
31229                  i = accessList.indexOf(dlg);
31230                 if(i != -1){
31231                     accessList.splice(i, 1);
31232                 }
31233             }
31234         },
31235
31236         /**
31237          * Gets a registered dialog by id
31238          * @param {String/Object} id The id of the dialog or a dialog
31239          * @return {Roo.BasicDialog} this
31240          */
31241         get : function(id){
31242             return typeof id == "object" ? id : list[id];
31243         },
31244
31245         /**
31246          * Brings the specified dialog to the front
31247          * @param {String/Object} dlg The id of the dialog or a dialog
31248          * @return {Roo.BasicDialog} this
31249          */
31250         bringToFront : function(dlg){
31251             dlg = this.get(dlg);
31252             if(dlg != front){
31253                 front = dlg;
31254                 dlg._lastAccess = new Date().getTime();
31255                 orderDialogs();
31256             }
31257             return dlg;
31258         },
31259
31260         /**
31261          * Sends the specified dialog to the back
31262          * @param {String/Object} dlg The id of the dialog or a dialog
31263          * @return {Roo.BasicDialog} this
31264          */
31265         sendToBack : function(dlg){
31266             dlg = this.get(dlg);
31267             dlg._lastAccess = -(new Date().getTime());
31268             orderDialogs();
31269             return dlg;
31270         },
31271
31272         /**
31273          * Hides all dialogs
31274          */
31275         hideAll : function(){
31276             for(var id in list){
31277                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31278                     list[id].hide();
31279                 }
31280             }
31281         }
31282     };
31283 }();
31284
31285 /**
31286  * @class Roo.LayoutDialog
31287  * @extends Roo.BasicDialog
31288  * Dialog which provides adjustments for working with a layout in a Dialog.
31289  * Add your necessary layout config options to the dialog's config.<br>
31290  * Example usage (including a nested layout):
31291  * <pre><code>
31292 if(!dialog){
31293     dialog = new Roo.LayoutDialog("download-dlg", {
31294         modal: true,
31295         width:600,
31296         height:450,
31297         shadow:true,
31298         minWidth:500,
31299         minHeight:350,
31300         autoTabs:true,
31301         proxyDrag:true,
31302         // layout config merges with the dialog config
31303         center:{
31304             tabPosition: "top",
31305             alwaysShowTabs: true
31306         }
31307     });
31308     dialog.addKeyListener(27, dialog.hide, dialog);
31309     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31310     dialog.addButton("Build It!", this.getDownload, this);
31311
31312     // we can even add nested layouts
31313     var innerLayout = new Roo.BorderLayout("dl-inner", {
31314         east: {
31315             initialSize: 200,
31316             autoScroll:true,
31317             split:true
31318         },
31319         center: {
31320             autoScroll:true
31321         }
31322     });
31323     innerLayout.beginUpdate();
31324     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31325     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31326     innerLayout.endUpdate(true);
31327
31328     var layout = dialog.getLayout();
31329     layout.beginUpdate();
31330     layout.add("center", new Roo.ContentPanel("standard-panel",
31331                         {title: "Download the Source", fitToFrame:true}));
31332     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31333                {title: "Build your own roo.js"}));
31334     layout.getRegion("center").showPanel(sp);
31335     layout.endUpdate();
31336 }
31337 </code></pre>
31338     * @constructor
31339     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31340     * @param {Object} config configuration options
31341   */
31342 Roo.LayoutDialog = function(el, cfg){
31343     
31344     var config=  cfg;
31345     if (typeof(cfg) == 'undefined') {
31346         config = Roo.apply({}, el);
31347         // not sure why we use documentElement here.. - it should always be body.
31348         // IE7 borks horribly if we use documentElement.
31349         // webkit also does not like documentElement - it creates a body element...
31350         el = Roo.get( document.body || document.documentElement ).createChild();
31351         //config.autoCreate = true;
31352     }
31353     
31354     
31355     config.autoTabs = false;
31356     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31357     this.body.setStyle({overflow:"hidden", position:"relative"});
31358     this.layout = new Roo.BorderLayout(this.body.dom, config);
31359     this.layout.monitorWindowResize = false;
31360     this.el.addClass("x-dlg-auto-layout");
31361     // fix case when center region overwrites center function
31362     this.center = Roo.BasicDialog.prototype.center;
31363     this.on("show", this.layout.layout, this.layout, true);
31364     if (config.items) {
31365         var xitems = config.items;
31366         delete config.items;
31367         Roo.each(xitems, this.addxtype, this);
31368     }
31369     
31370     
31371 };
31372 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31373     /**
31374      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31375      * @deprecated
31376      */
31377     endUpdate : function(){
31378         this.layout.endUpdate();
31379     },
31380
31381     /**
31382      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31383      *  @deprecated
31384      */
31385     beginUpdate : function(){
31386         this.layout.beginUpdate();
31387     },
31388
31389     /**
31390      * Get the BorderLayout for this dialog
31391      * @return {Roo.BorderLayout}
31392      */
31393     getLayout : function(){
31394         return this.layout;
31395     },
31396
31397     showEl : function(){
31398         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31399         if(Roo.isIE7){
31400             this.layout.layout();
31401         }
31402     },
31403
31404     // private
31405     // Use the syncHeightBeforeShow config option to control this automatically
31406     syncBodyHeight : function(){
31407         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31408         if(this.layout){this.layout.layout();}
31409     },
31410     
31411       /**
31412      * Add an xtype element (actually adds to the layout.)
31413      * @return {Object} xdata xtype object data.
31414      */
31415     
31416     addxtype : function(c) {
31417         return this.layout.addxtype(c);
31418     }
31419 });/*
31420  * Based on:
31421  * Ext JS Library 1.1.1
31422  * Copyright(c) 2006-2007, Ext JS, LLC.
31423  *
31424  * Originally Released Under LGPL - original licence link has changed is not relivant.
31425  *
31426  * Fork - LGPL
31427  * <script type="text/javascript">
31428  */
31429  
31430 /**
31431  * @class Roo.MessageBox
31432  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31433  * Example usage:
31434  *<pre><code>
31435 // Basic alert:
31436 Roo.Msg.alert('Status', 'Changes saved successfully.');
31437
31438 // Prompt for user data:
31439 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31440     if (btn == 'ok'){
31441         // process text value...
31442     }
31443 });
31444
31445 // Show a dialog using config options:
31446 Roo.Msg.show({
31447    title:'Save Changes?',
31448    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31449    buttons: Roo.Msg.YESNOCANCEL,
31450    fn: processResult,
31451    animEl: 'elId'
31452 });
31453 </code></pre>
31454  * @singleton
31455  */
31456 Roo.MessageBox = function(){
31457     var dlg, opt, mask, waitTimer;
31458     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31459     var buttons, activeTextEl, bwidth;
31460
31461     // private
31462     var handleButton = function(button){
31463         dlg.hide();
31464         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31465     };
31466
31467     // private
31468     var handleHide = function(){
31469         if(opt && opt.cls){
31470             dlg.el.removeClass(opt.cls);
31471         }
31472         if(waitTimer){
31473             Roo.TaskMgr.stop(waitTimer);
31474             waitTimer = null;
31475         }
31476     };
31477
31478     // private
31479     var updateButtons = function(b){
31480         var width = 0;
31481         if(!b){
31482             buttons["ok"].hide();
31483             buttons["cancel"].hide();
31484             buttons["yes"].hide();
31485             buttons["no"].hide();
31486             dlg.footer.dom.style.display = 'none';
31487             return width;
31488         }
31489         dlg.footer.dom.style.display = '';
31490         for(var k in buttons){
31491             if(typeof buttons[k] != "function"){
31492                 if(b[k]){
31493                     buttons[k].show();
31494                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31495                     width += buttons[k].el.getWidth()+15;
31496                 }else{
31497                     buttons[k].hide();
31498                 }
31499             }
31500         }
31501         return width;
31502     };
31503
31504     // private
31505     var handleEsc = function(d, k, e){
31506         if(opt && opt.closable !== false){
31507             dlg.hide();
31508         }
31509         if(e){
31510             e.stopEvent();
31511         }
31512     };
31513
31514     return {
31515         /**
31516          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31517          * @return {Roo.BasicDialog} The BasicDialog element
31518          */
31519         getDialog : function(){
31520            if(!dlg){
31521                 dlg = new Roo.BasicDialog("x-msg-box", {
31522                     autoCreate : true,
31523                     shadow: true,
31524                     draggable: true,
31525                     resizable:false,
31526                     constraintoviewport:false,
31527                     fixedcenter:true,
31528                     collapsible : false,
31529                     shim:true,
31530                     modal: true,
31531                     width:400, height:100,
31532                     buttonAlign:"center",
31533                     closeClick : function(){
31534                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31535                             handleButton("no");
31536                         }else{
31537                             handleButton("cancel");
31538                         }
31539                     }
31540                 });
31541                 dlg.on("hide", handleHide);
31542                 mask = dlg.mask;
31543                 dlg.addKeyListener(27, handleEsc);
31544                 buttons = {};
31545                 var bt = this.buttonText;
31546                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31547                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31548                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31549                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31550                 bodyEl = dlg.body.createChild({
31551
31552                     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>'
31553                 });
31554                 msgEl = bodyEl.dom.firstChild;
31555                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31556                 textboxEl.enableDisplayMode();
31557                 textboxEl.addKeyListener([10,13], function(){
31558                     if(dlg.isVisible() && opt && opt.buttons){
31559                         if(opt.buttons.ok){
31560                             handleButton("ok");
31561                         }else if(opt.buttons.yes){
31562                             handleButton("yes");
31563                         }
31564                     }
31565                 });
31566                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31567                 textareaEl.enableDisplayMode();
31568                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31569                 progressEl.enableDisplayMode();
31570                 var pf = progressEl.dom.firstChild;
31571                 if (pf) {
31572                     pp = Roo.get(pf.firstChild);
31573                     pp.setHeight(pf.offsetHeight);
31574                 }
31575                 
31576             }
31577             return dlg;
31578         },
31579
31580         /**
31581          * Updates the message box body text
31582          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31583          * the XHTML-compliant non-breaking space character '&amp;#160;')
31584          * @return {Roo.MessageBox} This message box
31585          */
31586         updateText : function(text){
31587             if(!dlg.isVisible() && !opt.width){
31588                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31589             }
31590             msgEl.innerHTML = text || '&#160;';
31591       
31592             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31593             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31594             var w = Math.max(
31595                     Math.min(opt.width || cw , this.maxWidth), 
31596                     Math.max(opt.minWidth || this.minWidth, bwidth)
31597             );
31598             if(opt.prompt){
31599                 activeTextEl.setWidth(w);
31600             }
31601             if(dlg.isVisible()){
31602                 dlg.fixedcenter = false;
31603             }
31604             // to big, make it scroll. = But as usual stupid IE does not support
31605             // !important..
31606             
31607             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31608                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31609                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31610             } else {
31611                 bodyEl.dom.style.height = '';
31612                 bodyEl.dom.style.overflowY = '';
31613             }
31614             if (cw > w) {
31615                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31616             } else {
31617                 bodyEl.dom.style.overflowX = '';
31618             }
31619             
31620             dlg.setContentSize(w, bodyEl.getHeight());
31621             if(dlg.isVisible()){
31622                 dlg.fixedcenter = true;
31623             }
31624             return this;
31625         },
31626
31627         /**
31628          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31629          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31630          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31631          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31632          * @return {Roo.MessageBox} This message box
31633          */
31634         updateProgress : function(value, text){
31635             if(text){
31636                 this.updateText(text);
31637             }
31638             if (pp) { // weird bug on my firefox - for some reason this is not defined
31639                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31640             }
31641             return this;
31642         },        
31643
31644         /**
31645          * Returns true if the message box is currently displayed
31646          * @return {Boolean} True if the message box is visible, else false
31647          */
31648         isVisible : function(){
31649             return dlg && dlg.isVisible();  
31650         },
31651
31652         /**
31653          * Hides the message box if it is displayed
31654          */
31655         hide : function(){
31656             if(this.isVisible()){
31657                 dlg.hide();
31658             }  
31659         },
31660
31661         /**
31662          * Displays a new message box, or reinitializes an existing message box, based on the config options
31663          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31664          * The following config object properties are supported:
31665          * <pre>
31666 Property    Type             Description
31667 ----------  ---------------  ------------------------------------------------------------------------------------
31668 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31669                                    closes (defaults to undefined)
31670 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31671                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31672 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31673                                    progress and wait dialogs will ignore this property and always hide the
31674                                    close button as they can only be closed programmatically.
31675 cls               String           A custom CSS class to apply to the message box element
31676 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31677                                    displayed (defaults to 75)
31678 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31679                                    function will be btn (the name of the button that was clicked, if applicable,
31680                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31681                                    Progress and wait dialogs will ignore this option since they do not respond to
31682                                    user actions and can only be closed programmatically, so any required function
31683                                    should be called by the same code after it closes the dialog.
31684 icon              String           A CSS class that provides a background image to be used as an icon for
31685                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31686 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31687 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31688 modal             Boolean          False to allow user interaction with the page while the message box is
31689                                    displayed (defaults to true)
31690 msg               String           A string that will replace the existing message box body text (defaults
31691                                    to the XHTML-compliant non-breaking space character '&#160;')
31692 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31693 progress          Boolean          True to display a progress bar (defaults to false)
31694 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31695 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31696 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31697 title             String           The title text
31698 value             String           The string value to set into the active textbox element if displayed
31699 wait              Boolean          True to display a progress bar (defaults to false)
31700 width             Number           The width of the dialog in pixels
31701 </pre>
31702          *
31703          * Example usage:
31704          * <pre><code>
31705 Roo.Msg.show({
31706    title: 'Address',
31707    msg: 'Please enter your address:',
31708    width: 300,
31709    buttons: Roo.MessageBox.OKCANCEL,
31710    multiline: true,
31711    fn: saveAddress,
31712    animEl: 'addAddressBtn'
31713 });
31714 </code></pre>
31715          * @param {Object} config Configuration options
31716          * @return {Roo.MessageBox} This message box
31717          */
31718         show : function(options)
31719         {
31720             
31721             // this causes nightmares if you show one dialog after another
31722             // especially on callbacks..
31723              
31724             if(this.isVisible()){
31725                 
31726                 this.hide();
31727                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31728                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31729                 Roo.log("New Dialog Message:" +  options.msg )
31730                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31731                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31732                 
31733             }
31734             var d = this.getDialog();
31735             opt = options;
31736             d.setTitle(opt.title || "&#160;");
31737             d.close.setDisplayed(opt.closable !== false);
31738             activeTextEl = textboxEl;
31739             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31740             if(opt.prompt){
31741                 if(opt.multiline){
31742                     textboxEl.hide();
31743                     textareaEl.show();
31744                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31745                         opt.multiline : this.defaultTextHeight);
31746                     activeTextEl = textareaEl;
31747                 }else{
31748                     textboxEl.show();
31749                     textareaEl.hide();
31750                 }
31751             }else{
31752                 textboxEl.hide();
31753                 textareaEl.hide();
31754             }
31755             progressEl.setDisplayed(opt.progress === true);
31756             this.updateProgress(0);
31757             activeTextEl.dom.value = opt.value || "";
31758             if(opt.prompt){
31759                 dlg.setDefaultButton(activeTextEl);
31760             }else{
31761                 var bs = opt.buttons;
31762                 var db = null;
31763                 if(bs && bs.ok){
31764                     db = buttons["ok"];
31765                 }else if(bs && bs.yes){
31766                     db = buttons["yes"];
31767                 }
31768                 dlg.setDefaultButton(db);
31769             }
31770             bwidth = updateButtons(opt.buttons);
31771             this.updateText(opt.msg);
31772             if(opt.cls){
31773                 d.el.addClass(opt.cls);
31774             }
31775             d.proxyDrag = opt.proxyDrag === true;
31776             d.modal = opt.modal !== false;
31777             d.mask = opt.modal !== false ? mask : false;
31778             if(!d.isVisible()){
31779                 // force it to the end of the z-index stack so it gets a cursor in FF
31780                 document.body.appendChild(dlg.el.dom);
31781                 d.animateTarget = null;
31782                 d.show(options.animEl);
31783             }
31784             return this;
31785         },
31786
31787         /**
31788          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31789          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31790          * and closing the message box when the process is complete.
31791          * @param {String} title The title bar text
31792          * @param {String} msg The message box body text
31793          * @return {Roo.MessageBox} This message box
31794          */
31795         progress : function(title, msg){
31796             this.show({
31797                 title : title,
31798                 msg : msg,
31799                 buttons: false,
31800                 progress:true,
31801                 closable:false,
31802                 minWidth: this.minProgressWidth,
31803                 modal : true
31804             });
31805             return this;
31806         },
31807
31808         /**
31809          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31810          * If a callback function is passed it will be called after the user clicks the button, and the
31811          * id of the button that was clicked will be passed as the only parameter to the callback
31812          * (could also be the top-right close button).
31813          * @param {String} title The title bar text
31814          * @param {String} msg The message box body text
31815          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31816          * @param {Object} scope (optional) The scope of the callback function
31817          * @return {Roo.MessageBox} This message box
31818          */
31819         alert : function(title, msg, fn, scope){
31820             this.show({
31821                 title : title,
31822                 msg : msg,
31823                 buttons: this.OK,
31824                 fn: fn,
31825                 scope : scope,
31826                 modal : true
31827             });
31828             return this;
31829         },
31830
31831         /**
31832          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31833          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31834          * You are responsible for closing the message box when the process is complete.
31835          * @param {String} msg The message box body text
31836          * @param {String} title (optional) The title bar text
31837          * @return {Roo.MessageBox} This message box
31838          */
31839         wait : function(msg, title){
31840             this.show({
31841                 title : title,
31842                 msg : msg,
31843                 buttons: false,
31844                 closable:false,
31845                 progress:true,
31846                 modal:true,
31847                 width:300,
31848                 wait:true
31849             });
31850             waitTimer = Roo.TaskMgr.start({
31851                 run: function(i){
31852                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31853                 },
31854                 interval: 1000
31855             });
31856             return this;
31857         },
31858
31859         /**
31860          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31861          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31862          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31863          * @param {String} title The title bar text
31864          * @param {String} msg The message box body text
31865          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31866          * @param {Object} scope (optional) The scope of the callback function
31867          * @return {Roo.MessageBox} This message box
31868          */
31869         confirm : function(title, msg, fn, scope){
31870             this.show({
31871                 title : title,
31872                 msg : msg,
31873                 buttons: this.YESNO,
31874                 fn: fn,
31875                 scope : scope,
31876                 modal : true
31877             });
31878             return this;
31879         },
31880
31881         /**
31882          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31883          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31884          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31885          * (could also be the top-right close button) and the text that was entered will be passed as the two
31886          * parameters to the callback.
31887          * @param {String} title The title bar text
31888          * @param {String} msg The message box body text
31889          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31890          * @param {Object} scope (optional) The scope of the callback function
31891          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31892          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31893          * @return {Roo.MessageBox} This message box
31894          */
31895         prompt : function(title, msg, fn, scope, multiline){
31896             this.show({
31897                 title : title,
31898                 msg : msg,
31899                 buttons: this.OKCANCEL,
31900                 fn: fn,
31901                 minWidth:250,
31902                 scope : scope,
31903                 prompt:true,
31904                 multiline: multiline,
31905                 modal : true
31906             });
31907             return this;
31908         },
31909
31910         /**
31911          * Button config that displays a single OK button
31912          * @type Object
31913          */
31914         OK : {ok:true},
31915         /**
31916          * Button config that displays Yes and No buttons
31917          * @type Object
31918          */
31919         YESNO : {yes:true, no:true},
31920         /**
31921          * Button config that displays OK and Cancel buttons
31922          * @type Object
31923          */
31924         OKCANCEL : {ok:true, cancel:true},
31925         /**
31926          * Button config that displays Yes, No and Cancel buttons
31927          * @type Object
31928          */
31929         YESNOCANCEL : {yes:true, no:true, cancel:true},
31930
31931         /**
31932          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31933          * @type Number
31934          */
31935         defaultTextHeight : 75,
31936         /**
31937          * The maximum width in pixels of the message box (defaults to 600)
31938          * @type Number
31939          */
31940         maxWidth : 600,
31941         /**
31942          * The minimum width in pixels of the message box (defaults to 100)
31943          * @type Number
31944          */
31945         minWidth : 100,
31946         /**
31947          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31948          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31949          * @type Number
31950          */
31951         minProgressWidth : 250,
31952         /**
31953          * An object containing the default button text strings that can be overriden for localized language support.
31954          * Supported properties are: ok, cancel, yes and no.
31955          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31956          * @type Object
31957          */
31958         buttonText : {
31959             ok : "OK",
31960             cancel : "Cancel",
31961             yes : "Yes",
31962             no : "No"
31963         }
31964     };
31965 }();
31966
31967 /**
31968  * Shorthand for {@link Roo.MessageBox}
31969  */
31970 Roo.Msg = Roo.MessageBox;/*
31971  * Based on:
31972  * Ext JS Library 1.1.1
31973  * Copyright(c) 2006-2007, Ext JS, LLC.
31974  *
31975  * Originally Released Under LGPL - original licence link has changed is not relivant.
31976  *
31977  * Fork - LGPL
31978  * <script type="text/javascript">
31979  */
31980 /**
31981  * @class Roo.QuickTips
31982  * Provides attractive and customizable tooltips for any element.
31983  * @singleton
31984  */
31985 Roo.QuickTips = function(){
31986     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31987     var ce, bd, xy, dd;
31988     var visible = false, disabled = true, inited = false;
31989     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31990     
31991     var onOver = function(e){
31992         if(disabled){
31993             return;
31994         }
31995         var t = e.getTarget();
31996         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31997             return;
31998         }
31999         if(ce && t == ce.el){
32000             clearTimeout(hideProc);
32001             return;
32002         }
32003         if(t && tagEls[t.id]){
32004             tagEls[t.id].el = t;
32005             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32006             return;
32007         }
32008         var ttp, et = Roo.fly(t);
32009         var ns = cfg.namespace;
32010         if(tm.interceptTitles && t.title){
32011             ttp = t.title;
32012             t.qtip = ttp;
32013             t.removeAttribute("title");
32014             e.preventDefault();
32015         }else{
32016             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32017         }
32018         if(ttp){
32019             showProc = show.defer(tm.showDelay, tm, [{
32020                 el: t, 
32021                 text: ttp, 
32022                 width: et.getAttributeNS(ns, cfg.width),
32023                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32024                 title: et.getAttributeNS(ns, cfg.title),
32025                     cls: et.getAttributeNS(ns, cfg.cls)
32026             }]);
32027         }
32028     };
32029     
32030     var onOut = function(e){
32031         clearTimeout(showProc);
32032         var t = e.getTarget();
32033         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32034             hideProc = setTimeout(hide, tm.hideDelay);
32035         }
32036     };
32037     
32038     var onMove = function(e){
32039         if(disabled){
32040             return;
32041         }
32042         xy = e.getXY();
32043         xy[1] += 18;
32044         if(tm.trackMouse && ce){
32045             el.setXY(xy);
32046         }
32047     };
32048     
32049     var onDown = function(e){
32050         clearTimeout(showProc);
32051         clearTimeout(hideProc);
32052         if(!e.within(el)){
32053             if(tm.hideOnClick){
32054                 hide();
32055                 tm.disable();
32056                 tm.enable.defer(100, tm);
32057             }
32058         }
32059     };
32060     
32061     var getPad = function(){
32062         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32063     };
32064
32065     var show = function(o){
32066         if(disabled){
32067             return;
32068         }
32069         clearTimeout(dismissProc);
32070         ce = o;
32071         if(removeCls){ // in case manually hidden
32072             el.removeClass(removeCls);
32073             removeCls = null;
32074         }
32075         if(ce.cls){
32076             el.addClass(ce.cls);
32077             removeCls = ce.cls;
32078         }
32079         if(ce.title){
32080             tipTitle.update(ce.title);
32081             tipTitle.show();
32082         }else{
32083             tipTitle.update('');
32084             tipTitle.hide();
32085         }
32086         el.dom.style.width  = tm.maxWidth+'px';
32087         //tipBody.dom.style.width = '';
32088         tipBodyText.update(o.text);
32089         var p = getPad(), w = ce.width;
32090         if(!w){
32091             var td = tipBodyText.dom;
32092             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32093             if(aw > tm.maxWidth){
32094                 w = tm.maxWidth;
32095             }else if(aw < tm.minWidth){
32096                 w = tm.minWidth;
32097             }else{
32098                 w = aw;
32099             }
32100         }
32101         //tipBody.setWidth(w);
32102         el.setWidth(parseInt(w, 10) + p);
32103         if(ce.autoHide === false){
32104             close.setDisplayed(true);
32105             if(dd){
32106                 dd.unlock();
32107             }
32108         }else{
32109             close.setDisplayed(false);
32110             if(dd){
32111                 dd.lock();
32112             }
32113         }
32114         if(xy){
32115             el.avoidY = xy[1]-18;
32116             el.setXY(xy);
32117         }
32118         if(tm.animate){
32119             el.setOpacity(.1);
32120             el.setStyle("visibility", "visible");
32121             el.fadeIn({callback: afterShow});
32122         }else{
32123             afterShow();
32124         }
32125     };
32126     
32127     var afterShow = function(){
32128         if(ce){
32129             el.show();
32130             esc.enable();
32131             if(tm.autoDismiss && ce.autoHide !== false){
32132                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32133             }
32134         }
32135     };
32136     
32137     var hide = function(noanim){
32138         clearTimeout(dismissProc);
32139         clearTimeout(hideProc);
32140         ce = null;
32141         if(el.isVisible()){
32142             esc.disable();
32143             if(noanim !== true && tm.animate){
32144                 el.fadeOut({callback: afterHide});
32145             }else{
32146                 afterHide();
32147             } 
32148         }
32149     };
32150     
32151     var afterHide = function(){
32152         el.hide();
32153         if(removeCls){
32154             el.removeClass(removeCls);
32155             removeCls = null;
32156         }
32157     };
32158     
32159     return {
32160         /**
32161         * @cfg {Number} minWidth
32162         * The minimum width of the quick tip (defaults to 40)
32163         */
32164        minWidth : 40,
32165         /**
32166         * @cfg {Number} maxWidth
32167         * The maximum width of the quick tip (defaults to 300)
32168         */
32169        maxWidth : 300,
32170         /**
32171         * @cfg {Boolean} interceptTitles
32172         * True to automatically use the element's DOM title value if available (defaults to false)
32173         */
32174        interceptTitles : false,
32175         /**
32176         * @cfg {Boolean} trackMouse
32177         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32178         */
32179        trackMouse : false,
32180         /**
32181         * @cfg {Boolean} hideOnClick
32182         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32183         */
32184        hideOnClick : true,
32185         /**
32186         * @cfg {Number} showDelay
32187         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32188         */
32189        showDelay : 500,
32190         /**
32191         * @cfg {Number} hideDelay
32192         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32193         */
32194        hideDelay : 200,
32195         /**
32196         * @cfg {Boolean} autoHide
32197         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32198         * Used in conjunction with hideDelay.
32199         */
32200        autoHide : true,
32201         /**
32202         * @cfg {Boolean}
32203         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32204         * (defaults to true).  Used in conjunction with autoDismissDelay.
32205         */
32206        autoDismiss : true,
32207         /**
32208         * @cfg {Number}
32209         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32210         */
32211        autoDismissDelay : 5000,
32212        /**
32213         * @cfg {Boolean} animate
32214         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32215         */
32216        animate : false,
32217
32218        /**
32219         * @cfg {String} title
32220         * Title text to display (defaults to '').  This can be any valid HTML markup.
32221         */
32222         title: '',
32223        /**
32224         * @cfg {String} text
32225         * Body text to display (defaults to '').  This can be any valid HTML markup.
32226         */
32227         text : '',
32228        /**
32229         * @cfg {String} cls
32230         * A CSS class to apply to the base quick tip element (defaults to '').
32231         */
32232         cls : '',
32233        /**
32234         * @cfg {Number} width
32235         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32236         * minWidth or maxWidth.
32237         */
32238         width : null,
32239
32240     /**
32241      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32242      * or display QuickTips in a page.
32243      */
32244        init : function(){
32245           tm = Roo.QuickTips;
32246           cfg = tm.tagConfig;
32247           if(!inited){
32248               if(!Roo.isReady){ // allow calling of init() before onReady
32249                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32250                   return;
32251               }
32252               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32253               el.fxDefaults = {stopFx: true};
32254               // maximum custom styling
32255               //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>');
32256               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>');              
32257               tipTitle = el.child('h3');
32258               tipTitle.enableDisplayMode("block");
32259               tipBody = el.child('div.x-tip-bd');
32260               tipBodyText = el.child('div.x-tip-bd-inner');
32261               //bdLeft = el.child('div.x-tip-bd-left');
32262               //bdRight = el.child('div.x-tip-bd-right');
32263               close = el.child('div.x-tip-close');
32264               close.enableDisplayMode("block");
32265               close.on("click", hide);
32266               var d = Roo.get(document);
32267               d.on("mousedown", onDown);
32268               d.on("mouseover", onOver);
32269               d.on("mouseout", onOut);
32270               d.on("mousemove", onMove);
32271               esc = d.addKeyListener(27, hide);
32272               esc.disable();
32273               if(Roo.dd.DD){
32274                   dd = el.initDD("default", null, {
32275                       onDrag : function(){
32276                           el.sync();  
32277                       }
32278                   });
32279                   dd.setHandleElId(tipTitle.id);
32280                   dd.lock();
32281               }
32282               inited = true;
32283           }
32284           this.enable(); 
32285        },
32286
32287     /**
32288      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32289      * are supported:
32290      * <pre>
32291 Property    Type                   Description
32292 ----------  ---------------------  ------------------------------------------------------------------------
32293 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32294      * </ul>
32295      * @param {Object} config The config object
32296      */
32297        register : function(config){
32298            var cs = config instanceof Array ? config : arguments;
32299            for(var i = 0, len = cs.length; i < len; i++) {
32300                var c = cs[i];
32301                var target = c.target;
32302                if(target){
32303                    if(target instanceof Array){
32304                        for(var j = 0, jlen = target.length; j < jlen; j++){
32305                            tagEls[target[j]] = c;
32306                        }
32307                    }else{
32308                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32309                    }
32310                }
32311            }
32312        },
32313
32314     /**
32315      * Removes this quick tip from its element and destroys it.
32316      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32317      */
32318        unregister : function(el){
32319            delete tagEls[Roo.id(el)];
32320        },
32321
32322     /**
32323      * Enable this quick tip.
32324      */
32325        enable : function(){
32326            if(inited && disabled){
32327                locks.pop();
32328                if(locks.length < 1){
32329                    disabled = false;
32330                }
32331            }
32332        },
32333
32334     /**
32335      * Disable this quick tip.
32336      */
32337        disable : function(){
32338           disabled = true;
32339           clearTimeout(showProc);
32340           clearTimeout(hideProc);
32341           clearTimeout(dismissProc);
32342           if(ce){
32343               hide(true);
32344           }
32345           locks.push(1);
32346        },
32347
32348     /**
32349      * Returns true if the quick tip is enabled, else false.
32350      */
32351        isEnabled : function(){
32352             return !disabled;
32353        },
32354
32355         // private
32356        tagConfig : {
32357            namespace : "ext",
32358            attribute : "qtip",
32359            width : "width",
32360            target : "target",
32361            title : "qtitle",
32362            hide : "hide",
32363            cls : "qclass"
32364        }
32365    };
32366 }();
32367
32368 // backwards compat
32369 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32370  * Based on:
32371  * Ext JS Library 1.1.1
32372  * Copyright(c) 2006-2007, Ext JS, LLC.
32373  *
32374  * Originally Released Under LGPL - original licence link has changed is not relivant.
32375  *
32376  * Fork - LGPL
32377  * <script type="text/javascript">
32378  */
32379  
32380
32381 /**
32382  * @class Roo.tree.TreePanel
32383  * @extends Roo.data.Tree
32384
32385  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32386  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32387  * @cfg {Boolean} enableDD true to enable drag and drop
32388  * @cfg {Boolean} enableDrag true to enable just drag
32389  * @cfg {Boolean} enableDrop true to enable just drop
32390  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32391  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32392  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32393  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32394  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32395  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32396  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32397  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32398  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32399  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32400  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32401  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32402  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32403  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32404  * @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>
32405  * @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>
32406  * 
32407  * @constructor
32408  * @param {String/HTMLElement/Element} el The container element
32409  * @param {Object} config
32410  */
32411 Roo.tree.TreePanel = function(el, config){
32412     var root = false;
32413     var loader = false;
32414     if (config.root) {
32415         root = config.root;
32416         delete config.root;
32417     }
32418     if (config.loader) {
32419         loader = config.loader;
32420         delete config.loader;
32421     }
32422     
32423     Roo.apply(this, config);
32424     Roo.tree.TreePanel.superclass.constructor.call(this);
32425     this.el = Roo.get(el);
32426     this.el.addClass('x-tree');
32427     //console.log(root);
32428     if (root) {
32429         this.setRootNode( Roo.factory(root, Roo.tree));
32430     }
32431     if (loader) {
32432         this.loader = Roo.factory(loader, Roo.tree);
32433     }
32434    /**
32435     * Read-only. The id of the container element becomes this TreePanel's id.
32436     */
32437     this.id = this.el.id;
32438     this.addEvents({
32439         /**
32440         * @event beforeload
32441         * Fires before a node is loaded, return false to cancel
32442         * @param {Node} node The node being loaded
32443         */
32444         "beforeload" : true,
32445         /**
32446         * @event load
32447         * Fires when a node is loaded
32448         * @param {Node} node The node that was loaded
32449         */
32450         "load" : true,
32451         /**
32452         * @event textchange
32453         * Fires when the text for a node is changed
32454         * @param {Node} node The node
32455         * @param {String} text The new text
32456         * @param {String} oldText The old text
32457         */
32458         "textchange" : true,
32459         /**
32460         * @event beforeexpand
32461         * Fires before a node is expanded, return false to cancel.
32462         * @param {Node} node The node
32463         * @param {Boolean} deep
32464         * @param {Boolean} anim
32465         */
32466         "beforeexpand" : true,
32467         /**
32468         * @event beforecollapse
32469         * Fires before a node is collapsed, return false to cancel.
32470         * @param {Node} node The node
32471         * @param {Boolean} deep
32472         * @param {Boolean} anim
32473         */
32474         "beforecollapse" : true,
32475         /**
32476         * @event expand
32477         * Fires when a node is expanded
32478         * @param {Node} node The node
32479         */
32480         "expand" : true,
32481         /**
32482         * @event disabledchange
32483         * Fires when the disabled status of a node changes
32484         * @param {Node} node The node
32485         * @param {Boolean} disabled
32486         */
32487         "disabledchange" : true,
32488         /**
32489         * @event collapse
32490         * Fires when a node is collapsed
32491         * @param {Node} node The node
32492         */
32493         "collapse" : true,
32494         /**
32495         * @event beforeclick
32496         * Fires before click processing on a node. Return false to cancel the default action.
32497         * @param {Node} node The node
32498         * @param {Roo.EventObject} e The event object
32499         */
32500         "beforeclick":true,
32501         /**
32502         * @event checkchange
32503         * Fires when a node with a checkbox's checked property changes
32504         * @param {Node} this This node
32505         * @param {Boolean} checked
32506         */
32507         "checkchange":true,
32508         /**
32509         * @event click
32510         * Fires when a node is clicked
32511         * @param {Node} node The node
32512         * @param {Roo.EventObject} e The event object
32513         */
32514         "click":true,
32515         /**
32516         * @event dblclick
32517         * Fires when a node is double clicked
32518         * @param {Node} node The node
32519         * @param {Roo.EventObject} e The event object
32520         */
32521         "dblclick":true,
32522         /**
32523         * @event contextmenu
32524         * Fires when a node is right clicked
32525         * @param {Node} node The node
32526         * @param {Roo.EventObject} e The event object
32527         */
32528         "contextmenu":true,
32529         /**
32530         * @event beforechildrenrendered
32531         * Fires right before the child nodes for a node are rendered
32532         * @param {Node} node The node
32533         */
32534         "beforechildrenrendered":true,
32535         /**
32536         * @event startdrag
32537         * Fires when a node starts being dragged
32538         * @param {Roo.tree.TreePanel} this
32539         * @param {Roo.tree.TreeNode} node
32540         * @param {event} e The raw browser event
32541         */ 
32542        "startdrag" : true,
32543        /**
32544         * @event enddrag
32545         * Fires when a drag operation is complete
32546         * @param {Roo.tree.TreePanel} this
32547         * @param {Roo.tree.TreeNode} node
32548         * @param {event} e The raw browser event
32549         */
32550        "enddrag" : true,
32551        /**
32552         * @event dragdrop
32553         * Fires when a dragged node is dropped on a valid DD target
32554         * @param {Roo.tree.TreePanel} this
32555         * @param {Roo.tree.TreeNode} node
32556         * @param {DD} dd The dd it was dropped on
32557         * @param {event} e The raw browser event
32558         */
32559        "dragdrop" : true,
32560        /**
32561         * @event beforenodedrop
32562         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32563         * passed to handlers has the following properties:<br />
32564         * <ul style="padding:5px;padding-left:16px;">
32565         * <li>tree - The TreePanel</li>
32566         * <li>target - The node being targeted for the drop</li>
32567         * <li>data - The drag data from the drag source</li>
32568         * <li>point - The point of the drop - append, above or below</li>
32569         * <li>source - The drag source</li>
32570         * <li>rawEvent - Raw mouse event</li>
32571         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32572         * to be inserted by setting them on this object.</li>
32573         * <li>cancel - Set this to true to cancel the drop.</li>
32574         * </ul>
32575         * @param {Object} dropEvent
32576         */
32577        "beforenodedrop" : true,
32578        /**
32579         * @event nodedrop
32580         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32581         * passed to handlers has the following properties:<br />
32582         * <ul style="padding:5px;padding-left:16px;">
32583         * <li>tree - The TreePanel</li>
32584         * <li>target - The node being targeted for the drop</li>
32585         * <li>data - The drag data from the drag source</li>
32586         * <li>point - The point of the drop - append, above or below</li>
32587         * <li>source - The drag source</li>
32588         * <li>rawEvent - Raw mouse event</li>
32589         * <li>dropNode - Dropped node(s).</li>
32590         * </ul>
32591         * @param {Object} dropEvent
32592         */
32593        "nodedrop" : true,
32594         /**
32595         * @event nodedragover
32596         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32597         * passed to handlers has the following properties:<br />
32598         * <ul style="padding:5px;padding-left:16px;">
32599         * <li>tree - The TreePanel</li>
32600         * <li>target - The node being targeted for the drop</li>
32601         * <li>data - The drag data from the drag source</li>
32602         * <li>point - The point of the drop - append, above or below</li>
32603         * <li>source - The drag source</li>
32604         * <li>rawEvent - Raw mouse event</li>
32605         * <li>dropNode - Drop node(s) provided by the source.</li>
32606         * <li>cancel - Set this to true to signal drop not allowed.</li>
32607         * </ul>
32608         * @param {Object} dragOverEvent
32609         */
32610        "nodedragover" : true
32611         
32612     });
32613     if(this.singleExpand){
32614        this.on("beforeexpand", this.restrictExpand, this);
32615     }
32616     if (this.editor) {
32617         this.editor.tree = this;
32618         this.editor = Roo.factory(this.editor, Roo.tree);
32619     }
32620     
32621     if (this.selModel) {
32622         this.selModel = Roo.factory(this.selModel, Roo.tree);
32623     }
32624    
32625 };
32626 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32627     rootVisible : true,
32628     animate: Roo.enableFx,
32629     lines : true,
32630     enableDD : false,
32631     hlDrop : Roo.enableFx,
32632   
32633     renderer: false,
32634     
32635     rendererTip: false,
32636     // private
32637     restrictExpand : function(node){
32638         var p = node.parentNode;
32639         if(p){
32640             if(p.expandedChild && p.expandedChild.parentNode == p){
32641                 p.expandedChild.collapse();
32642             }
32643             p.expandedChild = node;
32644         }
32645     },
32646
32647     // private override
32648     setRootNode : function(node){
32649         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32650         if(!this.rootVisible){
32651             node.ui = new Roo.tree.RootTreeNodeUI(node);
32652         }
32653         return node;
32654     },
32655
32656     /**
32657      * Returns the container element for this TreePanel
32658      */
32659     getEl : function(){
32660         return this.el;
32661     },
32662
32663     /**
32664      * Returns the default TreeLoader for this TreePanel
32665      */
32666     getLoader : function(){
32667         return this.loader;
32668     },
32669
32670     /**
32671      * Expand all nodes
32672      */
32673     expandAll : function(){
32674         this.root.expand(true);
32675     },
32676
32677     /**
32678      * Collapse all nodes
32679      */
32680     collapseAll : function(){
32681         this.root.collapse(true);
32682     },
32683
32684     /**
32685      * Returns the selection model used by this TreePanel
32686      */
32687     getSelectionModel : function(){
32688         if(!this.selModel){
32689             this.selModel = new Roo.tree.DefaultSelectionModel();
32690         }
32691         return this.selModel;
32692     },
32693
32694     /**
32695      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32696      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32697      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32698      * @return {Array}
32699      */
32700     getChecked : function(a, startNode){
32701         startNode = startNode || this.root;
32702         var r = [];
32703         var f = function(){
32704             if(this.attributes.checked){
32705                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32706             }
32707         }
32708         startNode.cascade(f);
32709         return r;
32710     },
32711
32712     /**
32713      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32714      * @param {String} path
32715      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32716      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32717      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32718      */
32719     expandPath : function(path, attr, callback){
32720         attr = attr || "id";
32721         var keys = path.split(this.pathSeparator);
32722         var curNode = this.root;
32723         if(curNode.attributes[attr] != keys[1]){ // invalid root
32724             if(callback){
32725                 callback(false, null);
32726             }
32727             return;
32728         }
32729         var index = 1;
32730         var f = function(){
32731             if(++index == keys.length){
32732                 if(callback){
32733                     callback(true, curNode);
32734                 }
32735                 return;
32736             }
32737             var c = curNode.findChild(attr, keys[index]);
32738             if(!c){
32739                 if(callback){
32740                     callback(false, curNode);
32741                 }
32742                 return;
32743             }
32744             curNode = c;
32745             c.expand(false, false, f);
32746         };
32747         curNode.expand(false, false, f);
32748     },
32749
32750     /**
32751      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32752      * @param {String} path
32753      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32754      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32755      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32756      */
32757     selectPath : function(path, attr, callback){
32758         attr = attr || "id";
32759         var keys = path.split(this.pathSeparator);
32760         var v = keys.pop();
32761         if(keys.length > 0){
32762             var f = function(success, node){
32763                 if(success && node){
32764                     var n = node.findChild(attr, v);
32765                     if(n){
32766                         n.select();
32767                         if(callback){
32768                             callback(true, n);
32769                         }
32770                     }else if(callback){
32771                         callback(false, n);
32772                     }
32773                 }else{
32774                     if(callback){
32775                         callback(false, n);
32776                     }
32777                 }
32778             };
32779             this.expandPath(keys.join(this.pathSeparator), attr, f);
32780         }else{
32781             this.root.select();
32782             if(callback){
32783                 callback(true, this.root);
32784             }
32785         }
32786     },
32787
32788     getTreeEl : function(){
32789         return this.el;
32790     },
32791
32792     /**
32793      * Trigger rendering of this TreePanel
32794      */
32795     render : function(){
32796         if (this.innerCt) {
32797             return this; // stop it rendering more than once!!
32798         }
32799         
32800         this.innerCt = this.el.createChild({tag:"ul",
32801                cls:"x-tree-root-ct " +
32802                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32803
32804         if(this.containerScroll){
32805             Roo.dd.ScrollManager.register(this.el);
32806         }
32807         if((this.enableDD || this.enableDrop) && !this.dropZone){
32808            /**
32809             * The dropZone used by this tree if drop is enabled
32810             * @type Roo.tree.TreeDropZone
32811             */
32812              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32813                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32814            });
32815         }
32816         if((this.enableDD || this.enableDrag) && !this.dragZone){
32817            /**
32818             * The dragZone used by this tree if drag is enabled
32819             * @type Roo.tree.TreeDragZone
32820             */
32821             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32822                ddGroup: this.ddGroup || "TreeDD",
32823                scroll: this.ddScroll
32824            });
32825         }
32826         this.getSelectionModel().init(this);
32827         if (!this.root) {
32828             Roo.log("ROOT not set in tree");
32829             return this;
32830         }
32831         this.root.render();
32832         if(!this.rootVisible){
32833             this.root.renderChildren();
32834         }
32835         return this;
32836     }
32837 });/*
32838  * Based on:
32839  * Ext JS Library 1.1.1
32840  * Copyright(c) 2006-2007, Ext JS, LLC.
32841  *
32842  * Originally Released Under LGPL - original licence link has changed is not relivant.
32843  *
32844  * Fork - LGPL
32845  * <script type="text/javascript">
32846  */
32847  
32848
32849 /**
32850  * @class Roo.tree.DefaultSelectionModel
32851  * @extends Roo.util.Observable
32852  * The default single selection for a TreePanel.
32853  * @param {Object} cfg Configuration
32854  */
32855 Roo.tree.DefaultSelectionModel = function(cfg){
32856    this.selNode = null;
32857    
32858    
32859    
32860    this.addEvents({
32861        /**
32862         * @event selectionchange
32863         * Fires when the selected node changes
32864         * @param {DefaultSelectionModel} this
32865         * @param {TreeNode} node the new selection
32866         */
32867        "selectionchange" : true,
32868
32869        /**
32870         * @event beforeselect
32871         * Fires before the selected node changes, return false to cancel the change
32872         * @param {DefaultSelectionModel} this
32873         * @param {TreeNode} node the new selection
32874         * @param {TreeNode} node the old selection
32875         */
32876        "beforeselect" : true
32877    });
32878    
32879     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32880 };
32881
32882 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32883     init : function(tree){
32884         this.tree = tree;
32885         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32886         tree.on("click", this.onNodeClick, this);
32887     },
32888     
32889     onNodeClick : function(node, e){
32890         if (e.ctrlKey && this.selNode == node)  {
32891             this.unselect(node);
32892             return;
32893         }
32894         this.select(node);
32895     },
32896     
32897     /**
32898      * Select a node.
32899      * @param {TreeNode} node The node to select
32900      * @return {TreeNode} The selected node
32901      */
32902     select : function(node){
32903         var last = this.selNode;
32904         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32905             if(last){
32906                 last.ui.onSelectedChange(false);
32907             }
32908             this.selNode = node;
32909             node.ui.onSelectedChange(true);
32910             this.fireEvent("selectionchange", this, node, last);
32911         }
32912         return node;
32913     },
32914     
32915     /**
32916      * Deselect a node.
32917      * @param {TreeNode} node The node to unselect
32918      */
32919     unselect : function(node){
32920         if(this.selNode == node){
32921             this.clearSelections();
32922         }    
32923     },
32924     
32925     /**
32926      * Clear all selections
32927      */
32928     clearSelections : function(){
32929         var n = this.selNode;
32930         if(n){
32931             n.ui.onSelectedChange(false);
32932             this.selNode = null;
32933             this.fireEvent("selectionchange", this, null);
32934         }
32935         return n;
32936     },
32937     
32938     /**
32939      * Get the selected node
32940      * @return {TreeNode} The selected node
32941      */
32942     getSelectedNode : function(){
32943         return this.selNode;    
32944     },
32945     
32946     /**
32947      * Returns true if the node is selected
32948      * @param {TreeNode} node The node to check
32949      * @return {Boolean}
32950      */
32951     isSelected : function(node){
32952         return this.selNode == node;  
32953     },
32954
32955     /**
32956      * Selects the node above the selected node in the tree, intelligently walking the nodes
32957      * @return TreeNode The new selection
32958      */
32959     selectPrevious : function(){
32960         var s = this.selNode || this.lastSelNode;
32961         if(!s){
32962             return null;
32963         }
32964         var ps = s.previousSibling;
32965         if(ps){
32966             if(!ps.isExpanded() || ps.childNodes.length < 1){
32967                 return this.select(ps);
32968             } else{
32969                 var lc = ps.lastChild;
32970                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32971                     lc = lc.lastChild;
32972                 }
32973                 return this.select(lc);
32974             }
32975         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32976             return this.select(s.parentNode);
32977         }
32978         return null;
32979     },
32980
32981     /**
32982      * Selects the node above the selected node in the tree, intelligently walking the nodes
32983      * @return TreeNode The new selection
32984      */
32985     selectNext : function(){
32986         var s = this.selNode || this.lastSelNode;
32987         if(!s){
32988             return null;
32989         }
32990         if(s.firstChild && s.isExpanded()){
32991              return this.select(s.firstChild);
32992          }else if(s.nextSibling){
32993              return this.select(s.nextSibling);
32994          }else if(s.parentNode){
32995             var newS = null;
32996             s.parentNode.bubble(function(){
32997                 if(this.nextSibling){
32998                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32999                     return false;
33000                 }
33001             });
33002             return newS;
33003          }
33004         return null;
33005     },
33006
33007     onKeyDown : function(e){
33008         var s = this.selNode || this.lastSelNode;
33009         // undesirable, but required
33010         var sm = this;
33011         if(!s){
33012             return;
33013         }
33014         var k = e.getKey();
33015         switch(k){
33016              case e.DOWN:
33017                  e.stopEvent();
33018                  this.selectNext();
33019              break;
33020              case e.UP:
33021                  e.stopEvent();
33022                  this.selectPrevious();
33023              break;
33024              case e.RIGHT:
33025                  e.preventDefault();
33026                  if(s.hasChildNodes()){
33027                      if(!s.isExpanded()){
33028                          s.expand();
33029                      }else if(s.firstChild){
33030                          this.select(s.firstChild, e);
33031                      }
33032                  }
33033              break;
33034              case e.LEFT:
33035                  e.preventDefault();
33036                  if(s.hasChildNodes() && s.isExpanded()){
33037                      s.collapse();
33038                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33039                      this.select(s.parentNode, e);
33040                  }
33041              break;
33042         };
33043     }
33044 });
33045
33046 /**
33047  * @class Roo.tree.MultiSelectionModel
33048  * @extends Roo.util.Observable
33049  * Multi selection for a TreePanel.
33050  * @param {Object} cfg Configuration
33051  */
33052 Roo.tree.MultiSelectionModel = function(){
33053    this.selNodes = [];
33054    this.selMap = {};
33055    this.addEvents({
33056        /**
33057         * @event selectionchange
33058         * Fires when the selected nodes change
33059         * @param {MultiSelectionModel} this
33060         * @param {Array} nodes Array of the selected nodes
33061         */
33062        "selectionchange" : true
33063    });
33064    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33065    
33066 };
33067
33068 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33069     init : function(tree){
33070         this.tree = tree;
33071         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33072         tree.on("click", this.onNodeClick, this);
33073     },
33074     
33075     onNodeClick : function(node, e){
33076         this.select(node, e, e.ctrlKey);
33077     },
33078     
33079     /**
33080      * Select a node.
33081      * @param {TreeNode} node The node to select
33082      * @param {EventObject} e (optional) An event associated with the selection
33083      * @param {Boolean} keepExisting True to retain existing selections
33084      * @return {TreeNode} The selected node
33085      */
33086     select : function(node, e, keepExisting){
33087         if(keepExisting !== true){
33088             this.clearSelections(true);
33089         }
33090         if(this.isSelected(node)){
33091             this.lastSelNode = node;
33092             return node;
33093         }
33094         this.selNodes.push(node);
33095         this.selMap[node.id] = node;
33096         this.lastSelNode = node;
33097         node.ui.onSelectedChange(true);
33098         this.fireEvent("selectionchange", this, this.selNodes);
33099         return node;
33100     },
33101     
33102     /**
33103      * Deselect a node.
33104      * @param {TreeNode} node The node to unselect
33105      */
33106     unselect : function(node){
33107         if(this.selMap[node.id]){
33108             node.ui.onSelectedChange(false);
33109             var sn = this.selNodes;
33110             var index = -1;
33111             if(sn.indexOf){
33112                 index = sn.indexOf(node);
33113             }else{
33114                 for(var i = 0, len = sn.length; i < len; i++){
33115                     if(sn[i] == node){
33116                         index = i;
33117                         break;
33118                     }
33119                 }
33120             }
33121             if(index != -1){
33122                 this.selNodes.splice(index, 1);
33123             }
33124             delete this.selMap[node.id];
33125             this.fireEvent("selectionchange", this, this.selNodes);
33126         }
33127     },
33128     
33129     /**
33130      * Clear all selections
33131      */
33132     clearSelections : function(suppressEvent){
33133         var sn = this.selNodes;
33134         if(sn.length > 0){
33135             for(var i = 0, len = sn.length; i < len; i++){
33136                 sn[i].ui.onSelectedChange(false);
33137             }
33138             this.selNodes = [];
33139             this.selMap = {};
33140             if(suppressEvent !== true){
33141                 this.fireEvent("selectionchange", this, this.selNodes);
33142             }
33143         }
33144     },
33145     
33146     /**
33147      * Returns true if the node is selected
33148      * @param {TreeNode} node The node to check
33149      * @return {Boolean}
33150      */
33151     isSelected : function(node){
33152         return this.selMap[node.id] ? true : false;  
33153     },
33154     
33155     /**
33156      * Returns an array of the selected nodes
33157      * @return {Array}
33158      */
33159     getSelectedNodes : function(){
33160         return this.selNodes;    
33161     },
33162
33163     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33164
33165     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33166
33167     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33168 });/*
33169  * Based on:
33170  * Ext JS Library 1.1.1
33171  * Copyright(c) 2006-2007, Ext JS, LLC.
33172  *
33173  * Originally Released Under LGPL - original licence link has changed is not relivant.
33174  *
33175  * Fork - LGPL
33176  * <script type="text/javascript">
33177  */
33178  
33179 /**
33180  * @class Roo.tree.TreeNode
33181  * @extends Roo.data.Node
33182  * @cfg {String} text The text for this node
33183  * @cfg {Boolean} expanded true to start the node expanded
33184  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33185  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33186  * @cfg {Boolean} disabled true to start the node disabled
33187  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33188  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33189  * @cfg {String} cls A css class to be added to the node
33190  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33191  * @cfg {String} href URL of the link used for the node (defaults to #)
33192  * @cfg {String} hrefTarget target frame for the link
33193  * @cfg {String} qtip An Ext QuickTip for the node
33194  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33195  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33196  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33197  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33198  * (defaults to undefined with no checkbox rendered)
33199  * @constructor
33200  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33201  */
33202 Roo.tree.TreeNode = function(attributes){
33203     attributes = attributes || {};
33204     if(typeof attributes == "string"){
33205         attributes = {text: attributes};
33206     }
33207     this.childrenRendered = false;
33208     this.rendered = false;
33209     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33210     this.expanded = attributes.expanded === true;
33211     this.isTarget = attributes.isTarget !== false;
33212     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33213     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33214
33215     /**
33216      * Read-only. The text for this node. To change it use setText().
33217      * @type String
33218      */
33219     this.text = attributes.text;
33220     /**
33221      * True if this node is disabled.
33222      * @type Boolean
33223      */
33224     this.disabled = attributes.disabled === true;
33225
33226     this.addEvents({
33227         /**
33228         * @event textchange
33229         * Fires when the text for this node is changed
33230         * @param {Node} this This node
33231         * @param {String} text The new text
33232         * @param {String} oldText The old text
33233         */
33234         "textchange" : true,
33235         /**
33236         * @event beforeexpand
33237         * Fires before this node is expanded, return false to cancel.
33238         * @param {Node} this This node
33239         * @param {Boolean} deep
33240         * @param {Boolean} anim
33241         */
33242         "beforeexpand" : true,
33243         /**
33244         * @event beforecollapse
33245         * Fires before this node is collapsed, return false to cancel.
33246         * @param {Node} this This node
33247         * @param {Boolean} deep
33248         * @param {Boolean} anim
33249         */
33250         "beforecollapse" : true,
33251         /**
33252         * @event expand
33253         * Fires when this node is expanded
33254         * @param {Node} this This node
33255         */
33256         "expand" : true,
33257         /**
33258         * @event disabledchange
33259         * Fires when the disabled status of this node changes
33260         * @param {Node} this This node
33261         * @param {Boolean} disabled
33262         */
33263         "disabledchange" : true,
33264         /**
33265         * @event collapse
33266         * Fires when this node is collapsed
33267         * @param {Node} this This node
33268         */
33269         "collapse" : true,
33270         /**
33271         * @event beforeclick
33272         * Fires before click processing. Return false to cancel the default action.
33273         * @param {Node} this This node
33274         * @param {Roo.EventObject} e The event object
33275         */
33276         "beforeclick":true,
33277         /**
33278         * @event checkchange
33279         * Fires when a node with a checkbox's checked property changes
33280         * @param {Node} this This node
33281         * @param {Boolean} checked
33282         */
33283         "checkchange":true,
33284         /**
33285         * @event click
33286         * Fires when this node is clicked
33287         * @param {Node} this This node
33288         * @param {Roo.EventObject} e The event object
33289         */
33290         "click":true,
33291         /**
33292         * @event dblclick
33293         * Fires when this node is double clicked
33294         * @param {Node} this This node
33295         * @param {Roo.EventObject} e The event object
33296         */
33297         "dblclick":true,
33298         /**
33299         * @event contextmenu
33300         * Fires when this node is right clicked
33301         * @param {Node} this This node
33302         * @param {Roo.EventObject} e The event object
33303         */
33304         "contextmenu":true,
33305         /**
33306         * @event beforechildrenrendered
33307         * Fires right before the child nodes for this node are rendered
33308         * @param {Node} this This node
33309         */
33310         "beforechildrenrendered":true
33311     });
33312
33313     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33314
33315     /**
33316      * Read-only. The UI for this node
33317      * @type TreeNodeUI
33318      */
33319     this.ui = new uiClass(this);
33320     
33321     // finally support items[]
33322     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33323         return;
33324     }
33325     
33326     
33327     Roo.each(this.attributes.items, function(c) {
33328         this.appendChild(Roo.factory(c,Roo.Tree));
33329     }, this);
33330     delete this.attributes.items;
33331     
33332     
33333     
33334 };
33335 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33336     preventHScroll: true,
33337     /**
33338      * Returns true if this node is expanded
33339      * @return {Boolean}
33340      */
33341     isExpanded : function(){
33342         return this.expanded;
33343     },
33344
33345     /**
33346      * Returns the UI object for this node
33347      * @return {TreeNodeUI}
33348      */
33349     getUI : function(){
33350         return this.ui;
33351     },
33352
33353     // private override
33354     setFirstChild : function(node){
33355         var of = this.firstChild;
33356         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33357         if(this.childrenRendered && of && node != of){
33358             of.renderIndent(true, true);
33359         }
33360         if(this.rendered){
33361             this.renderIndent(true, true);
33362         }
33363     },
33364
33365     // private override
33366     setLastChild : function(node){
33367         var ol = this.lastChild;
33368         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33369         if(this.childrenRendered && ol && node != ol){
33370             ol.renderIndent(true, true);
33371         }
33372         if(this.rendered){
33373             this.renderIndent(true, true);
33374         }
33375     },
33376
33377     // these methods are overridden to provide lazy rendering support
33378     // private override
33379     appendChild : function()
33380     {
33381         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33382         if(node && this.childrenRendered){
33383             node.render();
33384         }
33385         this.ui.updateExpandIcon();
33386         return node;
33387     },
33388
33389     // private override
33390     removeChild : function(node){
33391         this.ownerTree.getSelectionModel().unselect(node);
33392         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33393         // if it's been rendered remove dom node
33394         if(this.childrenRendered){
33395             node.ui.remove();
33396         }
33397         if(this.childNodes.length < 1){
33398             this.collapse(false, false);
33399         }else{
33400             this.ui.updateExpandIcon();
33401         }
33402         if(!this.firstChild) {
33403             this.childrenRendered = false;
33404         }
33405         return node;
33406     },
33407
33408     // private override
33409     insertBefore : function(node, refNode){
33410         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33411         if(newNode && refNode && this.childrenRendered){
33412             node.render();
33413         }
33414         this.ui.updateExpandIcon();
33415         return newNode;
33416     },
33417
33418     /**
33419      * Sets the text for this node
33420      * @param {String} text
33421      */
33422     setText : function(text){
33423         var oldText = this.text;
33424         this.text = text;
33425         this.attributes.text = text;
33426         if(this.rendered){ // event without subscribing
33427             this.ui.onTextChange(this, text, oldText);
33428         }
33429         this.fireEvent("textchange", this, text, oldText);
33430     },
33431
33432     /**
33433      * Triggers selection of this node
33434      */
33435     select : function(){
33436         this.getOwnerTree().getSelectionModel().select(this);
33437     },
33438
33439     /**
33440      * Triggers deselection of this node
33441      */
33442     unselect : function(){
33443         this.getOwnerTree().getSelectionModel().unselect(this);
33444     },
33445
33446     /**
33447      * Returns true if this node is selected
33448      * @return {Boolean}
33449      */
33450     isSelected : function(){
33451         return this.getOwnerTree().getSelectionModel().isSelected(this);
33452     },
33453
33454     /**
33455      * Expand this node.
33456      * @param {Boolean} deep (optional) True to expand all children as well
33457      * @param {Boolean} anim (optional) false to cancel the default animation
33458      * @param {Function} callback (optional) A callback to be called when
33459      * expanding this node completes (does not wait for deep expand to complete).
33460      * Called with 1 parameter, this node.
33461      */
33462     expand : function(deep, anim, callback){
33463         if(!this.expanded){
33464             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33465                 return;
33466             }
33467             if(!this.childrenRendered){
33468                 this.renderChildren();
33469             }
33470             this.expanded = true;
33471             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33472                 this.ui.animExpand(function(){
33473                     this.fireEvent("expand", this);
33474                     if(typeof callback == "function"){
33475                         callback(this);
33476                     }
33477                     if(deep === true){
33478                         this.expandChildNodes(true);
33479                     }
33480                 }.createDelegate(this));
33481                 return;
33482             }else{
33483                 this.ui.expand();
33484                 this.fireEvent("expand", this);
33485                 if(typeof callback == "function"){
33486                     callback(this);
33487                 }
33488             }
33489         }else{
33490            if(typeof callback == "function"){
33491                callback(this);
33492            }
33493         }
33494         if(deep === true){
33495             this.expandChildNodes(true);
33496         }
33497     },
33498
33499     isHiddenRoot : function(){
33500         return this.isRoot && !this.getOwnerTree().rootVisible;
33501     },
33502
33503     /**
33504      * Collapse this node.
33505      * @param {Boolean} deep (optional) True to collapse all children as well
33506      * @param {Boolean} anim (optional) false to cancel the default animation
33507      */
33508     collapse : function(deep, anim){
33509         if(this.expanded && !this.isHiddenRoot()){
33510             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33511                 return;
33512             }
33513             this.expanded = false;
33514             if((this.getOwnerTree().animate && anim !== false) || anim){
33515                 this.ui.animCollapse(function(){
33516                     this.fireEvent("collapse", this);
33517                     if(deep === true){
33518                         this.collapseChildNodes(true);
33519                     }
33520                 }.createDelegate(this));
33521                 return;
33522             }else{
33523                 this.ui.collapse();
33524                 this.fireEvent("collapse", this);
33525             }
33526         }
33527         if(deep === true){
33528             var cs = this.childNodes;
33529             for(var i = 0, len = cs.length; i < len; i++) {
33530                 cs[i].collapse(true, false);
33531             }
33532         }
33533     },
33534
33535     // private
33536     delayedExpand : function(delay){
33537         if(!this.expandProcId){
33538             this.expandProcId = this.expand.defer(delay, this);
33539         }
33540     },
33541
33542     // private
33543     cancelExpand : function(){
33544         if(this.expandProcId){
33545             clearTimeout(this.expandProcId);
33546         }
33547         this.expandProcId = false;
33548     },
33549
33550     /**
33551      * Toggles expanded/collapsed state of the node
33552      */
33553     toggle : function(){
33554         if(this.expanded){
33555             this.collapse();
33556         }else{
33557             this.expand();
33558         }
33559     },
33560
33561     /**
33562      * Ensures all parent nodes are expanded
33563      */
33564     ensureVisible : function(callback){
33565         var tree = this.getOwnerTree();
33566         tree.expandPath(this.parentNode.getPath(), false, function(){
33567             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33568             Roo.callback(callback);
33569         }.createDelegate(this));
33570     },
33571
33572     /**
33573      * Expand all child nodes
33574      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33575      */
33576     expandChildNodes : function(deep){
33577         var cs = this.childNodes;
33578         for(var i = 0, len = cs.length; i < len; i++) {
33579                 cs[i].expand(deep);
33580         }
33581     },
33582
33583     /**
33584      * Collapse all child nodes
33585      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33586      */
33587     collapseChildNodes : function(deep){
33588         var cs = this.childNodes;
33589         for(var i = 0, len = cs.length; i < len; i++) {
33590                 cs[i].collapse(deep);
33591         }
33592     },
33593
33594     /**
33595      * Disables this node
33596      */
33597     disable : function(){
33598         this.disabled = true;
33599         this.unselect();
33600         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33601             this.ui.onDisableChange(this, true);
33602         }
33603         this.fireEvent("disabledchange", this, true);
33604     },
33605
33606     /**
33607      * Enables this node
33608      */
33609     enable : function(){
33610         this.disabled = false;
33611         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33612             this.ui.onDisableChange(this, false);
33613         }
33614         this.fireEvent("disabledchange", this, false);
33615     },
33616
33617     // private
33618     renderChildren : function(suppressEvent){
33619         if(suppressEvent !== false){
33620             this.fireEvent("beforechildrenrendered", this);
33621         }
33622         var cs = this.childNodes;
33623         for(var i = 0, len = cs.length; i < len; i++){
33624             cs[i].render(true);
33625         }
33626         this.childrenRendered = true;
33627     },
33628
33629     // private
33630     sort : function(fn, scope){
33631         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33632         if(this.childrenRendered){
33633             var cs = this.childNodes;
33634             for(var i = 0, len = cs.length; i < len; i++){
33635                 cs[i].render(true);
33636             }
33637         }
33638     },
33639
33640     // private
33641     render : function(bulkRender){
33642         this.ui.render(bulkRender);
33643         if(!this.rendered){
33644             this.rendered = true;
33645             if(this.expanded){
33646                 this.expanded = false;
33647                 this.expand(false, false);
33648             }
33649         }
33650     },
33651
33652     // private
33653     renderIndent : function(deep, refresh){
33654         if(refresh){
33655             this.ui.childIndent = null;
33656         }
33657         this.ui.renderIndent();
33658         if(deep === true && this.childrenRendered){
33659             var cs = this.childNodes;
33660             for(var i = 0, len = cs.length; i < len; i++){
33661                 cs[i].renderIndent(true, refresh);
33662             }
33663         }
33664     }
33665 });/*
33666  * Based on:
33667  * Ext JS Library 1.1.1
33668  * Copyright(c) 2006-2007, Ext JS, LLC.
33669  *
33670  * Originally Released Under LGPL - original licence link has changed is not relivant.
33671  *
33672  * Fork - LGPL
33673  * <script type="text/javascript">
33674  */
33675  
33676 /**
33677  * @class Roo.tree.AsyncTreeNode
33678  * @extends Roo.tree.TreeNode
33679  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33680  * @constructor
33681  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33682  */
33683  Roo.tree.AsyncTreeNode = function(config){
33684     this.loaded = false;
33685     this.loading = false;
33686     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33687     /**
33688     * @event beforeload
33689     * Fires before this node is loaded, return false to cancel
33690     * @param {Node} this This node
33691     */
33692     this.addEvents({'beforeload':true, 'load': true});
33693     /**
33694     * @event load
33695     * Fires when this node is loaded
33696     * @param {Node} this This node
33697     */
33698     /**
33699      * The loader used by this node (defaults to using the tree's defined loader)
33700      * @type TreeLoader
33701      * @property loader
33702      */
33703 };
33704 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33705     expand : function(deep, anim, callback){
33706         if(this.loading){ // if an async load is already running, waiting til it's done
33707             var timer;
33708             var f = function(){
33709                 if(!this.loading){ // done loading
33710                     clearInterval(timer);
33711                     this.expand(deep, anim, callback);
33712                 }
33713             }.createDelegate(this);
33714             timer = setInterval(f, 200);
33715             return;
33716         }
33717         if(!this.loaded){
33718             if(this.fireEvent("beforeload", this) === false){
33719                 return;
33720             }
33721             this.loading = true;
33722             this.ui.beforeLoad(this);
33723             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33724             if(loader){
33725                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33726                 return;
33727             }
33728         }
33729         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33730     },
33731     
33732     /**
33733      * Returns true if this node is currently loading
33734      * @return {Boolean}
33735      */
33736     isLoading : function(){
33737         return this.loading;  
33738     },
33739     
33740     loadComplete : function(deep, anim, callback){
33741         this.loading = false;
33742         this.loaded = true;
33743         this.ui.afterLoad(this);
33744         this.fireEvent("load", this);
33745         this.expand(deep, anim, callback);
33746     },
33747     
33748     /**
33749      * Returns true if this node has been loaded
33750      * @return {Boolean}
33751      */
33752     isLoaded : function(){
33753         return this.loaded;
33754     },
33755     
33756     hasChildNodes : function(){
33757         if(!this.isLeaf() && !this.loaded){
33758             return true;
33759         }else{
33760             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33761         }
33762     },
33763
33764     /**
33765      * Trigger a reload for this node
33766      * @param {Function} callback
33767      */
33768     reload : function(callback){
33769         this.collapse(false, false);
33770         while(this.firstChild){
33771             this.removeChild(this.firstChild);
33772         }
33773         this.childrenRendered = false;
33774         this.loaded = false;
33775         if(this.isHiddenRoot()){
33776             this.expanded = false;
33777         }
33778         this.expand(false, false, callback);
33779     }
33780 });/*
33781  * Based on:
33782  * Ext JS Library 1.1.1
33783  * Copyright(c) 2006-2007, Ext JS, LLC.
33784  *
33785  * Originally Released Under LGPL - original licence link has changed is not relivant.
33786  *
33787  * Fork - LGPL
33788  * <script type="text/javascript">
33789  */
33790  
33791 /**
33792  * @class Roo.tree.TreeNodeUI
33793  * @constructor
33794  * @param {Object} node The node to render
33795  * The TreeNode UI implementation is separate from the
33796  * tree implementation. Unless you are customizing the tree UI,
33797  * you should never have to use this directly.
33798  */
33799 Roo.tree.TreeNodeUI = function(node){
33800     this.node = node;
33801     this.rendered = false;
33802     this.animating = false;
33803     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33804 };
33805
33806 Roo.tree.TreeNodeUI.prototype = {
33807     removeChild : function(node){
33808         if(this.rendered){
33809             this.ctNode.removeChild(node.ui.getEl());
33810         }
33811     },
33812
33813     beforeLoad : function(){
33814          this.addClass("x-tree-node-loading");
33815     },
33816
33817     afterLoad : function(){
33818          this.removeClass("x-tree-node-loading");
33819     },
33820
33821     onTextChange : function(node, text, oldText){
33822         if(this.rendered){
33823             this.textNode.innerHTML = text;
33824         }
33825     },
33826
33827     onDisableChange : function(node, state){
33828         this.disabled = state;
33829         if(state){
33830             this.addClass("x-tree-node-disabled");
33831         }else{
33832             this.removeClass("x-tree-node-disabled");
33833         }
33834     },
33835
33836     onSelectedChange : function(state){
33837         if(state){
33838             this.focus();
33839             this.addClass("x-tree-selected");
33840         }else{
33841             //this.blur();
33842             this.removeClass("x-tree-selected");
33843         }
33844     },
33845
33846     onMove : function(tree, node, oldParent, newParent, index, refNode){
33847         this.childIndent = null;
33848         if(this.rendered){
33849             var targetNode = newParent.ui.getContainer();
33850             if(!targetNode){//target not rendered
33851                 this.holder = document.createElement("div");
33852                 this.holder.appendChild(this.wrap);
33853                 return;
33854             }
33855             var insertBefore = refNode ? refNode.ui.getEl() : null;
33856             if(insertBefore){
33857                 targetNode.insertBefore(this.wrap, insertBefore);
33858             }else{
33859                 targetNode.appendChild(this.wrap);
33860             }
33861             this.node.renderIndent(true);
33862         }
33863     },
33864
33865     addClass : function(cls){
33866         if(this.elNode){
33867             Roo.fly(this.elNode).addClass(cls);
33868         }
33869     },
33870
33871     removeClass : function(cls){
33872         if(this.elNode){
33873             Roo.fly(this.elNode).removeClass(cls);
33874         }
33875     },
33876
33877     remove : function(){
33878         if(this.rendered){
33879             this.holder = document.createElement("div");
33880             this.holder.appendChild(this.wrap);
33881         }
33882     },
33883
33884     fireEvent : function(){
33885         return this.node.fireEvent.apply(this.node, arguments);
33886     },
33887
33888     initEvents : function(){
33889         this.node.on("move", this.onMove, this);
33890         var E = Roo.EventManager;
33891         var a = this.anchor;
33892
33893         var el = Roo.fly(a, '_treeui');
33894
33895         if(Roo.isOpera){ // opera render bug ignores the CSS
33896             el.setStyle("text-decoration", "none");
33897         }
33898
33899         el.on("click", this.onClick, this);
33900         el.on("dblclick", this.onDblClick, this);
33901
33902         if(this.checkbox){
33903             Roo.EventManager.on(this.checkbox,
33904                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33905         }
33906
33907         el.on("contextmenu", this.onContextMenu, this);
33908
33909         var icon = Roo.fly(this.iconNode);
33910         icon.on("click", this.onClick, this);
33911         icon.on("dblclick", this.onDblClick, this);
33912         icon.on("contextmenu", this.onContextMenu, this);
33913         E.on(this.ecNode, "click", this.ecClick, this, true);
33914
33915         if(this.node.disabled){
33916             this.addClass("x-tree-node-disabled");
33917         }
33918         if(this.node.hidden){
33919             this.addClass("x-tree-node-disabled");
33920         }
33921         var ot = this.node.getOwnerTree();
33922         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33923         if(dd && (!this.node.isRoot || ot.rootVisible)){
33924             Roo.dd.Registry.register(this.elNode, {
33925                 node: this.node,
33926                 handles: this.getDDHandles(),
33927                 isHandle: false
33928             });
33929         }
33930     },
33931
33932     getDDHandles : function(){
33933         return [this.iconNode, this.textNode];
33934     },
33935
33936     hide : function(){
33937         if(this.rendered){
33938             this.wrap.style.display = "none";
33939         }
33940     },
33941
33942     show : function(){
33943         if(this.rendered){
33944             this.wrap.style.display = "";
33945         }
33946     },
33947
33948     onContextMenu : function(e){
33949         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33950             e.preventDefault();
33951             this.focus();
33952             this.fireEvent("contextmenu", this.node, e);
33953         }
33954     },
33955
33956     onClick : function(e){
33957         if(this.dropping){
33958             e.stopEvent();
33959             return;
33960         }
33961         if(this.fireEvent("beforeclick", this.node, e) !== false){
33962             if(!this.disabled && this.node.attributes.href){
33963                 this.fireEvent("click", this.node, e);
33964                 return;
33965             }
33966             e.preventDefault();
33967             if(this.disabled){
33968                 return;
33969             }
33970
33971             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33972                 this.node.toggle();
33973             }
33974
33975             this.fireEvent("click", this.node, e);
33976         }else{
33977             e.stopEvent();
33978         }
33979     },
33980
33981     onDblClick : function(e){
33982         e.preventDefault();
33983         if(this.disabled){
33984             return;
33985         }
33986         if(this.checkbox){
33987             this.toggleCheck();
33988         }
33989         if(!this.animating && this.node.hasChildNodes()){
33990             this.node.toggle();
33991         }
33992         this.fireEvent("dblclick", this.node, e);
33993     },
33994
33995     onCheckChange : function(){
33996         var checked = this.checkbox.checked;
33997         this.node.attributes.checked = checked;
33998         this.fireEvent('checkchange', this.node, checked);
33999     },
34000
34001     ecClick : function(e){
34002         if(!this.animating && this.node.hasChildNodes()){
34003             this.node.toggle();
34004         }
34005     },
34006
34007     startDrop : function(){
34008         this.dropping = true;
34009     },
34010
34011     // delayed drop so the click event doesn't get fired on a drop
34012     endDrop : function(){
34013        setTimeout(function(){
34014            this.dropping = false;
34015        }.createDelegate(this), 50);
34016     },
34017
34018     expand : function(){
34019         this.updateExpandIcon();
34020         this.ctNode.style.display = "";
34021     },
34022
34023     focus : function(){
34024         if(!this.node.preventHScroll){
34025             try{this.anchor.focus();
34026             }catch(e){}
34027         }else if(!Roo.isIE){
34028             try{
34029                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34030                 var l = noscroll.scrollLeft;
34031                 this.anchor.focus();
34032                 noscroll.scrollLeft = l;
34033             }catch(e){}
34034         }
34035     },
34036
34037     toggleCheck : function(value){
34038         var cb = this.checkbox;
34039         if(cb){
34040             cb.checked = (value === undefined ? !cb.checked : value);
34041         }
34042     },
34043
34044     blur : function(){
34045         try{
34046             this.anchor.blur();
34047         }catch(e){}
34048     },
34049
34050     animExpand : function(callback){
34051         var ct = Roo.get(this.ctNode);
34052         ct.stopFx();
34053         if(!this.node.hasChildNodes()){
34054             this.updateExpandIcon();
34055             this.ctNode.style.display = "";
34056             Roo.callback(callback);
34057             return;
34058         }
34059         this.animating = true;
34060         this.updateExpandIcon();
34061
34062         ct.slideIn('t', {
34063            callback : function(){
34064                this.animating = false;
34065                Roo.callback(callback);
34066             },
34067             scope: this,
34068             duration: this.node.ownerTree.duration || .25
34069         });
34070     },
34071
34072     highlight : function(){
34073         var tree = this.node.getOwnerTree();
34074         Roo.fly(this.wrap).highlight(
34075             tree.hlColor || "C3DAF9",
34076             {endColor: tree.hlBaseColor}
34077         );
34078     },
34079
34080     collapse : function(){
34081         this.updateExpandIcon();
34082         this.ctNode.style.display = "none";
34083     },
34084
34085     animCollapse : function(callback){
34086         var ct = Roo.get(this.ctNode);
34087         ct.enableDisplayMode('block');
34088         ct.stopFx();
34089
34090         this.animating = true;
34091         this.updateExpandIcon();
34092
34093         ct.slideOut('t', {
34094             callback : function(){
34095                this.animating = false;
34096                Roo.callback(callback);
34097             },
34098             scope: this,
34099             duration: this.node.ownerTree.duration || .25
34100         });
34101     },
34102
34103     getContainer : function(){
34104         return this.ctNode;
34105     },
34106
34107     getEl : function(){
34108         return this.wrap;
34109     },
34110
34111     appendDDGhost : function(ghostNode){
34112         ghostNode.appendChild(this.elNode.cloneNode(true));
34113     },
34114
34115     getDDRepairXY : function(){
34116         return Roo.lib.Dom.getXY(this.iconNode);
34117     },
34118
34119     onRender : function(){
34120         this.render();
34121     },
34122
34123     render : function(bulkRender){
34124         var n = this.node, a = n.attributes;
34125         var targetNode = n.parentNode ?
34126               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34127
34128         if(!this.rendered){
34129             this.rendered = true;
34130
34131             this.renderElements(n, a, targetNode, bulkRender);
34132
34133             if(a.qtip){
34134                if(this.textNode.setAttributeNS){
34135                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34136                    if(a.qtipTitle){
34137                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34138                    }
34139                }else{
34140                    this.textNode.setAttribute("ext:qtip", a.qtip);
34141                    if(a.qtipTitle){
34142                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34143                    }
34144                }
34145             }else if(a.qtipCfg){
34146                 a.qtipCfg.target = Roo.id(this.textNode);
34147                 Roo.QuickTips.register(a.qtipCfg);
34148             }
34149             this.initEvents();
34150             if(!this.node.expanded){
34151                 this.updateExpandIcon();
34152             }
34153         }else{
34154             if(bulkRender === true) {
34155                 targetNode.appendChild(this.wrap);
34156             }
34157         }
34158     },
34159
34160     renderElements : function(n, a, targetNode, bulkRender)
34161     {
34162         // add some indent caching, this helps performance when rendering a large tree
34163         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34164         var t = n.getOwnerTree();
34165         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34166         if (typeof(n.attributes.html) != 'undefined') {
34167             txt = n.attributes.html;
34168         }
34169         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34170         var cb = typeof a.checked == 'boolean';
34171         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34172         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34173             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34174             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34175             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34176             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34177             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34178              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34179                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34180             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34181             "</li>"];
34182
34183         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34184             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34185                                 n.nextSibling.ui.getEl(), buf.join(""));
34186         }else{
34187             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34188         }
34189
34190         this.elNode = this.wrap.childNodes[0];
34191         this.ctNode = this.wrap.childNodes[1];
34192         var cs = this.elNode.childNodes;
34193         this.indentNode = cs[0];
34194         this.ecNode = cs[1];
34195         this.iconNode = cs[2];
34196         var index = 3;
34197         if(cb){
34198             this.checkbox = cs[3];
34199             index++;
34200         }
34201         this.anchor = cs[index];
34202         this.textNode = cs[index].firstChild;
34203     },
34204
34205     getAnchor : function(){
34206         return this.anchor;
34207     },
34208
34209     getTextEl : function(){
34210         return this.textNode;
34211     },
34212
34213     getIconEl : function(){
34214         return this.iconNode;
34215     },
34216
34217     isChecked : function(){
34218         return this.checkbox ? this.checkbox.checked : false;
34219     },
34220
34221     updateExpandIcon : function(){
34222         if(this.rendered){
34223             var n = this.node, c1, c2;
34224             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34225             var hasChild = n.hasChildNodes();
34226             if(hasChild){
34227                 if(n.expanded){
34228                     cls += "-minus";
34229                     c1 = "x-tree-node-collapsed";
34230                     c2 = "x-tree-node-expanded";
34231                 }else{
34232                     cls += "-plus";
34233                     c1 = "x-tree-node-expanded";
34234                     c2 = "x-tree-node-collapsed";
34235                 }
34236                 if(this.wasLeaf){
34237                     this.removeClass("x-tree-node-leaf");
34238                     this.wasLeaf = false;
34239                 }
34240                 if(this.c1 != c1 || this.c2 != c2){
34241                     Roo.fly(this.elNode).replaceClass(c1, c2);
34242                     this.c1 = c1; this.c2 = c2;
34243                 }
34244             }else{
34245                 // this changes non-leafs into leafs if they have no children.
34246                 // it's not very rational behaviour..
34247                 
34248                 if(!this.wasLeaf && this.node.leaf){
34249                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34250                     delete this.c1;
34251                     delete this.c2;
34252                     this.wasLeaf = true;
34253                 }
34254             }
34255             var ecc = "x-tree-ec-icon "+cls;
34256             if(this.ecc != ecc){
34257                 this.ecNode.className = ecc;
34258                 this.ecc = ecc;
34259             }
34260         }
34261     },
34262
34263     getChildIndent : function(){
34264         if(!this.childIndent){
34265             var buf = [];
34266             var p = this.node;
34267             while(p){
34268                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34269                     if(!p.isLast()) {
34270                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34271                     } else {
34272                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34273                     }
34274                 }
34275                 p = p.parentNode;
34276             }
34277             this.childIndent = buf.join("");
34278         }
34279         return this.childIndent;
34280     },
34281
34282     renderIndent : function(){
34283         if(this.rendered){
34284             var indent = "";
34285             var p = this.node.parentNode;
34286             if(p){
34287                 indent = p.ui.getChildIndent();
34288             }
34289             if(this.indentMarkup != indent){ // don't rerender if not required
34290                 this.indentNode.innerHTML = indent;
34291                 this.indentMarkup = indent;
34292             }
34293             this.updateExpandIcon();
34294         }
34295     }
34296 };
34297
34298 Roo.tree.RootTreeNodeUI = function(){
34299     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34300 };
34301 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34302     render : function(){
34303         if(!this.rendered){
34304             var targetNode = this.node.ownerTree.innerCt.dom;
34305             this.node.expanded = true;
34306             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34307             this.wrap = this.ctNode = targetNode.firstChild;
34308         }
34309     },
34310     collapse : function(){
34311     },
34312     expand : function(){
34313     }
34314 });/*
34315  * Based on:
34316  * Ext JS Library 1.1.1
34317  * Copyright(c) 2006-2007, Ext JS, LLC.
34318  *
34319  * Originally Released Under LGPL - original licence link has changed is not relivant.
34320  *
34321  * Fork - LGPL
34322  * <script type="text/javascript">
34323  */
34324 /**
34325  * @class Roo.tree.TreeLoader
34326  * @extends Roo.util.Observable
34327  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34328  * nodes from a specified URL. The response must be a javascript Array definition
34329  * who's elements are node definition objects. eg:
34330  * <pre><code>
34331 {  success : true,
34332    data :      [
34333    
34334     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34335     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34336     ]
34337 }
34338
34339
34340 </code></pre>
34341  * <br><br>
34342  * The old style respose with just an array is still supported, but not recommended.
34343  * <br><br>
34344  *
34345  * A server request is sent, and child nodes are loaded only when a node is expanded.
34346  * The loading node's id is passed to the server under the parameter name "node" to
34347  * enable the server to produce the correct child nodes.
34348  * <br><br>
34349  * To pass extra parameters, an event handler may be attached to the "beforeload"
34350  * event, and the parameters specified in the TreeLoader's baseParams property:
34351  * <pre><code>
34352     myTreeLoader.on("beforeload", function(treeLoader, node) {
34353         this.baseParams.category = node.attributes.category;
34354     }, this);
34355 </code></pre><
34356  * This would pass an HTTP parameter called "category" to the server containing
34357  * the value of the Node's "category" attribute.
34358  * @constructor
34359  * Creates a new Treeloader.
34360  * @param {Object} config A config object containing config properties.
34361  */
34362 Roo.tree.TreeLoader = function(config){
34363     this.baseParams = {};
34364     this.requestMethod = "POST";
34365     Roo.apply(this, config);
34366
34367     this.addEvents({
34368     
34369         /**
34370          * @event beforeload
34371          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34372          * @param {Object} This TreeLoader object.
34373          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34374          * @param {Object} callback The callback function specified in the {@link #load} call.
34375          */
34376         beforeload : true,
34377         /**
34378          * @event load
34379          * Fires when the node has been successfuly loaded.
34380          * @param {Object} This TreeLoader object.
34381          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34382          * @param {Object} response The response object containing the data from the server.
34383          */
34384         load : true,
34385         /**
34386          * @event loadexception
34387          * Fires if the network request failed.
34388          * @param {Object} This TreeLoader object.
34389          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34390          * @param {Object} response The response object containing the data from the server.
34391          */
34392         loadexception : true,
34393         /**
34394          * @event create
34395          * Fires before a node is created, enabling you to return custom Node types 
34396          * @param {Object} This TreeLoader object.
34397          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34398          */
34399         create : true
34400     });
34401
34402     Roo.tree.TreeLoader.superclass.constructor.call(this);
34403 };
34404
34405 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34406     /**
34407     * @cfg {String} dataUrl The URL from which to request a Json string which
34408     * specifies an array of node definition object representing the child nodes
34409     * to be loaded.
34410     */
34411     /**
34412     * @cfg {String} requestMethod either GET or POST
34413     * defaults to POST (due to BC)
34414     * to be loaded.
34415     */
34416     /**
34417     * @cfg {Object} baseParams (optional) An object containing properties which
34418     * specify HTTP parameters to be passed to each request for child nodes.
34419     */
34420     /**
34421     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34422     * created by this loader. If the attributes sent by the server have an attribute in this object,
34423     * they take priority.
34424     */
34425     /**
34426     * @cfg {Object} uiProviders (optional) An object containing properties which
34427     * 
34428     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34429     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34430     * <i>uiProvider</i> attribute of a returned child node is a string rather
34431     * than a reference to a TreeNodeUI implementation, this that string value
34432     * is used as a property name in the uiProviders object. You can define the provider named
34433     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34434     */
34435     uiProviders : {},
34436
34437     /**
34438     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34439     * child nodes before loading.
34440     */
34441     clearOnLoad : true,
34442
34443     /**
34444     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34445     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34446     * Grid query { data : [ .....] }
34447     */
34448     
34449     root : false,
34450      /**
34451     * @cfg {String} queryParam (optional) 
34452     * Name of the query as it will be passed on the querystring (defaults to 'node')
34453     * eg. the request will be ?node=[id]
34454     */
34455     
34456     
34457     queryParam: false,
34458     
34459     /**
34460      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34461      * This is called automatically when a node is expanded, but may be used to reload
34462      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34463      * @param {Roo.tree.TreeNode} node
34464      * @param {Function} callback
34465      */
34466     load : function(node, callback){
34467         if(this.clearOnLoad){
34468             while(node.firstChild){
34469                 node.removeChild(node.firstChild);
34470             }
34471         }
34472         if(node.attributes.children){ // preloaded json children
34473             var cs = node.attributes.children;
34474             for(var i = 0, len = cs.length; i < len; i++){
34475                 node.appendChild(this.createNode(cs[i]));
34476             }
34477             if(typeof callback == "function"){
34478                 callback();
34479             }
34480         }else if(this.dataUrl){
34481             this.requestData(node, callback);
34482         }
34483     },
34484
34485     getParams: function(node){
34486         var buf = [], bp = this.baseParams;
34487         for(var key in bp){
34488             if(typeof bp[key] != "function"){
34489                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34490             }
34491         }
34492         var n = this.queryParam === false ? 'node' : this.queryParam;
34493         buf.push(n + "=", encodeURIComponent(node.id));
34494         return buf.join("");
34495     },
34496
34497     requestData : function(node, callback){
34498         if(this.fireEvent("beforeload", this, node, callback) !== false){
34499             this.transId = Roo.Ajax.request({
34500                 method:this.requestMethod,
34501                 url: this.dataUrl||this.url,
34502                 success: this.handleResponse,
34503                 failure: this.handleFailure,
34504                 scope: this,
34505                 argument: {callback: callback, node: node},
34506                 params: this.getParams(node)
34507             });
34508         }else{
34509             // if the load is cancelled, make sure we notify
34510             // the node that we are done
34511             if(typeof callback == "function"){
34512                 callback();
34513             }
34514         }
34515     },
34516
34517     isLoading : function(){
34518         return this.transId ? true : false;
34519     },
34520
34521     abort : function(){
34522         if(this.isLoading()){
34523             Roo.Ajax.abort(this.transId);
34524         }
34525     },
34526
34527     // private
34528     createNode : function(attr)
34529     {
34530         // apply baseAttrs, nice idea Corey!
34531         if(this.baseAttrs){
34532             Roo.applyIf(attr, this.baseAttrs);
34533         }
34534         if(this.applyLoader !== false){
34535             attr.loader = this;
34536         }
34537         // uiProvider = depreciated..
34538         
34539         if(typeof(attr.uiProvider) == 'string'){
34540            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34541                 /**  eval:var:attr */ eval(attr.uiProvider);
34542         }
34543         if(typeof(this.uiProviders['default']) != 'undefined') {
34544             attr.uiProvider = this.uiProviders['default'];
34545         }
34546         
34547         this.fireEvent('create', this, attr);
34548         
34549         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34550         return(attr.leaf ?
34551                         new Roo.tree.TreeNode(attr) :
34552                         new Roo.tree.AsyncTreeNode(attr));
34553     },
34554
34555     processResponse : function(response, node, callback)
34556     {
34557         var json = response.responseText;
34558         try {
34559             
34560             var o = Roo.decode(json);
34561             
34562             if (this.root === false && typeof(o.success) != undefined) {
34563                 this.root = 'data'; // the default behaviour for list like data..
34564                 }
34565                 
34566             if (this.root !== false &&  !o.success) {
34567                 // it's a failure condition.
34568                 var a = response.argument;
34569                 this.fireEvent("loadexception", this, a.node, response);
34570                 Roo.log("Load failed - should have a handler really");
34571                 return;
34572             }
34573             
34574             
34575             
34576             if (this.root !== false) {
34577                  o = o[this.root];
34578             }
34579             
34580             for(var i = 0, len = o.length; i < len; i++){
34581                 var n = this.createNode(o[i]);
34582                 if(n){
34583                     node.appendChild(n);
34584                 }
34585             }
34586             if(typeof callback == "function"){
34587                 callback(this, node);
34588             }
34589         }catch(e){
34590             this.handleFailure(response);
34591         }
34592     },
34593
34594     handleResponse : function(response){
34595         this.transId = false;
34596         var a = response.argument;
34597         this.processResponse(response, a.node, a.callback);
34598         this.fireEvent("load", this, a.node, response);
34599     },
34600
34601     handleFailure : function(response)
34602     {
34603         // should handle failure better..
34604         this.transId = false;
34605         var a = response.argument;
34606         this.fireEvent("loadexception", this, a.node, response);
34607         if(typeof a.callback == "function"){
34608             a.callback(this, a.node);
34609         }
34610     }
34611 });/*
34612  * Based on:
34613  * Ext JS Library 1.1.1
34614  * Copyright(c) 2006-2007, Ext JS, LLC.
34615  *
34616  * Originally Released Under LGPL - original licence link has changed is not relivant.
34617  *
34618  * Fork - LGPL
34619  * <script type="text/javascript">
34620  */
34621
34622 /**
34623 * @class Roo.tree.TreeFilter
34624 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34625 * @param {TreePanel} tree
34626 * @param {Object} config (optional)
34627  */
34628 Roo.tree.TreeFilter = function(tree, config){
34629     this.tree = tree;
34630     this.filtered = {};
34631     Roo.apply(this, config);
34632 };
34633
34634 Roo.tree.TreeFilter.prototype = {
34635     clearBlank:false,
34636     reverse:false,
34637     autoClear:false,
34638     remove:false,
34639
34640      /**
34641      * Filter the data by a specific attribute.
34642      * @param {String/RegExp} value Either string that the attribute value
34643      * should start with or a RegExp to test against the attribute
34644      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34645      * @param {TreeNode} startNode (optional) The node to start the filter at.
34646      */
34647     filter : function(value, attr, startNode){
34648         attr = attr || "text";
34649         var f;
34650         if(typeof value == "string"){
34651             var vlen = value.length;
34652             // auto clear empty filter
34653             if(vlen == 0 && this.clearBlank){
34654                 this.clear();
34655                 return;
34656             }
34657             value = value.toLowerCase();
34658             f = function(n){
34659                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34660             };
34661         }else if(value.exec){ // regex?
34662             f = function(n){
34663                 return value.test(n.attributes[attr]);
34664             };
34665         }else{
34666             throw 'Illegal filter type, must be string or regex';
34667         }
34668         this.filterBy(f, null, startNode);
34669         },
34670
34671     /**
34672      * Filter by a function. The passed function will be called with each
34673      * node in the tree (or from the startNode). If the function returns true, the node is kept
34674      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34675      * @param {Function} fn The filter function
34676      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34677      */
34678     filterBy : function(fn, scope, startNode){
34679         startNode = startNode || this.tree.root;
34680         if(this.autoClear){
34681             this.clear();
34682         }
34683         var af = this.filtered, rv = this.reverse;
34684         var f = function(n){
34685             if(n == startNode){
34686                 return true;
34687             }
34688             if(af[n.id]){
34689                 return false;
34690             }
34691             var m = fn.call(scope || n, n);
34692             if(!m || rv){
34693                 af[n.id] = n;
34694                 n.ui.hide();
34695                 return false;
34696             }
34697             return true;
34698         };
34699         startNode.cascade(f);
34700         if(this.remove){
34701            for(var id in af){
34702                if(typeof id != "function"){
34703                    var n = af[id];
34704                    if(n && n.parentNode){
34705                        n.parentNode.removeChild(n);
34706                    }
34707                }
34708            }
34709         }
34710     },
34711
34712     /**
34713      * Clears the current filter. Note: with the "remove" option
34714      * set a filter cannot be cleared.
34715      */
34716     clear : function(){
34717         var t = this.tree;
34718         var af = this.filtered;
34719         for(var id in af){
34720             if(typeof id != "function"){
34721                 var n = af[id];
34722                 if(n){
34723                     n.ui.show();
34724                 }
34725             }
34726         }
34727         this.filtered = {};
34728     }
34729 };
34730 /*
34731  * Based on:
34732  * Ext JS Library 1.1.1
34733  * Copyright(c) 2006-2007, Ext JS, LLC.
34734  *
34735  * Originally Released Under LGPL - original licence link has changed is not relivant.
34736  *
34737  * Fork - LGPL
34738  * <script type="text/javascript">
34739  */
34740  
34741
34742 /**
34743  * @class Roo.tree.TreeSorter
34744  * Provides sorting of nodes in a TreePanel
34745  * 
34746  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34747  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34748  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34749  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34750  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34751  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34752  * @constructor
34753  * @param {TreePanel} tree
34754  * @param {Object} config
34755  */
34756 Roo.tree.TreeSorter = function(tree, config){
34757     Roo.apply(this, config);
34758     tree.on("beforechildrenrendered", this.doSort, this);
34759     tree.on("append", this.updateSort, this);
34760     tree.on("insert", this.updateSort, this);
34761     
34762     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34763     var p = this.property || "text";
34764     var sortType = this.sortType;
34765     var fs = this.folderSort;
34766     var cs = this.caseSensitive === true;
34767     var leafAttr = this.leafAttr || 'leaf';
34768
34769     this.sortFn = function(n1, n2){
34770         if(fs){
34771             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34772                 return 1;
34773             }
34774             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34775                 return -1;
34776             }
34777         }
34778         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34779         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34780         if(v1 < v2){
34781                         return dsc ? +1 : -1;
34782                 }else if(v1 > v2){
34783                         return dsc ? -1 : +1;
34784         }else{
34785                 return 0;
34786         }
34787     };
34788 };
34789
34790 Roo.tree.TreeSorter.prototype = {
34791     doSort : function(node){
34792         node.sort(this.sortFn);
34793     },
34794     
34795     compareNodes : function(n1, n2){
34796         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34797     },
34798     
34799     updateSort : function(tree, node){
34800         if(node.childrenRendered){
34801             this.doSort.defer(1, this, [node]);
34802         }
34803     }
34804 };/*
34805  * Based on:
34806  * Ext JS Library 1.1.1
34807  * Copyright(c) 2006-2007, Ext JS, LLC.
34808  *
34809  * Originally Released Under LGPL - original licence link has changed is not relivant.
34810  *
34811  * Fork - LGPL
34812  * <script type="text/javascript">
34813  */
34814
34815 if(Roo.dd.DropZone){
34816     
34817 Roo.tree.TreeDropZone = function(tree, config){
34818     this.allowParentInsert = false;
34819     this.allowContainerDrop = false;
34820     this.appendOnly = false;
34821     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34822     this.tree = tree;
34823     this.lastInsertClass = "x-tree-no-status";
34824     this.dragOverData = {};
34825 };
34826
34827 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34828     ddGroup : "TreeDD",
34829     scroll:  true,
34830     
34831     expandDelay : 1000,
34832     
34833     expandNode : function(node){
34834         if(node.hasChildNodes() && !node.isExpanded()){
34835             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34836         }
34837     },
34838     
34839     queueExpand : function(node){
34840         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34841     },
34842     
34843     cancelExpand : function(){
34844         if(this.expandProcId){
34845             clearTimeout(this.expandProcId);
34846             this.expandProcId = false;
34847         }
34848     },
34849     
34850     isValidDropPoint : function(n, pt, dd, e, data){
34851         if(!n || !data){ return false; }
34852         var targetNode = n.node;
34853         var dropNode = data.node;
34854         // default drop rules
34855         if(!(targetNode && targetNode.isTarget && pt)){
34856             return false;
34857         }
34858         if(pt == "append" && targetNode.allowChildren === false){
34859             return false;
34860         }
34861         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34862             return false;
34863         }
34864         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34865             return false;
34866         }
34867         // reuse the object
34868         var overEvent = this.dragOverData;
34869         overEvent.tree = this.tree;
34870         overEvent.target = targetNode;
34871         overEvent.data = data;
34872         overEvent.point = pt;
34873         overEvent.source = dd;
34874         overEvent.rawEvent = e;
34875         overEvent.dropNode = dropNode;
34876         overEvent.cancel = false;  
34877         var result = this.tree.fireEvent("nodedragover", overEvent);
34878         return overEvent.cancel === false && result !== false;
34879     },
34880     
34881     getDropPoint : function(e, n, dd)
34882     {
34883         var tn = n.node;
34884         if(tn.isRoot){
34885             return tn.allowChildren !== false ? "append" : false; // always append for root
34886         }
34887         var dragEl = n.ddel;
34888         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34889         var y = Roo.lib.Event.getPageY(e);
34890         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34891         
34892         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34893         var noAppend = tn.allowChildren === false;
34894         if(this.appendOnly || tn.parentNode.allowChildren === false){
34895             return noAppend ? false : "append";
34896         }
34897         var noBelow = false;
34898         if(!this.allowParentInsert){
34899             noBelow = tn.hasChildNodes() && tn.isExpanded();
34900         }
34901         var q = (b - t) / (noAppend ? 2 : 3);
34902         if(y >= t && y < (t + q)){
34903             return "above";
34904         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34905             return "below";
34906         }else{
34907             return "append";
34908         }
34909     },
34910     
34911     onNodeEnter : function(n, dd, e, data)
34912     {
34913         this.cancelExpand();
34914     },
34915     
34916     onNodeOver : function(n, dd, e, data)
34917     {
34918        
34919         var pt = this.getDropPoint(e, n, dd);
34920         var node = n.node;
34921         
34922         // auto node expand check
34923         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34924             this.queueExpand(node);
34925         }else if(pt != "append"){
34926             this.cancelExpand();
34927         }
34928         
34929         // set the insert point style on the target node
34930         var returnCls = this.dropNotAllowed;
34931         if(this.isValidDropPoint(n, pt, dd, e, data)){
34932            if(pt){
34933                var el = n.ddel;
34934                var cls;
34935                if(pt == "above"){
34936                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34937                    cls = "x-tree-drag-insert-above";
34938                }else if(pt == "below"){
34939                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34940                    cls = "x-tree-drag-insert-below";
34941                }else{
34942                    returnCls = "x-tree-drop-ok-append";
34943                    cls = "x-tree-drag-append";
34944                }
34945                if(this.lastInsertClass != cls){
34946                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34947                    this.lastInsertClass = cls;
34948                }
34949            }
34950        }
34951        return returnCls;
34952     },
34953     
34954     onNodeOut : function(n, dd, e, data){
34955         
34956         this.cancelExpand();
34957         this.removeDropIndicators(n);
34958     },
34959     
34960     onNodeDrop : function(n, dd, e, data){
34961         var point = this.getDropPoint(e, n, dd);
34962         var targetNode = n.node;
34963         targetNode.ui.startDrop();
34964         if(!this.isValidDropPoint(n, point, dd, e, data)){
34965             targetNode.ui.endDrop();
34966             return false;
34967         }
34968         // first try to find the drop node
34969         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34970         var dropEvent = {
34971             tree : this.tree,
34972             target: targetNode,
34973             data: data,
34974             point: point,
34975             source: dd,
34976             rawEvent: e,
34977             dropNode: dropNode,
34978             cancel: !dropNode   
34979         };
34980         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34981         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34982             targetNode.ui.endDrop();
34983             return false;
34984         }
34985         // allow target changing
34986         targetNode = dropEvent.target;
34987         if(point == "append" && !targetNode.isExpanded()){
34988             targetNode.expand(false, null, function(){
34989                 this.completeDrop(dropEvent);
34990             }.createDelegate(this));
34991         }else{
34992             this.completeDrop(dropEvent);
34993         }
34994         return true;
34995     },
34996     
34997     completeDrop : function(de){
34998         var ns = de.dropNode, p = de.point, t = de.target;
34999         if(!(ns instanceof Array)){
35000             ns = [ns];
35001         }
35002         var n;
35003         for(var i = 0, len = ns.length; i < len; i++){
35004             n = ns[i];
35005             if(p == "above"){
35006                 t.parentNode.insertBefore(n, t);
35007             }else if(p == "below"){
35008                 t.parentNode.insertBefore(n, t.nextSibling);
35009             }else{
35010                 t.appendChild(n);
35011             }
35012         }
35013         n.ui.focus();
35014         if(this.tree.hlDrop){
35015             n.ui.highlight();
35016         }
35017         t.ui.endDrop();
35018         this.tree.fireEvent("nodedrop", de);
35019     },
35020     
35021     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35022         if(this.tree.hlDrop){
35023             dropNode.ui.focus();
35024             dropNode.ui.highlight();
35025         }
35026         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35027     },
35028     
35029     getTree : function(){
35030         return this.tree;
35031     },
35032     
35033     removeDropIndicators : function(n){
35034         if(n && n.ddel){
35035             var el = n.ddel;
35036             Roo.fly(el).removeClass([
35037                     "x-tree-drag-insert-above",
35038                     "x-tree-drag-insert-below",
35039                     "x-tree-drag-append"]);
35040             this.lastInsertClass = "_noclass";
35041         }
35042     },
35043     
35044     beforeDragDrop : function(target, e, id){
35045         this.cancelExpand();
35046         return true;
35047     },
35048     
35049     afterRepair : function(data){
35050         if(data && Roo.enableFx){
35051             data.node.ui.highlight();
35052         }
35053         this.hideProxy();
35054     } 
35055     
35056 });
35057
35058 }
35059 /*
35060  * Based on:
35061  * Ext JS Library 1.1.1
35062  * Copyright(c) 2006-2007, Ext JS, LLC.
35063  *
35064  * Originally Released Under LGPL - original licence link has changed is not relivant.
35065  *
35066  * Fork - LGPL
35067  * <script type="text/javascript">
35068  */
35069  
35070
35071 if(Roo.dd.DragZone){
35072 Roo.tree.TreeDragZone = function(tree, config){
35073     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35074     this.tree = tree;
35075 };
35076
35077 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35078     ddGroup : "TreeDD",
35079    
35080     onBeforeDrag : function(data, e){
35081         var n = data.node;
35082         return n && n.draggable && !n.disabled;
35083     },
35084      
35085     
35086     onInitDrag : function(e){
35087         var data = this.dragData;
35088         this.tree.getSelectionModel().select(data.node);
35089         this.proxy.update("");
35090         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35091         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35092     },
35093     
35094     getRepairXY : function(e, data){
35095         return data.node.ui.getDDRepairXY();
35096     },
35097     
35098     onEndDrag : function(data, e){
35099         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35100         
35101         
35102     },
35103     
35104     onValidDrop : function(dd, e, id){
35105         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35106         this.hideProxy();
35107     },
35108     
35109     beforeInvalidDrop : function(e, id){
35110         // this scrolls the original position back into view
35111         var sm = this.tree.getSelectionModel();
35112         sm.clearSelections();
35113         sm.select(this.dragData.node);
35114     }
35115 });
35116 }/*
35117  * Based on:
35118  * Ext JS Library 1.1.1
35119  * Copyright(c) 2006-2007, Ext JS, LLC.
35120  *
35121  * Originally Released Under LGPL - original licence link has changed is not relivant.
35122  *
35123  * Fork - LGPL
35124  * <script type="text/javascript">
35125  */
35126 /**
35127  * @class Roo.tree.TreeEditor
35128  * @extends Roo.Editor
35129  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35130  * as the editor field.
35131  * @constructor
35132  * @param {Object} config (used to be the tree panel.)
35133  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35134  * 
35135  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35136  * @cfg {Roo.form.TextField|Object} field The field configuration
35137  *
35138  * 
35139  */
35140 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35141     var tree = config;
35142     var field;
35143     if (oldconfig) { // old style..
35144         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35145     } else {
35146         // new style..
35147         tree = config.tree;
35148         config.field = config.field  || {};
35149         config.field.xtype = 'TextField';
35150         field = Roo.factory(config.field, Roo.form);
35151     }
35152     config = config || {};
35153     
35154     
35155     this.addEvents({
35156         /**
35157          * @event beforenodeedit
35158          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35159          * false from the handler of this event.
35160          * @param {Editor} this
35161          * @param {Roo.tree.Node} node 
35162          */
35163         "beforenodeedit" : true
35164     });
35165     
35166     //Roo.log(config);
35167     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35168
35169     this.tree = tree;
35170
35171     tree.on('beforeclick', this.beforeNodeClick, this);
35172     tree.getTreeEl().on('mousedown', this.hide, this);
35173     this.on('complete', this.updateNode, this);
35174     this.on('beforestartedit', this.fitToTree, this);
35175     this.on('startedit', this.bindScroll, this, {delay:10});
35176     this.on('specialkey', this.onSpecialKey, this);
35177 };
35178
35179 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35180     /**
35181      * @cfg {String} alignment
35182      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35183      */
35184     alignment: "l-l",
35185     // inherit
35186     autoSize: false,
35187     /**
35188      * @cfg {Boolean} hideEl
35189      * True to hide the bound element while the editor is displayed (defaults to false)
35190      */
35191     hideEl : false,
35192     /**
35193      * @cfg {String} cls
35194      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35195      */
35196     cls: "x-small-editor x-tree-editor",
35197     /**
35198      * @cfg {Boolean} shim
35199      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35200      */
35201     shim:false,
35202     // inherit
35203     shadow:"frame",
35204     /**
35205      * @cfg {Number} maxWidth
35206      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35207      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35208      * scroll and client offsets into account prior to each edit.
35209      */
35210     maxWidth: 250,
35211
35212     editDelay : 350,
35213
35214     // private
35215     fitToTree : function(ed, el){
35216         var td = this.tree.getTreeEl().dom, nd = el.dom;
35217         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35218             td.scrollLeft = nd.offsetLeft;
35219         }
35220         var w = Math.min(
35221                 this.maxWidth,
35222                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35223         this.setSize(w, '');
35224         
35225         return this.fireEvent('beforenodeedit', this, this.editNode);
35226         
35227     },
35228
35229     // private
35230     triggerEdit : function(node){
35231         this.completeEdit();
35232         this.editNode = node;
35233         this.startEdit(node.ui.textNode, node.text);
35234     },
35235
35236     // private
35237     bindScroll : function(){
35238         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35239     },
35240
35241     // private
35242     beforeNodeClick : function(node, e){
35243         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35244         this.lastClick = new Date();
35245         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35246             e.stopEvent();
35247             this.triggerEdit(node);
35248             return false;
35249         }
35250         return true;
35251     },
35252
35253     // private
35254     updateNode : function(ed, value){
35255         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35256         this.editNode.setText(value);
35257     },
35258
35259     // private
35260     onHide : function(){
35261         Roo.tree.TreeEditor.superclass.onHide.call(this);
35262         if(this.editNode){
35263             this.editNode.ui.focus();
35264         }
35265     },
35266
35267     // private
35268     onSpecialKey : function(field, e){
35269         var k = e.getKey();
35270         if(k == e.ESC){
35271             e.stopEvent();
35272             this.cancelEdit();
35273         }else if(k == e.ENTER && !e.hasModifier()){
35274             e.stopEvent();
35275             this.completeEdit();
35276         }
35277     }
35278 });//<Script type="text/javascript">
35279 /*
35280  * Based on:
35281  * Ext JS Library 1.1.1
35282  * Copyright(c) 2006-2007, Ext JS, LLC.
35283  *
35284  * Originally Released Under LGPL - original licence link has changed is not relivant.
35285  *
35286  * Fork - LGPL
35287  * <script type="text/javascript">
35288  */
35289  
35290 /**
35291  * Not documented??? - probably should be...
35292  */
35293
35294 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35295     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35296     
35297     renderElements : function(n, a, targetNode, bulkRender){
35298         //consel.log("renderElements?");
35299         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35300
35301         var t = n.getOwnerTree();
35302         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35303         
35304         var cols = t.columns;
35305         var bw = t.borderWidth;
35306         var c = cols[0];
35307         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35308          var cb = typeof a.checked == "boolean";
35309         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35310         var colcls = 'x-t-' + tid + '-c0';
35311         var buf = [
35312             '<li class="x-tree-node">',
35313             
35314                 
35315                 '<div class="x-tree-node-el ', a.cls,'">',
35316                     // extran...
35317                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35318                 
35319                 
35320                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35321                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35322                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35323                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35324                            (a.iconCls ? ' '+a.iconCls : ''),
35325                            '" unselectable="on" />',
35326                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35327                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35328                              
35329                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35330                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35331                             '<span unselectable="on" qtip="' + tx + '">',
35332                              tx,
35333                              '</span></a>' ,
35334                     '</div>',
35335                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35336                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35337                  ];
35338         for(var i = 1, len = cols.length; i < len; i++){
35339             c = cols[i];
35340             colcls = 'x-t-' + tid + '-c' +i;
35341             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35342             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35343                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35344                       "</div>");
35345          }
35346          
35347          buf.push(
35348             '</a>',
35349             '<div class="x-clear"></div></div>',
35350             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35351             "</li>");
35352         
35353         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35354             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35355                                 n.nextSibling.ui.getEl(), buf.join(""));
35356         }else{
35357             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35358         }
35359         var el = this.wrap.firstChild;
35360         this.elRow = el;
35361         this.elNode = el.firstChild;
35362         this.ranchor = el.childNodes[1];
35363         this.ctNode = this.wrap.childNodes[1];
35364         var cs = el.firstChild.childNodes;
35365         this.indentNode = cs[0];
35366         this.ecNode = cs[1];
35367         this.iconNode = cs[2];
35368         var index = 3;
35369         if(cb){
35370             this.checkbox = cs[3];
35371             index++;
35372         }
35373         this.anchor = cs[index];
35374         
35375         this.textNode = cs[index].firstChild;
35376         
35377         //el.on("click", this.onClick, this);
35378         //el.on("dblclick", this.onDblClick, this);
35379         
35380         
35381        // console.log(this);
35382     },
35383     initEvents : function(){
35384         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35385         
35386             
35387         var a = this.ranchor;
35388
35389         var el = Roo.get(a);
35390
35391         if(Roo.isOpera){ // opera render bug ignores the CSS
35392             el.setStyle("text-decoration", "none");
35393         }
35394
35395         el.on("click", this.onClick, this);
35396         el.on("dblclick", this.onDblClick, this);
35397         el.on("contextmenu", this.onContextMenu, this);
35398         
35399     },
35400     
35401     /*onSelectedChange : function(state){
35402         if(state){
35403             this.focus();
35404             this.addClass("x-tree-selected");
35405         }else{
35406             //this.blur();
35407             this.removeClass("x-tree-selected");
35408         }
35409     },*/
35410     addClass : function(cls){
35411         if(this.elRow){
35412             Roo.fly(this.elRow).addClass(cls);
35413         }
35414         
35415     },
35416     
35417     
35418     removeClass : function(cls){
35419         if(this.elRow){
35420             Roo.fly(this.elRow).removeClass(cls);
35421         }
35422     }
35423
35424     
35425     
35426 });//<Script type="text/javascript">
35427
35428 /*
35429  * Based on:
35430  * Ext JS Library 1.1.1
35431  * Copyright(c) 2006-2007, Ext JS, LLC.
35432  *
35433  * Originally Released Under LGPL - original licence link has changed is not relivant.
35434  *
35435  * Fork - LGPL
35436  * <script type="text/javascript">
35437  */
35438  
35439
35440 /**
35441  * @class Roo.tree.ColumnTree
35442  * @extends Roo.data.TreePanel
35443  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35444  * @cfg {int} borderWidth  compined right/left border allowance
35445  * @constructor
35446  * @param {String/HTMLElement/Element} el The container element
35447  * @param {Object} config
35448  */
35449 Roo.tree.ColumnTree =  function(el, config)
35450 {
35451    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35452    this.addEvents({
35453         /**
35454         * @event resize
35455         * Fire this event on a container when it resizes
35456         * @param {int} w Width
35457         * @param {int} h Height
35458         */
35459        "resize" : true
35460     });
35461     this.on('resize', this.onResize, this);
35462 };
35463
35464 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35465     //lines:false,
35466     
35467     
35468     borderWidth: Roo.isBorderBox ? 0 : 2, 
35469     headEls : false,
35470     
35471     render : function(){
35472         // add the header.....
35473        
35474         Roo.tree.ColumnTree.superclass.render.apply(this);
35475         
35476         this.el.addClass('x-column-tree');
35477         
35478         this.headers = this.el.createChild(
35479             {cls:'x-tree-headers'},this.innerCt.dom);
35480    
35481         var cols = this.columns, c;
35482         var totalWidth = 0;
35483         this.headEls = [];
35484         var  len = cols.length;
35485         for(var i = 0; i < len; i++){
35486              c = cols[i];
35487              totalWidth += c.width;
35488             this.headEls.push(this.headers.createChild({
35489                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35490                  cn: {
35491                      cls:'x-tree-hd-text',
35492                      html: c.header
35493                  },
35494                  style:'width:'+(c.width-this.borderWidth)+'px;'
35495              }));
35496         }
35497         this.headers.createChild({cls:'x-clear'});
35498         // prevent floats from wrapping when clipped
35499         this.headers.setWidth(totalWidth);
35500         //this.innerCt.setWidth(totalWidth);
35501         this.innerCt.setStyle({ overflow: 'auto' });
35502         this.onResize(this.width, this.height);
35503              
35504         
35505     },
35506     onResize : function(w,h)
35507     {
35508         this.height = h;
35509         this.width = w;
35510         // resize cols..
35511         this.innerCt.setWidth(this.width);
35512         this.innerCt.setHeight(this.height-20);
35513         
35514         // headers...
35515         var cols = this.columns, c;
35516         var totalWidth = 0;
35517         var expEl = false;
35518         var len = cols.length;
35519         for(var i = 0; i < len; i++){
35520             c = cols[i];
35521             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35522                 // it's the expander..
35523                 expEl  = this.headEls[i];
35524                 continue;
35525             }
35526             totalWidth += c.width;
35527             
35528         }
35529         if (expEl) {
35530             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35531         }
35532         this.headers.setWidth(w-20);
35533
35534         
35535         
35536         
35537     }
35538 });
35539 /*
35540  * Based on:
35541  * Ext JS Library 1.1.1
35542  * Copyright(c) 2006-2007, Ext JS, LLC.
35543  *
35544  * Originally Released Under LGPL - original licence link has changed is not relivant.
35545  *
35546  * Fork - LGPL
35547  * <script type="text/javascript">
35548  */
35549  
35550 /**
35551  * @class Roo.menu.Menu
35552  * @extends Roo.util.Observable
35553  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35554  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35555  * @constructor
35556  * Creates a new Menu
35557  * @param {Object} config Configuration options
35558  */
35559 Roo.menu.Menu = function(config){
35560     Roo.apply(this, config);
35561     this.id = this.id || Roo.id();
35562     this.addEvents({
35563         /**
35564          * @event beforeshow
35565          * Fires before this menu is displayed
35566          * @param {Roo.menu.Menu} this
35567          */
35568         beforeshow : true,
35569         /**
35570          * @event beforehide
35571          * Fires before this menu is hidden
35572          * @param {Roo.menu.Menu} this
35573          */
35574         beforehide : true,
35575         /**
35576          * @event show
35577          * Fires after this menu is displayed
35578          * @param {Roo.menu.Menu} this
35579          */
35580         show : true,
35581         /**
35582          * @event hide
35583          * Fires after this menu is hidden
35584          * @param {Roo.menu.Menu} this
35585          */
35586         hide : true,
35587         /**
35588          * @event click
35589          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35590          * @param {Roo.menu.Menu} this
35591          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35592          * @param {Roo.EventObject} e
35593          */
35594         click : true,
35595         /**
35596          * @event mouseover
35597          * Fires when the mouse is hovering over this menu
35598          * @param {Roo.menu.Menu} this
35599          * @param {Roo.EventObject} e
35600          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35601          */
35602         mouseover : true,
35603         /**
35604          * @event mouseout
35605          * Fires when the mouse exits this menu
35606          * @param {Roo.menu.Menu} this
35607          * @param {Roo.EventObject} e
35608          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35609          */
35610         mouseout : true,
35611         /**
35612          * @event itemclick
35613          * Fires when a menu item contained in this menu is clicked
35614          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35615          * @param {Roo.EventObject} e
35616          */
35617         itemclick: true
35618     });
35619     if (this.registerMenu) {
35620         Roo.menu.MenuMgr.register(this);
35621     }
35622     
35623     var mis = this.items;
35624     this.items = new Roo.util.MixedCollection();
35625     if(mis){
35626         this.add.apply(this, mis);
35627     }
35628 };
35629
35630 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35631     /**
35632      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35633      */
35634     minWidth : 120,
35635     /**
35636      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35637      * for bottom-right shadow (defaults to "sides")
35638      */
35639     shadow : "sides",
35640     /**
35641      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35642      * this menu (defaults to "tl-tr?")
35643      */
35644     subMenuAlign : "tl-tr?",
35645     /**
35646      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35647      * relative to its element of origin (defaults to "tl-bl?")
35648      */
35649     defaultAlign : "tl-bl?",
35650     /**
35651      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35652      */
35653     allowOtherMenus : false,
35654     /**
35655      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35656      */
35657     registerMenu : true,
35658
35659     hidden:true,
35660
35661     // private
35662     render : function(){
35663         if(this.el){
35664             return;
35665         }
35666         var el = this.el = new Roo.Layer({
35667             cls: "x-menu",
35668             shadow:this.shadow,
35669             constrain: false,
35670             parentEl: this.parentEl || document.body,
35671             zindex:15000
35672         });
35673
35674         this.keyNav = new Roo.menu.MenuNav(this);
35675
35676         if(this.plain){
35677             el.addClass("x-menu-plain");
35678         }
35679         if(this.cls){
35680             el.addClass(this.cls);
35681         }
35682         // generic focus element
35683         this.focusEl = el.createChild({
35684             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35685         });
35686         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35687         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35688         
35689         ul.on("mouseover", this.onMouseOver, this);
35690         ul.on("mouseout", this.onMouseOut, this);
35691         this.items.each(function(item){
35692             if (item.hidden) {
35693                 return;
35694             }
35695             
35696             var li = document.createElement("li");
35697             li.className = "x-menu-list-item";
35698             ul.dom.appendChild(li);
35699             item.render(li, this);
35700         }, this);
35701         this.ul = ul;
35702         this.autoWidth();
35703     },
35704
35705     // private
35706     autoWidth : function(){
35707         var el = this.el, ul = this.ul;
35708         if(!el){
35709             return;
35710         }
35711         var w = this.width;
35712         if(w){
35713             el.setWidth(w);
35714         }else if(Roo.isIE){
35715             el.setWidth(this.minWidth);
35716             var t = el.dom.offsetWidth; // force recalc
35717             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35718         }
35719     },
35720
35721     // private
35722     delayAutoWidth : function(){
35723         if(this.rendered){
35724             if(!this.awTask){
35725                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35726             }
35727             this.awTask.delay(20);
35728         }
35729     },
35730
35731     // private
35732     findTargetItem : function(e){
35733         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35734         if(t && t.menuItemId){
35735             return this.items.get(t.menuItemId);
35736         }
35737     },
35738
35739     // private
35740     onClick : function(e){
35741         Roo.log("menu.onClick");
35742         var t = this.findTargetItem(e);
35743         if(!t){
35744             return;
35745         }
35746         Roo.log(e);
35747         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35748             if(t == this.activeItem && t.shouldDeactivate(e)){
35749                 this.activeItem.deactivate();
35750                 delete this.activeItem;
35751                 return;
35752             }
35753             if(t.canActivate){
35754                 this.setActiveItem(t, true);
35755             }
35756             return;
35757             
35758             
35759         }
35760         
35761         t.onClick(e);
35762         this.fireEvent("click", this, t, e);
35763     },
35764
35765     // private
35766     setActiveItem : function(item, autoExpand){
35767         if(item != this.activeItem){
35768             if(this.activeItem){
35769                 this.activeItem.deactivate();
35770             }
35771             this.activeItem = item;
35772             item.activate(autoExpand);
35773         }else if(autoExpand){
35774             item.expandMenu();
35775         }
35776     },
35777
35778     // private
35779     tryActivate : function(start, step){
35780         var items = this.items;
35781         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35782             var item = items.get(i);
35783             if(!item.disabled && item.canActivate){
35784                 this.setActiveItem(item, false);
35785                 return item;
35786             }
35787         }
35788         return false;
35789     },
35790
35791     // private
35792     onMouseOver : function(e){
35793         var t;
35794         if(t = this.findTargetItem(e)){
35795             if(t.canActivate && !t.disabled){
35796                 this.setActiveItem(t, true);
35797             }
35798         }
35799         this.fireEvent("mouseover", this, e, t);
35800     },
35801
35802     // private
35803     onMouseOut : function(e){
35804         var t;
35805         if(t = this.findTargetItem(e)){
35806             if(t == this.activeItem && t.shouldDeactivate(e)){
35807                 this.activeItem.deactivate();
35808                 delete this.activeItem;
35809             }
35810         }
35811         this.fireEvent("mouseout", this, e, t);
35812     },
35813
35814     /**
35815      * Read-only.  Returns true if the menu is currently displayed, else false.
35816      * @type Boolean
35817      */
35818     isVisible : function(){
35819         return this.el && !this.hidden;
35820     },
35821
35822     /**
35823      * Displays this menu relative to another element
35824      * @param {String/HTMLElement/Roo.Element} element The element to align to
35825      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35826      * the element (defaults to this.defaultAlign)
35827      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35828      */
35829     show : function(el, pos, parentMenu){
35830         this.parentMenu = parentMenu;
35831         if(!this.el){
35832             this.render();
35833         }
35834         this.fireEvent("beforeshow", this);
35835         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35836     },
35837
35838     /**
35839      * Displays this menu at a specific xy position
35840      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35841      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35842      */
35843     showAt : function(xy, parentMenu, /* private: */_e){
35844         this.parentMenu = parentMenu;
35845         if(!this.el){
35846             this.render();
35847         }
35848         if(_e !== false){
35849             this.fireEvent("beforeshow", this);
35850             xy = this.el.adjustForConstraints(xy);
35851         }
35852         this.el.setXY(xy);
35853         this.el.show();
35854         this.hidden = false;
35855         this.focus();
35856         this.fireEvent("show", this);
35857     },
35858
35859     focus : function(){
35860         if(!this.hidden){
35861             this.doFocus.defer(50, this);
35862         }
35863     },
35864
35865     doFocus : function(){
35866         if(!this.hidden){
35867             this.focusEl.focus();
35868         }
35869     },
35870
35871     /**
35872      * Hides this menu and optionally all parent menus
35873      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35874      */
35875     hide : function(deep){
35876         if(this.el && this.isVisible()){
35877             this.fireEvent("beforehide", this);
35878             if(this.activeItem){
35879                 this.activeItem.deactivate();
35880                 this.activeItem = null;
35881             }
35882             this.el.hide();
35883             this.hidden = true;
35884             this.fireEvent("hide", this);
35885         }
35886         if(deep === true && this.parentMenu){
35887             this.parentMenu.hide(true);
35888         }
35889     },
35890
35891     /**
35892      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35893      * Any of the following are valid:
35894      * <ul>
35895      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35896      * <li>An HTMLElement object which will be converted to a menu item</li>
35897      * <li>A menu item config object that will be created as a new menu item</li>
35898      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35899      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35900      * </ul>
35901      * Usage:
35902      * <pre><code>
35903 // Create the menu
35904 var menu = new Roo.menu.Menu();
35905
35906 // Create a menu item to add by reference
35907 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35908
35909 // Add a bunch of items at once using different methods.
35910 // Only the last item added will be returned.
35911 var item = menu.add(
35912     menuItem,                // add existing item by ref
35913     'Dynamic Item',          // new TextItem
35914     '-',                     // new separator
35915     { text: 'Config Item' }  // new item by config
35916 );
35917 </code></pre>
35918      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35919      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35920      */
35921     add : function(){
35922         var a = arguments, l = a.length, item;
35923         for(var i = 0; i < l; i++){
35924             var el = a[i];
35925             if ((typeof(el) == "object") && el.xtype && el.xns) {
35926                 el = Roo.factory(el, Roo.menu);
35927             }
35928             
35929             if(el.render){ // some kind of Item
35930                 item = this.addItem(el);
35931             }else if(typeof el == "string"){ // string
35932                 if(el == "separator" || el == "-"){
35933                     item = this.addSeparator();
35934                 }else{
35935                     item = this.addText(el);
35936                 }
35937             }else if(el.tagName || el.el){ // element
35938                 item = this.addElement(el);
35939             }else if(typeof el == "object"){ // must be menu item config?
35940                 item = this.addMenuItem(el);
35941             }
35942         }
35943         return item;
35944     },
35945
35946     /**
35947      * Returns this menu's underlying {@link Roo.Element} object
35948      * @return {Roo.Element} The element
35949      */
35950     getEl : function(){
35951         if(!this.el){
35952             this.render();
35953         }
35954         return this.el;
35955     },
35956
35957     /**
35958      * Adds a separator bar to the menu
35959      * @return {Roo.menu.Item} The menu item that was added
35960      */
35961     addSeparator : function(){
35962         return this.addItem(new Roo.menu.Separator());
35963     },
35964
35965     /**
35966      * Adds an {@link Roo.Element} object to the menu
35967      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35968      * @return {Roo.menu.Item} The menu item that was added
35969      */
35970     addElement : function(el){
35971         return this.addItem(new Roo.menu.BaseItem(el));
35972     },
35973
35974     /**
35975      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35976      * @param {Roo.menu.Item} item The menu item to add
35977      * @return {Roo.menu.Item} The menu item that was added
35978      */
35979     addItem : function(item){
35980         this.items.add(item);
35981         if(this.ul){
35982             var li = document.createElement("li");
35983             li.className = "x-menu-list-item";
35984             this.ul.dom.appendChild(li);
35985             item.render(li, this);
35986             this.delayAutoWidth();
35987         }
35988         return item;
35989     },
35990
35991     /**
35992      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35993      * @param {Object} config A MenuItem config object
35994      * @return {Roo.menu.Item} The menu item that was added
35995      */
35996     addMenuItem : function(config){
35997         if(!(config instanceof Roo.menu.Item)){
35998             if(typeof config.checked == "boolean"){ // must be check menu item config?
35999                 config = new Roo.menu.CheckItem(config);
36000             }else{
36001                 config = new Roo.menu.Item(config);
36002             }
36003         }
36004         return this.addItem(config);
36005     },
36006
36007     /**
36008      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36009      * @param {String} text The text to display in the menu item
36010      * @return {Roo.menu.Item} The menu item that was added
36011      */
36012     addText : function(text){
36013         return this.addItem(new Roo.menu.TextItem({ text : text }));
36014     },
36015
36016     /**
36017      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36018      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36019      * @param {Roo.menu.Item} item The menu item to add
36020      * @return {Roo.menu.Item} The menu item that was added
36021      */
36022     insert : function(index, item){
36023         this.items.insert(index, item);
36024         if(this.ul){
36025             var li = document.createElement("li");
36026             li.className = "x-menu-list-item";
36027             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36028             item.render(li, this);
36029             this.delayAutoWidth();
36030         }
36031         return item;
36032     },
36033
36034     /**
36035      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36036      * @param {Roo.menu.Item} item The menu item to remove
36037      */
36038     remove : function(item){
36039         this.items.removeKey(item.id);
36040         item.destroy();
36041     },
36042
36043     /**
36044      * Removes and destroys all items in the menu
36045      */
36046     removeAll : function(){
36047         var f;
36048         while(f = this.items.first()){
36049             this.remove(f);
36050         }
36051     }
36052 });
36053
36054 // MenuNav is a private utility class used internally by the Menu
36055 Roo.menu.MenuNav = function(menu){
36056     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36057     this.scope = this.menu = menu;
36058 };
36059
36060 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36061     doRelay : function(e, h){
36062         var k = e.getKey();
36063         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36064             this.menu.tryActivate(0, 1);
36065             return false;
36066         }
36067         return h.call(this.scope || this, e, this.menu);
36068     },
36069
36070     up : function(e, m){
36071         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36072             m.tryActivate(m.items.length-1, -1);
36073         }
36074     },
36075
36076     down : function(e, m){
36077         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36078             m.tryActivate(0, 1);
36079         }
36080     },
36081
36082     right : function(e, m){
36083         if(m.activeItem){
36084             m.activeItem.expandMenu(true);
36085         }
36086     },
36087
36088     left : function(e, m){
36089         m.hide();
36090         if(m.parentMenu && m.parentMenu.activeItem){
36091             m.parentMenu.activeItem.activate();
36092         }
36093     },
36094
36095     enter : function(e, m){
36096         if(m.activeItem){
36097             e.stopPropagation();
36098             m.activeItem.onClick(e);
36099             m.fireEvent("click", this, m.activeItem);
36100             return true;
36101         }
36102     }
36103 });/*
36104  * Based on:
36105  * Ext JS Library 1.1.1
36106  * Copyright(c) 2006-2007, Ext JS, LLC.
36107  *
36108  * Originally Released Under LGPL - original licence link has changed is not relivant.
36109  *
36110  * Fork - LGPL
36111  * <script type="text/javascript">
36112  */
36113  
36114 /**
36115  * @class Roo.menu.MenuMgr
36116  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36117  * @singleton
36118  */
36119 Roo.menu.MenuMgr = function(){
36120    var menus, active, groups = {}, attached = false, lastShow = new Date();
36121
36122    // private - called when first menu is created
36123    function init(){
36124        menus = {};
36125        active = new Roo.util.MixedCollection();
36126        Roo.get(document).addKeyListener(27, function(){
36127            if(active.length > 0){
36128                hideAll();
36129            }
36130        });
36131    }
36132
36133    // private
36134    function hideAll(){
36135        if(active && active.length > 0){
36136            var c = active.clone();
36137            c.each(function(m){
36138                m.hide();
36139            });
36140        }
36141    }
36142
36143    // private
36144    function onHide(m){
36145        active.remove(m);
36146        if(active.length < 1){
36147            Roo.get(document).un("mousedown", onMouseDown);
36148            attached = false;
36149        }
36150    }
36151
36152    // private
36153    function onShow(m){
36154        var last = active.last();
36155        lastShow = new Date();
36156        active.add(m);
36157        if(!attached){
36158            Roo.get(document).on("mousedown", onMouseDown);
36159            attached = true;
36160        }
36161        if(m.parentMenu){
36162           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36163           m.parentMenu.activeChild = m;
36164        }else if(last && last.isVisible()){
36165           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36166        }
36167    }
36168
36169    // private
36170    function onBeforeHide(m){
36171        if(m.activeChild){
36172            m.activeChild.hide();
36173        }
36174        if(m.autoHideTimer){
36175            clearTimeout(m.autoHideTimer);
36176            delete m.autoHideTimer;
36177        }
36178    }
36179
36180    // private
36181    function onBeforeShow(m){
36182        var pm = m.parentMenu;
36183        if(!pm && !m.allowOtherMenus){
36184            hideAll();
36185        }else if(pm && pm.activeChild && active != m){
36186            pm.activeChild.hide();
36187        }
36188    }
36189
36190    // private
36191    function onMouseDown(e){
36192        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36193            hideAll();
36194        }
36195    }
36196
36197    // private
36198    function onBeforeCheck(mi, state){
36199        if(state){
36200            var g = groups[mi.group];
36201            for(var i = 0, l = g.length; i < l; i++){
36202                if(g[i] != mi){
36203                    g[i].setChecked(false);
36204                }
36205            }
36206        }
36207    }
36208
36209    return {
36210
36211        /**
36212         * Hides all menus that are currently visible
36213         */
36214        hideAll : function(){
36215             hideAll();  
36216        },
36217
36218        // private
36219        register : function(menu){
36220            if(!menus){
36221                init();
36222            }
36223            menus[menu.id] = menu;
36224            menu.on("beforehide", onBeforeHide);
36225            menu.on("hide", onHide);
36226            menu.on("beforeshow", onBeforeShow);
36227            menu.on("show", onShow);
36228            var g = menu.group;
36229            if(g && menu.events["checkchange"]){
36230                if(!groups[g]){
36231                    groups[g] = [];
36232                }
36233                groups[g].push(menu);
36234                menu.on("checkchange", onCheck);
36235            }
36236        },
36237
36238         /**
36239          * Returns a {@link Roo.menu.Menu} object
36240          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36241          * be used to generate and return a new Menu instance.
36242          */
36243        get : function(menu){
36244            if(typeof menu == "string"){ // menu id
36245                return menus[menu];
36246            }else if(menu.events){  // menu instance
36247                return menu;
36248            }else if(typeof menu.length == 'number'){ // array of menu items?
36249                return new Roo.menu.Menu({items:menu});
36250            }else{ // otherwise, must be a config
36251                return new Roo.menu.Menu(menu);
36252            }
36253        },
36254
36255        // private
36256        unregister : function(menu){
36257            delete menus[menu.id];
36258            menu.un("beforehide", onBeforeHide);
36259            menu.un("hide", onHide);
36260            menu.un("beforeshow", onBeforeShow);
36261            menu.un("show", onShow);
36262            var g = menu.group;
36263            if(g && menu.events["checkchange"]){
36264                groups[g].remove(menu);
36265                menu.un("checkchange", onCheck);
36266            }
36267        },
36268
36269        // private
36270        registerCheckable : function(menuItem){
36271            var g = menuItem.group;
36272            if(g){
36273                if(!groups[g]){
36274                    groups[g] = [];
36275                }
36276                groups[g].push(menuItem);
36277                menuItem.on("beforecheckchange", onBeforeCheck);
36278            }
36279        },
36280
36281        // private
36282        unregisterCheckable : function(menuItem){
36283            var g = menuItem.group;
36284            if(g){
36285                groups[g].remove(menuItem);
36286                menuItem.un("beforecheckchange", onBeforeCheck);
36287            }
36288        }
36289    };
36290 }();/*
36291  * Based on:
36292  * Ext JS Library 1.1.1
36293  * Copyright(c) 2006-2007, Ext JS, LLC.
36294  *
36295  * Originally Released Under LGPL - original licence link has changed is not relivant.
36296  *
36297  * Fork - LGPL
36298  * <script type="text/javascript">
36299  */
36300  
36301
36302 /**
36303  * @class Roo.menu.BaseItem
36304  * @extends Roo.Component
36305  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36306  * management and base configuration options shared by all menu components.
36307  * @constructor
36308  * Creates a new BaseItem
36309  * @param {Object} config Configuration options
36310  */
36311 Roo.menu.BaseItem = function(config){
36312     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36313
36314     this.addEvents({
36315         /**
36316          * @event click
36317          * Fires when this item is clicked
36318          * @param {Roo.menu.BaseItem} this
36319          * @param {Roo.EventObject} e
36320          */
36321         click: true,
36322         /**
36323          * @event activate
36324          * Fires when this item is activated
36325          * @param {Roo.menu.BaseItem} this
36326          */
36327         activate : true,
36328         /**
36329          * @event deactivate
36330          * Fires when this item is deactivated
36331          * @param {Roo.menu.BaseItem} this
36332          */
36333         deactivate : true
36334     });
36335
36336     if(this.handler){
36337         this.on("click", this.handler, this.scope, true);
36338     }
36339 };
36340
36341 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36342     /**
36343      * @cfg {Function} handler
36344      * A function that will handle the click event of this menu item (defaults to undefined)
36345      */
36346     /**
36347      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36348      */
36349     canActivate : false,
36350     
36351      /**
36352      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36353      */
36354     hidden: false,
36355     
36356     /**
36357      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36358      */
36359     activeClass : "x-menu-item-active",
36360     /**
36361      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36362      */
36363     hideOnClick : true,
36364     /**
36365      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36366      */
36367     hideDelay : 100,
36368
36369     // private
36370     ctype: "Roo.menu.BaseItem",
36371
36372     // private
36373     actionMode : "container",
36374
36375     // private
36376     render : function(container, parentMenu){
36377         this.parentMenu = parentMenu;
36378         Roo.menu.BaseItem.superclass.render.call(this, container);
36379         this.container.menuItemId = this.id;
36380     },
36381
36382     // private
36383     onRender : function(container, position){
36384         this.el = Roo.get(this.el);
36385         container.dom.appendChild(this.el.dom);
36386     },
36387
36388     // private
36389     onClick : function(e){
36390         if(!this.disabled && this.fireEvent("click", this, e) !== false
36391                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36392             this.handleClick(e);
36393         }else{
36394             e.stopEvent();
36395         }
36396     },
36397
36398     // private
36399     activate : function(){
36400         if(this.disabled){
36401             return false;
36402         }
36403         var li = this.container;
36404         li.addClass(this.activeClass);
36405         this.region = li.getRegion().adjust(2, 2, -2, -2);
36406         this.fireEvent("activate", this);
36407         return true;
36408     },
36409
36410     // private
36411     deactivate : function(){
36412         this.container.removeClass(this.activeClass);
36413         this.fireEvent("deactivate", this);
36414     },
36415
36416     // private
36417     shouldDeactivate : function(e){
36418         return !this.region || !this.region.contains(e.getPoint());
36419     },
36420
36421     // private
36422     handleClick : function(e){
36423         if(this.hideOnClick){
36424             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36425         }
36426     },
36427
36428     // private
36429     expandMenu : function(autoActivate){
36430         // do nothing
36431     },
36432
36433     // private
36434     hideMenu : function(){
36435         // do nothing
36436     }
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447  
36448 /**
36449  * @class Roo.menu.Adapter
36450  * @extends Roo.menu.BaseItem
36451  * 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.
36452  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36453  * @constructor
36454  * Creates a new Adapter
36455  * @param {Object} config Configuration options
36456  */
36457 Roo.menu.Adapter = function(component, config){
36458     Roo.menu.Adapter.superclass.constructor.call(this, config);
36459     this.component = component;
36460 };
36461 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36462     // private
36463     canActivate : true,
36464
36465     // private
36466     onRender : function(container, position){
36467         this.component.render(container);
36468         this.el = this.component.getEl();
36469     },
36470
36471     // private
36472     activate : function(){
36473         if(this.disabled){
36474             return false;
36475         }
36476         this.component.focus();
36477         this.fireEvent("activate", this);
36478         return true;
36479     },
36480
36481     // private
36482     deactivate : function(){
36483         this.fireEvent("deactivate", this);
36484     },
36485
36486     // private
36487     disable : function(){
36488         this.component.disable();
36489         Roo.menu.Adapter.superclass.disable.call(this);
36490     },
36491
36492     // private
36493     enable : function(){
36494         this.component.enable();
36495         Roo.menu.Adapter.superclass.enable.call(this);
36496     }
36497 });/*
36498  * Based on:
36499  * Ext JS Library 1.1.1
36500  * Copyright(c) 2006-2007, Ext JS, LLC.
36501  *
36502  * Originally Released Under LGPL - original licence link has changed is not relivant.
36503  *
36504  * Fork - LGPL
36505  * <script type="text/javascript">
36506  */
36507
36508 /**
36509  * @class Roo.menu.TextItem
36510  * @extends Roo.menu.BaseItem
36511  * Adds a static text string to a menu, usually used as either a heading or group separator.
36512  * Note: old style constructor with text is still supported.
36513  * 
36514  * @constructor
36515  * Creates a new TextItem
36516  * @param {Object} cfg Configuration
36517  */
36518 Roo.menu.TextItem = function(cfg){
36519     if (typeof(cfg) == 'string') {
36520         this.text = cfg;
36521     } else {
36522         Roo.apply(this,cfg);
36523     }
36524     
36525     Roo.menu.TextItem.superclass.constructor.call(this);
36526 };
36527
36528 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36529     /**
36530      * @cfg {Boolean} text Text to show on item.
36531      */
36532     text : '',
36533     
36534     /**
36535      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36536      */
36537     hideOnClick : false,
36538     /**
36539      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36540      */
36541     itemCls : "x-menu-text",
36542
36543     // private
36544     onRender : function(){
36545         var s = document.createElement("span");
36546         s.className = this.itemCls;
36547         s.innerHTML = this.text;
36548         this.el = s;
36549         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36550     }
36551 });/*
36552  * Based on:
36553  * Ext JS Library 1.1.1
36554  * Copyright(c) 2006-2007, Ext JS, LLC.
36555  *
36556  * Originally Released Under LGPL - original licence link has changed is not relivant.
36557  *
36558  * Fork - LGPL
36559  * <script type="text/javascript">
36560  */
36561
36562 /**
36563  * @class Roo.menu.Separator
36564  * @extends Roo.menu.BaseItem
36565  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36566  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36567  * @constructor
36568  * @param {Object} config Configuration options
36569  */
36570 Roo.menu.Separator = function(config){
36571     Roo.menu.Separator.superclass.constructor.call(this, config);
36572 };
36573
36574 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36575     /**
36576      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36577      */
36578     itemCls : "x-menu-sep",
36579     /**
36580      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36581      */
36582     hideOnClick : false,
36583
36584     // private
36585     onRender : function(li){
36586         var s = document.createElement("span");
36587         s.className = this.itemCls;
36588         s.innerHTML = "&#160;";
36589         this.el = s;
36590         li.addClass("x-menu-sep-li");
36591         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36592     }
36593 });/*
36594  * Based on:
36595  * Ext JS Library 1.1.1
36596  * Copyright(c) 2006-2007, Ext JS, LLC.
36597  *
36598  * Originally Released Under LGPL - original licence link has changed is not relivant.
36599  *
36600  * Fork - LGPL
36601  * <script type="text/javascript">
36602  */
36603 /**
36604  * @class Roo.menu.Item
36605  * @extends Roo.menu.BaseItem
36606  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36607  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36608  * activation and click handling.
36609  * @constructor
36610  * Creates a new Item
36611  * @param {Object} config Configuration options
36612  */
36613 Roo.menu.Item = function(config){
36614     Roo.menu.Item.superclass.constructor.call(this, config);
36615     if(this.menu){
36616         this.menu = Roo.menu.MenuMgr.get(this.menu);
36617     }
36618 };
36619 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36620     
36621     /**
36622      * @cfg {String} text
36623      * The text to show on the menu item.
36624      */
36625     text: '',
36626      /**
36627      * @cfg {String} HTML to render in menu
36628      * The text to show on the menu item (HTML version).
36629      */
36630     html: '',
36631     /**
36632      * @cfg {String} icon
36633      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36634      */
36635     icon: undefined,
36636     /**
36637      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36638      */
36639     itemCls : "x-menu-item",
36640     /**
36641      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36642      */
36643     canActivate : true,
36644     /**
36645      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36646      */
36647     showDelay: 200,
36648     // doc'd in BaseItem
36649     hideDelay: 200,
36650
36651     // private
36652     ctype: "Roo.menu.Item",
36653     
36654     // private
36655     onRender : function(container, position){
36656         var el = document.createElement("a");
36657         el.hideFocus = true;
36658         el.unselectable = "on";
36659         el.href = this.href || "#";
36660         if(this.hrefTarget){
36661             el.target = this.hrefTarget;
36662         }
36663         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36664         
36665         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36666         
36667         el.innerHTML = String.format(
36668                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36669                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36670         this.el = el;
36671         Roo.menu.Item.superclass.onRender.call(this, container, position);
36672     },
36673
36674     /**
36675      * Sets the text to display in this menu item
36676      * @param {String} text The text to display
36677      * @param {Boolean} isHTML true to indicate text is pure html.
36678      */
36679     setText : function(text, isHTML){
36680         if (isHTML) {
36681             this.html = text;
36682         } else {
36683             this.text = text;
36684             this.html = '';
36685         }
36686         if(this.rendered){
36687             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36688      
36689             this.el.update(String.format(
36690                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36691                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36692             this.parentMenu.autoWidth();
36693         }
36694     },
36695
36696     // private
36697     handleClick : function(e){
36698         if(!this.href){ // if no link defined, stop the event automatically
36699             e.stopEvent();
36700         }
36701         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36702     },
36703
36704     // private
36705     activate : function(autoExpand){
36706         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36707             this.focus();
36708             if(autoExpand){
36709                 this.expandMenu();
36710             }
36711         }
36712         return true;
36713     },
36714
36715     // private
36716     shouldDeactivate : function(e){
36717         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36718             if(this.menu && this.menu.isVisible()){
36719                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36720             }
36721             return true;
36722         }
36723         return false;
36724     },
36725
36726     // private
36727     deactivate : function(){
36728         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36729         this.hideMenu();
36730     },
36731
36732     // private
36733     expandMenu : function(autoActivate){
36734         if(!this.disabled && this.menu){
36735             clearTimeout(this.hideTimer);
36736             delete this.hideTimer;
36737             if(!this.menu.isVisible() && !this.showTimer){
36738                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36739             }else if (this.menu.isVisible() && autoActivate){
36740                 this.menu.tryActivate(0, 1);
36741             }
36742         }
36743     },
36744
36745     // private
36746     deferExpand : function(autoActivate){
36747         delete this.showTimer;
36748         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36749         if(autoActivate){
36750             this.menu.tryActivate(0, 1);
36751         }
36752     },
36753
36754     // private
36755     hideMenu : function(){
36756         clearTimeout(this.showTimer);
36757         delete this.showTimer;
36758         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36759             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36760         }
36761     },
36762
36763     // private
36764     deferHide : function(){
36765         delete this.hideTimer;
36766         this.menu.hide();
36767     }
36768 });/*
36769  * Based on:
36770  * Ext JS Library 1.1.1
36771  * Copyright(c) 2006-2007, Ext JS, LLC.
36772  *
36773  * Originally Released Under LGPL - original licence link has changed is not relivant.
36774  *
36775  * Fork - LGPL
36776  * <script type="text/javascript">
36777  */
36778  
36779 /**
36780  * @class Roo.menu.CheckItem
36781  * @extends Roo.menu.Item
36782  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36783  * @constructor
36784  * Creates a new CheckItem
36785  * @param {Object} config Configuration options
36786  */
36787 Roo.menu.CheckItem = function(config){
36788     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36789     this.addEvents({
36790         /**
36791          * @event beforecheckchange
36792          * Fires before the checked value is set, providing an opportunity to cancel if needed
36793          * @param {Roo.menu.CheckItem} this
36794          * @param {Boolean} checked The new checked value that will be set
36795          */
36796         "beforecheckchange" : true,
36797         /**
36798          * @event checkchange
36799          * Fires after the checked value has been set
36800          * @param {Roo.menu.CheckItem} this
36801          * @param {Boolean} checked The checked value that was set
36802          */
36803         "checkchange" : true
36804     });
36805     if(this.checkHandler){
36806         this.on('checkchange', this.checkHandler, this.scope);
36807     }
36808 };
36809 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36810     /**
36811      * @cfg {String} group
36812      * All check items with the same group name will automatically be grouped into a single-select
36813      * radio button group (defaults to '')
36814      */
36815     /**
36816      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36817      */
36818     itemCls : "x-menu-item x-menu-check-item",
36819     /**
36820      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36821      */
36822     groupClass : "x-menu-group-item",
36823
36824     /**
36825      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36826      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36827      * initialized with checked = true will be rendered as checked.
36828      */
36829     checked: false,
36830
36831     // private
36832     ctype: "Roo.menu.CheckItem",
36833
36834     // private
36835     onRender : function(c){
36836         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36837         if(this.group){
36838             this.el.addClass(this.groupClass);
36839         }
36840         Roo.menu.MenuMgr.registerCheckable(this);
36841         if(this.checked){
36842             this.checked = false;
36843             this.setChecked(true, true);
36844         }
36845     },
36846
36847     // private
36848     destroy : function(){
36849         if(this.rendered){
36850             Roo.menu.MenuMgr.unregisterCheckable(this);
36851         }
36852         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36853     },
36854
36855     /**
36856      * Set the checked state of this item
36857      * @param {Boolean} checked The new checked value
36858      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36859      */
36860     setChecked : function(state, suppressEvent){
36861         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36862             if(this.container){
36863                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36864             }
36865             this.checked = state;
36866             if(suppressEvent !== true){
36867                 this.fireEvent("checkchange", this, state);
36868             }
36869         }
36870     },
36871
36872     // private
36873     handleClick : function(e){
36874        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36875            this.setChecked(!this.checked);
36876        }
36877        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36878     }
36879 });/*
36880  * Based on:
36881  * Ext JS Library 1.1.1
36882  * Copyright(c) 2006-2007, Ext JS, LLC.
36883  *
36884  * Originally Released Under LGPL - original licence link has changed is not relivant.
36885  *
36886  * Fork - LGPL
36887  * <script type="text/javascript">
36888  */
36889  
36890 /**
36891  * @class Roo.menu.DateItem
36892  * @extends Roo.menu.Adapter
36893  * A menu item that wraps the {@link Roo.DatPicker} component.
36894  * @constructor
36895  * Creates a new DateItem
36896  * @param {Object} config Configuration options
36897  */
36898 Roo.menu.DateItem = function(config){
36899     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36900     /** The Roo.DatePicker object @type Roo.DatePicker */
36901     this.picker = this.component;
36902     this.addEvents({select: true});
36903     
36904     this.picker.on("render", function(picker){
36905         picker.getEl().swallowEvent("click");
36906         picker.container.addClass("x-menu-date-item");
36907     });
36908
36909     this.picker.on("select", this.onSelect, this);
36910 };
36911
36912 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36913     // private
36914     onSelect : function(picker, date){
36915         this.fireEvent("select", this, date, picker);
36916         Roo.menu.DateItem.superclass.handleClick.call(this);
36917     }
36918 });/*
36919  * Based on:
36920  * Ext JS Library 1.1.1
36921  * Copyright(c) 2006-2007, Ext JS, LLC.
36922  *
36923  * Originally Released Under LGPL - original licence link has changed is not relivant.
36924  *
36925  * Fork - LGPL
36926  * <script type="text/javascript">
36927  */
36928  
36929 /**
36930  * @class Roo.menu.ColorItem
36931  * @extends Roo.menu.Adapter
36932  * A menu item that wraps the {@link Roo.ColorPalette} component.
36933  * @constructor
36934  * Creates a new ColorItem
36935  * @param {Object} config Configuration options
36936  */
36937 Roo.menu.ColorItem = function(config){
36938     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36939     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36940     this.palette = this.component;
36941     this.relayEvents(this.palette, ["select"]);
36942     if(this.selectHandler){
36943         this.on('select', this.selectHandler, this.scope);
36944     }
36945 };
36946 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36947  * Based on:
36948  * Ext JS Library 1.1.1
36949  * Copyright(c) 2006-2007, Ext JS, LLC.
36950  *
36951  * Originally Released Under LGPL - original licence link has changed is not relivant.
36952  *
36953  * Fork - LGPL
36954  * <script type="text/javascript">
36955  */
36956  
36957
36958 /**
36959  * @class Roo.menu.DateMenu
36960  * @extends Roo.menu.Menu
36961  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36962  * @constructor
36963  * Creates a new DateMenu
36964  * @param {Object} config Configuration options
36965  */
36966 Roo.menu.DateMenu = function(config){
36967     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36968     this.plain = true;
36969     var di = new Roo.menu.DateItem(config);
36970     this.add(di);
36971     /**
36972      * The {@link Roo.DatePicker} instance for this DateMenu
36973      * @type DatePicker
36974      */
36975     this.picker = di.picker;
36976     /**
36977      * @event select
36978      * @param {DatePicker} picker
36979      * @param {Date} date
36980      */
36981     this.relayEvents(di, ["select"]);
36982     this.on('beforeshow', function(){
36983         if(this.picker){
36984             this.picker.hideMonthPicker(false);
36985         }
36986     }, this);
36987 };
36988 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36989     cls:'x-date-menu'
36990 });/*
36991  * Based on:
36992  * Ext JS Library 1.1.1
36993  * Copyright(c) 2006-2007, Ext JS, LLC.
36994  *
36995  * Originally Released Under LGPL - original licence link has changed is not relivant.
36996  *
36997  * Fork - LGPL
36998  * <script type="text/javascript">
36999  */
37000  
37001
37002 /**
37003  * @class Roo.menu.ColorMenu
37004  * @extends Roo.menu.Menu
37005  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37006  * @constructor
37007  * Creates a new ColorMenu
37008  * @param {Object} config Configuration options
37009  */
37010 Roo.menu.ColorMenu = function(config){
37011     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37012     this.plain = true;
37013     var ci = new Roo.menu.ColorItem(config);
37014     this.add(ci);
37015     /**
37016      * The {@link Roo.ColorPalette} instance for this ColorMenu
37017      * @type ColorPalette
37018      */
37019     this.palette = ci.palette;
37020     /**
37021      * @event select
37022      * @param {ColorPalette} palette
37023      * @param {String} color
37024      */
37025     this.relayEvents(ci, ["select"]);
37026 };
37027 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37028  * Based on:
37029  * Ext JS Library 1.1.1
37030  * Copyright(c) 2006-2007, Ext JS, LLC.
37031  *
37032  * Originally Released Under LGPL - original licence link has changed is not relivant.
37033  *
37034  * Fork - LGPL
37035  * <script type="text/javascript">
37036  */
37037  
37038 /**
37039  * @class Roo.form.Field
37040  * @extends Roo.BoxComponent
37041  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37042  * @constructor
37043  * Creates a new Field
37044  * @param {Object} config Configuration options
37045  */
37046 Roo.form.Field = function(config){
37047     Roo.form.Field.superclass.constructor.call(this, config);
37048 };
37049
37050 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37051     /**
37052      * @cfg {String} fieldLabel Label to use when rendering a form.
37053      */
37054        /**
37055      * @cfg {String} qtip Mouse over tip
37056      */
37057      
37058     /**
37059      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37060      */
37061     invalidClass : "x-form-invalid",
37062     /**
37063      * @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")
37064      */
37065     invalidText : "The value in this field is invalid",
37066     /**
37067      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37068      */
37069     focusClass : "x-form-focus",
37070     /**
37071      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37072       automatic validation (defaults to "keyup").
37073      */
37074     validationEvent : "keyup",
37075     /**
37076      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37077      */
37078     validateOnBlur : true,
37079     /**
37080      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37081      */
37082     validationDelay : 250,
37083     /**
37084      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37085      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37086      */
37087     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37088     /**
37089      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37090      */
37091     fieldClass : "x-form-field",
37092     /**
37093      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37094      *<pre>
37095 Value         Description
37096 -----------   ----------------------------------------------------------------------
37097 qtip          Display a quick tip when the user hovers over the field
37098 title         Display a default browser title attribute popup
37099 under         Add a block div beneath the field containing the error text
37100 side          Add an error icon to the right of the field with a popup on hover
37101 [element id]  Add the error text directly to the innerHTML of the specified element
37102 </pre>
37103      */
37104     msgTarget : 'qtip',
37105     /**
37106      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37107      */
37108     msgFx : 'normal',
37109
37110     /**
37111      * @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.
37112      */
37113     readOnly : false,
37114
37115     /**
37116      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37117      */
37118     disabled : false,
37119
37120     /**
37121      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37122      */
37123     inputType : undefined,
37124     
37125     /**
37126      * @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).
37127          */
37128         tabIndex : undefined,
37129         
37130     // private
37131     isFormField : true,
37132
37133     // private
37134     hasFocus : false,
37135     /**
37136      * @property {Roo.Element} fieldEl
37137      * Element Containing the rendered Field (with label etc.)
37138      */
37139     /**
37140      * @cfg {Mixed} value A value to initialize this field with.
37141      */
37142     value : undefined,
37143
37144     /**
37145      * @cfg {String} name The field's HTML name attribute.
37146      */
37147     /**
37148      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37149      */
37150
37151         // private ??
37152         initComponent : function(){
37153         Roo.form.Field.superclass.initComponent.call(this);
37154         this.addEvents({
37155             /**
37156              * @event focus
37157              * Fires when this field receives input focus.
37158              * @param {Roo.form.Field} this
37159              */
37160             focus : true,
37161             /**
37162              * @event blur
37163              * Fires when this field loses input focus.
37164              * @param {Roo.form.Field} this
37165              */
37166             blur : true,
37167             /**
37168              * @event specialkey
37169              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37170              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37171              * @param {Roo.form.Field} this
37172              * @param {Roo.EventObject} e The event object
37173              */
37174             specialkey : true,
37175             /**
37176              * @event change
37177              * Fires just before the field blurs if the field value has changed.
37178              * @param {Roo.form.Field} this
37179              * @param {Mixed} newValue The new value
37180              * @param {Mixed} oldValue The original value
37181              */
37182             change : true,
37183             /**
37184              * @event invalid
37185              * Fires after the field has been marked as invalid.
37186              * @param {Roo.form.Field} this
37187              * @param {String} msg The validation message
37188              */
37189             invalid : true,
37190             /**
37191              * @event valid
37192              * Fires after the field has been validated with no errors.
37193              * @param {Roo.form.Field} this
37194              */
37195             valid : true,
37196              /**
37197              * @event keyup
37198              * Fires after the key up
37199              * @param {Roo.form.Field} this
37200              * @param {Roo.EventObject}  e The event Object
37201              */
37202             keyup : true
37203         });
37204     },
37205
37206     /**
37207      * Returns the name attribute of the field if available
37208      * @return {String} name The field name
37209      */
37210     getName: function(){
37211          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37212     },
37213
37214     // private
37215     onRender : function(ct, position){
37216         Roo.form.Field.superclass.onRender.call(this, ct, position);
37217         if(!this.el){
37218             var cfg = this.getAutoCreate();
37219             if(!cfg.name){
37220                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37221             }
37222             if (!cfg.name.length) {
37223                 delete cfg.name;
37224             }
37225             if(this.inputType){
37226                 cfg.type = this.inputType;
37227             }
37228             this.el = ct.createChild(cfg, position);
37229         }
37230         var type = this.el.dom.type;
37231         if(type){
37232             if(type == 'password'){
37233                 type = 'text';
37234             }
37235             this.el.addClass('x-form-'+type);
37236         }
37237         if(this.readOnly){
37238             this.el.dom.readOnly = true;
37239         }
37240         if(this.tabIndex !== undefined){
37241             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37242         }
37243
37244         this.el.addClass([this.fieldClass, this.cls]);
37245         this.initValue();
37246     },
37247
37248     /**
37249      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37250      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37251      * @return {Roo.form.Field} this
37252      */
37253     applyTo : function(target){
37254         this.allowDomMove = false;
37255         this.el = Roo.get(target);
37256         this.render(this.el.dom.parentNode);
37257         return this;
37258     },
37259
37260     // private
37261     initValue : function(){
37262         if(this.value !== undefined){
37263             this.setValue(this.value);
37264         }else if(this.el.dom.value.length > 0){
37265             this.setValue(this.el.dom.value);
37266         }
37267     },
37268
37269     /**
37270      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37271      */
37272     isDirty : function() {
37273         if(this.disabled) {
37274             return false;
37275         }
37276         return String(this.getValue()) !== String(this.originalValue);
37277     },
37278
37279     // private
37280     afterRender : function(){
37281         Roo.form.Field.superclass.afterRender.call(this);
37282         this.initEvents();
37283     },
37284
37285     // private
37286     fireKey : function(e){
37287         //Roo.log('field ' + e.getKey());
37288         if(e.isNavKeyPress()){
37289             this.fireEvent("specialkey", this, e);
37290         }
37291     },
37292
37293     /**
37294      * Resets the current field value to the originally loaded value and clears any validation messages
37295      */
37296     reset : function(){
37297         this.setValue(this.resetValue);
37298         this.clearInvalid();
37299     },
37300
37301     // private
37302     initEvents : function(){
37303         // safari killled keypress - so keydown is now used..
37304         this.el.on("keydown" , this.fireKey,  this);
37305         this.el.on("focus", this.onFocus,  this);
37306         this.el.on("blur", this.onBlur,  this);
37307         this.el.relayEvent('keyup', this);
37308
37309         // reference to original value for reset
37310         this.originalValue = this.getValue();
37311         this.resetValue =  this.getValue();
37312     },
37313
37314     // private
37315     onFocus : function(){
37316         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37317             this.el.addClass(this.focusClass);
37318         }
37319         if(!this.hasFocus){
37320             this.hasFocus = true;
37321             this.startValue = this.getValue();
37322             this.fireEvent("focus", this);
37323         }
37324     },
37325
37326     beforeBlur : Roo.emptyFn,
37327
37328     // private
37329     onBlur : function(){
37330         this.beforeBlur();
37331         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37332             this.el.removeClass(this.focusClass);
37333         }
37334         this.hasFocus = false;
37335         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37336             this.validate();
37337         }
37338         var v = this.getValue();
37339         if(String(v) !== String(this.startValue)){
37340             this.fireEvent('change', this, v, this.startValue);
37341         }
37342         this.fireEvent("blur", this);
37343     },
37344
37345     /**
37346      * Returns whether or not the field value is currently valid
37347      * @param {Boolean} preventMark True to disable marking the field invalid
37348      * @return {Boolean} True if the value is valid, else false
37349      */
37350     isValid : function(preventMark){
37351         if(this.disabled){
37352             return true;
37353         }
37354         var restore = this.preventMark;
37355         this.preventMark = preventMark === true;
37356         var v = this.validateValue(this.processValue(this.getRawValue()));
37357         this.preventMark = restore;
37358         return v;
37359     },
37360
37361     /**
37362      * Validates the field value
37363      * @return {Boolean} True if the value is valid, else false
37364      */
37365     validate : function(){
37366         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37367             this.clearInvalid();
37368             return true;
37369         }
37370         return false;
37371     },
37372
37373     processValue : function(value){
37374         return value;
37375     },
37376
37377     // private
37378     // Subclasses should provide the validation implementation by overriding this
37379     validateValue : function(value){
37380         return true;
37381     },
37382
37383     /**
37384      * Mark this field as invalid
37385      * @param {String} msg The validation message
37386      */
37387     markInvalid : function(msg){
37388         if(!this.rendered || this.preventMark){ // not rendered
37389             return;
37390         }
37391         
37392         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37393         
37394         obj.el.addClass(this.invalidClass);
37395         msg = msg || this.invalidText;
37396         switch(this.msgTarget){
37397             case 'qtip':
37398                 obj.el.dom.qtip = msg;
37399                 obj.el.dom.qclass = 'x-form-invalid-tip';
37400                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37401                     Roo.QuickTips.enable();
37402                 }
37403                 break;
37404             case 'title':
37405                 this.el.dom.title = msg;
37406                 break;
37407             case 'under':
37408                 if(!this.errorEl){
37409                     var elp = this.el.findParent('.x-form-element', 5, true);
37410                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37411                     this.errorEl.setWidth(elp.getWidth(true)-20);
37412                 }
37413                 this.errorEl.update(msg);
37414                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37415                 break;
37416             case 'side':
37417                 if(!this.errorIcon){
37418                     var elp = this.el.findParent('.x-form-element', 5, true);
37419                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37420                 }
37421                 this.alignErrorIcon();
37422                 this.errorIcon.dom.qtip = msg;
37423                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37424                 this.errorIcon.show();
37425                 this.on('resize', this.alignErrorIcon, this);
37426                 break;
37427             default:
37428                 var t = Roo.getDom(this.msgTarget);
37429                 t.innerHTML = msg;
37430                 t.style.display = this.msgDisplay;
37431                 break;
37432         }
37433         this.fireEvent('invalid', this, msg);
37434     },
37435
37436     // private
37437     alignErrorIcon : function(){
37438         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37439     },
37440
37441     /**
37442      * Clear any invalid styles/messages for this field
37443      */
37444     clearInvalid : function(){
37445         if(!this.rendered || this.preventMark){ // not rendered
37446             return;
37447         }
37448         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37449         
37450         obj.el.removeClass(this.invalidClass);
37451         switch(this.msgTarget){
37452             case 'qtip':
37453                 obj.el.dom.qtip = '';
37454                 break;
37455             case 'title':
37456                 this.el.dom.title = '';
37457                 break;
37458             case 'under':
37459                 if(this.errorEl){
37460                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37461                 }
37462                 break;
37463             case 'side':
37464                 if(this.errorIcon){
37465                     this.errorIcon.dom.qtip = '';
37466                     this.errorIcon.hide();
37467                     this.un('resize', this.alignErrorIcon, this);
37468                 }
37469                 break;
37470             default:
37471                 var t = Roo.getDom(this.msgTarget);
37472                 t.innerHTML = '';
37473                 t.style.display = 'none';
37474                 break;
37475         }
37476         this.fireEvent('valid', this);
37477     },
37478
37479     /**
37480      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37481      * @return {Mixed} value The field value
37482      */
37483     getRawValue : function(){
37484         var v = this.el.getValue();
37485         
37486         return v;
37487     },
37488
37489     /**
37490      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37491      * @return {Mixed} value The field value
37492      */
37493     getValue : function(){
37494         var v = this.el.getValue();
37495          
37496         return v;
37497     },
37498
37499     /**
37500      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37501      * @param {Mixed} value The value to set
37502      */
37503     setRawValue : function(v){
37504         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37505     },
37506
37507     /**
37508      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37509      * @param {Mixed} value The value to set
37510      */
37511     setValue : function(v){
37512         this.value = v;
37513         if(this.rendered){
37514             this.el.dom.value = (v === null || v === undefined ? '' : v);
37515              this.validate();
37516         }
37517     },
37518
37519     adjustSize : function(w, h){
37520         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37521         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37522         return s;
37523     },
37524
37525     adjustWidth : function(tag, w){
37526         tag = tag.toLowerCase();
37527         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37528             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37529                 if(tag == 'input'){
37530                     return w + 2;
37531                 }
37532                 if(tag == 'textarea'){
37533                     return w-2;
37534                 }
37535             }else if(Roo.isOpera){
37536                 if(tag == 'input'){
37537                     return w + 2;
37538                 }
37539                 if(tag == 'textarea'){
37540                     return w-2;
37541                 }
37542             }
37543         }
37544         return w;
37545     }
37546 });
37547
37548
37549 // anything other than normal should be considered experimental
37550 Roo.form.Field.msgFx = {
37551     normal : {
37552         show: function(msgEl, f){
37553             msgEl.setDisplayed('block');
37554         },
37555
37556         hide : function(msgEl, f){
37557             msgEl.setDisplayed(false).update('');
37558         }
37559     },
37560
37561     slide : {
37562         show: function(msgEl, f){
37563             msgEl.slideIn('t', {stopFx:true});
37564         },
37565
37566         hide : function(msgEl, f){
37567             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37568         }
37569     },
37570
37571     slideRight : {
37572         show: function(msgEl, f){
37573             msgEl.fixDisplay();
37574             msgEl.alignTo(f.el, 'tl-tr');
37575             msgEl.slideIn('l', {stopFx:true});
37576         },
37577
37578         hide : function(msgEl, f){
37579             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37580         }
37581     }
37582 };/*
37583  * Based on:
37584  * Ext JS Library 1.1.1
37585  * Copyright(c) 2006-2007, Ext JS, LLC.
37586  *
37587  * Originally Released Under LGPL - original licence link has changed is not relivant.
37588  *
37589  * Fork - LGPL
37590  * <script type="text/javascript">
37591  */
37592  
37593
37594 /**
37595  * @class Roo.form.TextField
37596  * @extends Roo.form.Field
37597  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37598  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37599  * @constructor
37600  * Creates a new TextField
37601  * @param {Object} config Configuration options
37602  */
37603 Roo.form.TextField = function(config){
37604     Roo.form.TextField.superclass.constructor.call(this, config);
37605     this.addEvents({
37606         /**
37607          * @event autosize
37608          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37609          * according to the default logic, but this event provides a hook for the developer to apply additional
37610          * logic at runtime to resize the field if needed.
37611              * @param {Roo.form.Field} this This text field
37612              * @param {Number} width The new field width
37613              */
37614         autosize : true
37615     });
37616 };
37617
37618 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37619     /**
37620      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37621      */
37622     grow : false,
37623     /**
37624      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37625      */
37626     growMin : 30,
37627     /**
37628      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37629      */
37630     growMax : 800,
37631     /**
37632      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37633      */
37634     vtype : null,
37635     /**
37636      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37637      */
37638     maskRe : null,
37639     /**
37640      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37641      */
37642     disableKeyFilter : false,
37643     /**
37644      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37645      */
37646     allowBlank : true,
37647     /**
37648      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37649      */
37650     minLength : 0,
37651     /**
37652      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37653      */
37654     maxLength : Number.MAX_VALUE,
37655     /**
37656      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37657      */
37658     minLengthText : "The minimum length for this field is {0}",
37659     /**
37660      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37661      */
37662     maxLengthText : "The maximum length for this field is {0}",
37663     /**
37664      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37665      */
37666     selectOnFocus : false,
37667     /**
37668      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37669      */
37670     blankText : "This field is required",
37671     /**
37672      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37673      * If available, this function will be called only after the basic validators all return true, and will be passed the
37674      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37675      */
37676     validator : null,
37677     /**
37678      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37679      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37680      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37681      */
37682     regex : null,
37683     /**
37684      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37685      */
37686     regexText : "",
37687     /**
37688      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37689      */
37690     emptyText : null,
37691    
37692
37693     // private
37694     initEvents : function()
37695     {
37696         if (this.emptyText) {
37697             this.el.attr('placeholder', this.emptyText);
37698         }
37699         
37700         Roo.form.TextField.superclass.initEvents.call(this);
37701         if(this.validationEvent == 'keyup'){
37702             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37703             this.el.on('keyup', this.filterValidation, this);
37704         }
37705         else if(this.validationEvent !== false){
37706             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37707         }
37708         
37709         if(this.selectOnFocus){
37710             this.on("focus", this.preFocus, this);
37711             
37712         }
37713         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37714             this.el.on("keypress", this.filterKeys, this);
37715         }
37716         if(this.grow){
37717             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37718             this.el.on("click", this.autoSize,  this);
37719         }
37720         if(this.el.is('input[type=password]') && Roo.isSafari){
37721             this.el.on('keydown', this.SafariOnKeyDown, this);
37722         }
37723     },
37724
37725     processValue : function(value){
37726         if(this.stripCharsRe){
37727             var newValue = value.replace(this.stripCharsRe, '');
37728             if(newValue !== value){
37729                 this.setRawValue(newValue);
37730                 return newValue;
37731             }
37732         }
37733         return value;
37734     },
37735
37736     filterValidation : function(e){
37737         if(!e.isNavKeyPress()){
37738             this.validationTask.delay(this.validationDelay);
37739         }
37740     },
37741
37742     // private
37743     onKeyUp : function(e){
37744         if(!e.isNavKeyPress()){
37745             this.autoSize();
37746         }
37747     },
37748
37749     /**
37750      * Resets the current field value to the originally-loaded value and clears any validation messages.
37751      *  
37752      */
37753     reset : function(){
37754         Roo.form.TextField.superclass.reset.call(this);
37755        
37756     },
37757
37758     
37759     // private
37760     preFocus : function(){
37761         
37762         if(this.selectOnFocus){
37763             this.el.dom.select();
37764         }
37765     },
37766
37767     
37768     // private
37769     filterKeys : function(e){
37770         var k = e.getKey();
37771         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37772             return;
37773         }
37774         var c = e.getCharCode(), cc = String.fromCharCode(c);
37775         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37776             return;
37777         }
37778         if(!this.maskRe.test(cc)){
37779             e.stopEvent();
37780         }
37781     },
37782
37783     setValue : function(v){
37784         
37785         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37786         
37787         this.autoSize();
37788     },
37789
37790     /**
37791      * Validates a value according to the field's validation rules and marks the field as invalid
37792      * if the validation fails
37793      * @param {Mixed} value The value to validate
37794      * @return {Boolean} True if the value is valid, else false
37795      */
37796     validateValue : function(value){
37797         if(value.length < 1)  { // if it's blank
37798              if(this.allowBlank){
37799                 this.clearInvalid();
37800                 return true;
37801              }else{
37802                 this.markInvalid(this.blankText);
37803                 return false;
37804              }
37805         }
37806         if(value.length < this.minLength){
37807             this.markInvalid(String.format(this.minLengthText, this.minLength));
37808             return false;
37809         }
37810         if(value.length > this.maxLength){
37811             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37812             return false;
37813         }
37814         if(this.vtype){
37815             var vt = Roo.form.VTypes;
37816             if(!vt[this.vtype](value, this)){
37817                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37818                 return false;
37819             }
37820         }
37821         if(typeof this.validator == "function"){
37822             var msg = this.validator(value);
37823             if(msg !== true){
37824                 this.markInvalid(msg);
37825                 return false;
37826             }
37827         }
37828         if(this.regex && !this.regex.test(value)){
37829             this.markInvalid(this.regexText);
37830             return false;
37831         }
37832         return true;
37833     },
37834
37835     /**
37836      * Selects text in this field
37837      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37838      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37839      */
37840     selectText : function(start, end){
37841         var v = this.getRawValue();
37842         if(v.length > 0){
37843             start = start === undefined ? 0 : start;
37844             end = end === undefined ? v.length : end;
37845             var d = this.el.dom;
37846             if(d.setSelectionRange){
37847                 d.setSelectionRange(start, end);
37848             }else if(d.createTextRange){
37849                 var range = d.createTextRange();
37850                 range.moveStart("character", start);
37851                 range.moveEnd("character", v.length-end);
37852                 range.select();
37853             }
37854         }
37855     },
37856
37857     /**
37858      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37859      * This only takes effect if grow = true, and fires the autosize event.
37860      */
37861     autoSize : function(){
37862         if(!this.grow || !this.rendered){
37863             return;
37864         }
37865         if(!this.metrics){
37866             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37867         }
37868         var el = this.el;
37869         var v = el.dom.value;
37870         var d = document.createElement('div');
37871         d.appendChild(document.createTextNode(v));
37872         v = d.innerHTML;
37873         d = null;
37874         v += "&#160;";
37875         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37876         this.el.setWidth(w);
37877         this.fireEvent("autosize", this, w);
37878     },
37879     
37880     // private
37881     SafariOnKeyDown : function(event)
37882     {
37883         // this is a workaround for a password hang bug on chrome/ webkit.
37884         
37885         var isSelectAll = false;
37886         
37887         if(this.el.dom.selectionEnd > 0){
37888             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37889         }
37890         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37891             event.preventDefault();
37892             this.setValue('');
37893             return;
37894         }
37895         
37896         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37897             
37898             event.preventDefault();
37899             // this is very hacky as keydown always get's upper case.
37900             
37901             var cc = String.fromCharCode(event.getCharCode());
37902             
37903             
37904             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37905             
37906         }
37907         
37908         
37909     }
37910 });/*
37911  * Based on:
37912  * Ext JS Library 1.1.1
37913  * Copyright(c) 2006-2007, Ext JS, LLC.
37914  *
37915  * Originally Released Under LGPL - original licence link has changed is not relivant.
37916  *
37917  * Fork - LGPL
37918  * <script type="text/javascript">
37919  */
37920  
37921 /**
37922  * @class Roo.form.Hidden
37923  * @extends Roo.form.TextField
37924  * Simple Hidden element used on forms 
37925  * 
37926  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37927  * 
37928  * @constructor
37929  * Creates a new Hidden form element.
37930  * @param {Object} config Configuration options
37931  */
37932
37933
37934
37935 // easy hidden field...
37936 Roo.form.Hidden = function(config){
37937     Roo.form.Hidden.superclass.constructor.call(this, config);
37938 };
37939   
37940 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37941     fieldLabel:      '',
37942     inputType:      'hidden',
37943     width:          50,
37944     allowBlank:     true,
37945     labelSeparator: '',
37946     hidden:         true,
37947     itemCls :       'x-form-item-display-none'
37948
37949
37950 });
37951
37952
37953 /*
37954  * Based on:
37955  * Ext JS Library 1.1.1
37956  * Copyright(c) 2006-2007, Ext JS, LLC.
37957  *
37958  * Originally Released Under LGPL - original licence link has changed is not relivant.
37959  *
37960  * Fork - LGPL
37961  * <script type="text/javascript">
37962  */
37963  
37964 /**
37965  * @class Roo.form.TriggerField
37966  * @extends Roo.form.TextField
37967  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37968  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37969  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37970  * for which you can provide a custom implementation.  For example:
37971  * <pre><code>
37972 var trigger = new Roo.form.TriggerField();
37973 trigger.onTriggerClick = myTriggerFn;
37974 trigger.applyTo('my-field');
37975 </code></pre>
37976  *
37977  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37978  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37979  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37980  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37981  * @constructor
37982  * Create a new TriggerField.
37983  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37984  * to the base TextField)
37985  */
37986 Roo.form.TriggerField = function(config){
37987     this.mimicing = false;
37988     Roo.form.TriggerField.superclass.constructor.call(this, config);
37989 };
37990
37991 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37992     /**
37993      * @cfg {String} triggerClass A CSS class to apply to the trigger
37994      */
37995     /**
37996      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37997      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37998      */
37999     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38000     /**
38001      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38002      */
38003     hideTrigger:false,
38004
38005     /** @cfg {Boolean} grow @hide */
38006     /** @cfg {Number} growMin @hide */
38007     /** @cfg {Number} growMax @hide */
38008
38009     /**
38010      * @hide 
38011      * @method
38012      */
38013     autoSize: Roo.emptyFn,
38014     // private
38015     monitorTab : true,
38016     // private
38017     deferHeight : true,
38018
38019     
38020     actionMode : 'wrap',
38021     // private
38022     onResize : function(w, h){
38023         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38024         if(typeof w == 'number'){
38025             var x = w - this.trigger.getWidth();
38026             this.el.setWidth(this.adjustWidth('input', x));
38027             this.trigger.setStyle('left', x+'px');
38028         }
38029     },
38030
38031     // private
38032     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38033
38034     // private
38035     getResizeEl : function(){
38036         return this.wrap;
38037     },
38038
38039     // private
38040     getPositionEl : function(){
38041         return this.wrap;
38042     },
38043
38044     // private
38045     alignErrorIcon : function(){
38046         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38047     },
38048
38049     // private
38050     onRender : function(ct, position){
38051         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38052         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38053         this.trigger = this.wrap.createChild(this.triggerConfig ||
38054                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38055         if(this.hideTrigger){
38056             this.trigger.setDisplayed(false);
38057         }
38058         this.initTrigger();
38059         if(!this.width){
38060             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38061         }
38062     },
38063
38064     // private
38065     initTrigger : function(){
38066         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38067         this.trigger.addClassOnOver('x-form-trigger-over');
38068         this.trigger.addClassOnClick('x-form-trigger-click');
38069     },
38070
38071     // private
38072     onDestroy : function(){
38073         if(this.trigger){
38074             this.trigger.removeAllListeners();
38075             this.trigger.remove();
38076         }
38077         if(this.wrap){
38078             this.wrap.remove();
38079         }
38080         Roo.form.TriggerField.superclass.onDestroy.call(this);
38081     },
38082
38083     // private
38084     onFocus : function(){
38085         Roo.form.TriggerField.superclass.onFocus.call(this);
38086         if(!this.mimicing){
38087             this.wrap.addClass('x-trigger-wrap-focus');
38088             this.mimicing = true;
38089             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38090             if(this.monitorTab){
38091                 this.el.on("keydown", this.checkTab, this);
38092             }
38093         }
38094     },
38095
38096     // private
38097     checkTab : function(e){
38098         if(e.getKey() == e.TAB){
38099             this.triggerBlur();
38100         }
38101     },
38102
38103     // private
38104     onBlur : function(){
38105         // do nothing
38106     },
38107
38108     // private
38109     mimicBlur : function(e, t){
38110         if(!this.wrap.contains(t) && this.validateBlur()){
38111             this.triggerBlur();
38112         }
38113     },
38114
38115     // private
38116     triggerBlur : function(){
38117         this.mimicing = false;
38118         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38119         if(this.monitorTab){
38120             this.el.un("keydown", this.checkTab, this);
38121         }
38122         this.wrap.removeClass('x-trigger-wrap-focus');
38123         Roo.form.TriggerField.superclass.onBlur.call(this);
38124     },
38125
38126     // private
38127     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38128     validateBlur : function(e, t){
38129         return true;
38130     },
38131
38132     // private
38133     onDisable : function(){
38134         Roo.form.TriggerField.superclass.onDisable.call(this);
38135         if(this.wrap){
38136             this.wrap.addClass('x-item-disabled');
38137         }
38138     },
38139
38140     // private
38141     onEnable : function(){
38142         Roo.form.TriggerField.superclass.onEnable.call(this);
38143         if(this.wrap){
38144             this.wrap.removeClass('x-item-disabled');
38145         }
38146     },
38147
38148     // private
38149     onShow : function(){
38150         var ae = this.getActionEl();
38151         
38152         if(ae){
38153             ae.dom.style.display = '';
38154             ae.dom.style.visibility = 'visible';
38155         }
38156     },
38157
38158     // private
38159     
38160     onHide : function(){
38161         var ae = this.getActionEl();
38162         ae.dom.style.display = 'none';
38163     },
38164
38165     /**
38166      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38167      * by an implementing function.
38168      * @method
38169      * @param {EventObject} e
38170      */
38171     onTriggerClick : Roo.emptyFn
38172 });
38173
38174 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38175 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38176 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38177 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38178     initComponent : function(){
38179         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38180
38181         this.triggerConfig = {
38182             tag:'span', cls:'x-form-twin-triggers', cn:[
38183             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38184             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38185         ]};
38186     },
38187
38188     getTrigger : function(index){
38189         return this.triggers[index];
38190     },
38191
38192     initTrigger : function(){
38193         var ts = this.trigger.select('.x-form-trigger', true);
38194         this.wrap.setStyle('overflow', 'hidden');
38195         var triggerField = this;
38196         ts.each(function(t, all, index){
38197             t.hide = function(){
38198                 var w = triggerField.wrap.getWidth();
38199                 this.dom.style.display = 'none';
38200                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38201             };
38202             t.show = function(){
38203                 var w = triggerField.wrap.getWidth();
38204                 this.dom.style.display = '';
38205                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38206             };
38207             var triggerIndex = 'Trigger'+(index+1);
38208
38209             if(this['hide'+triggerIndex]){
38210                 t.dom.style.display = 'none';
38211             }
38212             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38213             t.addClassOnOver('x-form-trigger-over');
38214             t.addClassOnClick('x-form-trigger-click');
38215         }, this);
38216         this.triggers = ts.elements;
38217     },
38218
38219     onTrigger1Click : Roo.emptyFn,
38220     onTrigger2Click : Roo.emptyFn
38221 });/*
38222  * Based on:
38223  * Ext JS Library 1.1.1
38224  * Copyright(c) 2006-2007, Ext JS, LLC.
38225  *
38226  * Originally Released Under LGPL - original licence link has changed is not relivant.
38227  *
38228  * Fork - LGPL
38229  * <script type="text/javascript">
38230  */
38231  
38232 /**
38233  * @class Roo.form.TextArea
38234  * @extends Roo.form.TextField
38235  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38236  * support for auto-sizing.
38237  * @constructor
38238  * Creates a new TextArea
38239  * @param {Object} config Configuration options
38240  */
38241 Roo.form.TextArea = function(config){
38242     Roo.form.TextArea.superclass.constructor.call(this, config);
38243     // these are provided exchanges for backwards compat
38244     // minHeight/maxHeight were replaced by growMin/growMax to be
38245     // compatible with TextField growing config values
38246     if(this.minHeight !== undefined){
38247         this.growMin = this.minHeight;
38248     }
38249     if(this.maxHeight !== undefined){
38250         this.growMax = this.maxHeight;
38251     }
38252 };
38253
38254 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38255     /**
38256      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38257      */
38258     growMin : 60,
38259     /**
38260      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38261      */
38262     growMax: 1000,
38263     /**
38264      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38265      * in the field (equivalent to setting overflow: hidden, defaults to false)
38266      */
38267     preventScrollbars: false,
38268     /**
38269      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38270      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38271      */
38272
38273     // private
38274     onRender : function(ct, position){
38275         if(!this.el){
38276             this.defaultAutoCreate = {
38277                 tag: "textarea",
38278                 style:"width:300px;height:60px;",
38279                 autocomplete: "new-password"
38280             };
38281         }
38282         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38283         if(this.grow){
38284             this.textSizeEl = Roo.DomHelper.append(document.body, {
38285                 tag: "pre", cls: "x-form-grow-sizer"
38286             });
38287             if(this.preventScrollbars){
38288                 this.el.setStyle("overflow", "hidden");
38289             }
38290             this.el.setHeight(this.growMin);
38291         }
38292     },
38293
38294     onDestroy : function(){
38295         if(this.textSizeEl){
38296             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38297         }
38298         Roo.form.TextArea.superclass.onDestroy.call(this);
38299     },
38300
38301     // private
38302     onKeyUp : function(e){
38303         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38304             this.autoSize();
38305         }
38306     },
38307
38308     /**
38309      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38310      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38311      */
38312     autoSize : function(){
38313         if(!this.grow || !this.textSizeEl){
38314             return;
38315         }
38316         var el = this.el;
38317         var v = el.dom.value;
38318         var ts = this.textSizeEl;
38319
38320         ts.innerHTML = '';
38321         ts.appendChild(document.createTextNode(v));
38322         v = ts.innerHTML;
38323
38324         Roo.fly(ts).setWidth(this.el.getWidth());
38325         if(v.length < 1){
38326             v = "&#160;&#160;";
38327         }else{
38328             if(Roo.isIE){
38329                 v = v.replace(/\n/g, '<p>&#160;</p>');
38330             }
38331             v += "&#160;\n&#160;";
38332         }
38333         ts.innerHTML = v;
38334         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38335         if(h != this.lastHeight){
38336             this.lastHeight = h;
38337             this.el.setHeight(h);
38338             this.fireEvent("autosize", this, h);
38339         }
38340     }
38341 });/*
38342  * Based on:
38343  * Ext JS Library 1.1.1
38344  * Copyright(c) 2006-2007, Ext JS, LLC.
38345  *
38346  * Originally Released Under LGPL - original licence link has changed is not relivant.
38347  *
38348  * Fork - LGPL
38349  * <script type="text/javascript">
38350  */
38351  
38352
38353 /**
38354  * @class Roo.form.NumberField
38355  * @extends Roo.form.TextField
38356  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38357  * @constructor
38358  * Creates a new NumberField
38359  * @param {Object} config Configuration options
38360  */
38361 Roo.form.NumberField = function(config){
38362     Roo.form.NumberField.superclass.constructor.call(this, config);
38363 };
38364
38365 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38366     /**
38367      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38368      */
38369     fieldClass: "x-form-field x-form-num-field",
38370     /**
38371      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38372      */
38373     allowDecimals : true,
38374     /**
38375      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38376      */
38377     decimalSeparator : ".",
38378     /**
38379      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38380      */
38381     decimalPrecision : 2,
38382     /**
38383      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38384      */
38385     allowNegative : true,
38386     /**
38387      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38388      */
38389     minValue : Number.NEGATIVE_INFINITY,
38390     /**
38391      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38392      */
38393     maxValue : Number.MAX_VALUE,
38394     /**
38395      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38396      */
38397     minText : "The minimum value for this field is {0}",
38398     /**
38399      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38400      */
38401     maxText : "The maximum value for this field is {0}",
38402     /**
38403      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38404      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38405      */
38406     nanText : "{0} is not a valid number",
38407
38408     // private
38409     initEvents : function(){
38410         Roo.form.NumberField.superclass.initEvents.call(this);
38411         var allowed = "0123456789";
38412         if(this.allowDecimals){
38413             allowed += this.decimalSeparator;
38414         }
38415         if(this.allowNegative){
38416             allowed += "-";
38417         }
38418         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38419         var keyPress = function(e){
38420             var k = e.getKey();
38421             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38422                 return;
38423             }
38424             var c = e.getCharCode();
38425             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38426                 e.stopEvent();
38427             }
38428         };
38429         this.el.on("keypress", keyPress, this);
38430     },
38431
38432     // private
38433     validateValue : function(value){
38434         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38435             return false;
38436         }
38437         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38438              return true;
38439         }
38440         var num = this.parseValue(value);
38441         if(isNaN(num)){
38442             this.markInvalid(String.format(this.nanText, value));
38443             return false;
38444         }
38445         if(num < this.minValue){
38446             this.markInvalid(String.format(this.minText, this.minValue));
38447             return false;
38448         }
38449         if(num > this.maxValue){
38450             this.markInvalid(String.format(this.maxText, this.maxValue));
38451             return false;
38452         }
38453         return true;
38454     },
38455
38456     getValue : function(){
38457         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38458     },
38459
38460     // private
38461     parseValue : function(value){
38462         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38463         return isNaN(value) ? '' : value;
38464     },
38465
38466     // private
38467     fixPrecision : function(value){
38468         var nan = isNaN(value);
38469         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38470             return nan ? '' : value;
38471         }
38472         return parseFloat(value).toFixed(this.decimalPrecision);
38473     },
38474
38475     setValue : function(v){
38476         v = this.fixPrecision(v);
38477         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38478     },
38479
38480     // private
38481     decimalPrecisionFcn : function(v){
38482         return Math.floor(v);
38483     },
38484
38485     beforeBlur : function(){
38486         var v = this.parseValue(this.getRawValue());
38487         if(v){
38488             this.setValue(v);
38489         }
38490     }
38491 });/*
38492  * Based on:
38493  * Ext JS Library 1.1.1
38494  * Copyright(c) 2006-2007, Ext JS, LLC.
38495  *
38496  * Originally Released Under LGPL - original licence link has changed is not relivant.
38497  *
38498  * Fork - LGPL
38499  * <script type="text/javascript">
38500  */
38501  
38502 /**
38503  * @class Roo.form.DateField
38504  * @extends Roo.form.TriggerField
38505  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38506 * @constructor
38507 * Create a new DateField
38508 * @param {Object} config
38509  */
38510 Roo.form.DateField = function(config){
38511     Roo.form.DateField.superclass.constructor.call(this, config);
38512     
38513       this.addEvents({
38514          
38515         /**
38516          * @event select
38517          * Fires when a date is selected
38518              * @param {Roo.form.DateField} combo This combo box
38519              * @param {Date} date The date selected
38520              */
38521         'select' : true
38522          
38523     });
38524     
38525     
38526     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38527     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38528     this.ddMatch = null;
38529     if(this.disabledDates){
38530         var dd = this.disabledDates;
38531         var re = "(?:";
38532         for(var i = 0; i < dd.length; i++){
38533             re += dd[i];
38534             if(i != dd.length-1) re += "|";
38535         }
38536         this.ddMatch = new RegExp(re + ")");
38537     }
38538 };
38539
38540 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38541     /**
38542      * @cfg {String} format
38543      * The default date format string which can be overriden for localization support.  The format must be
38544      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38545      */
38546     format : "m/d/y",
38547     /**
38548      * @cfg {String} altFormats
38549      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38550      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38551      */
38552     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38553     /**
38554      * @cfg {Array} disabledDays
38555      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38556      */
38557     disabledDays : null,
38558     /**
38559      * @cfg {String} disabledDaysText
38560      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38561      */
38562     disabledDaysText : "Disabled",
38563     /**
38564      * @cfg {Array} disabledDates
38565      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38566      * expression so they are very powerful. Some examples:
38567      * <ul>
38568      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38569      * <li>["03/08", "09/16"] would disable those days for every year</li>
38570      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38571      * <li>["03/../2006"] would disable every day in March 2006</li>
38572      * <li>["^03"] would disable every day in every March</li>
38573      * </ul>
38574      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38575      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38576      */
38577     disabledDates : null,
38578     /**
38579      * @cfg {String} disabledDatesText
38580      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38581      */
38582     disabledDatesText : "Disabled",
38583     /**
38584      * @cfg {Date/String} minValue
38585      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38586      * valid format (defaults to null).
38587      */
38588     minValue : null,
38589     /**
38590      * @cfg {Date/String} maxValue
38591      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38592      * valid format (defaults to null).
38593      */
38594     maxValue : null,
38595     /**
38596      * @cfg {String} minText
38597      * The error text to display when the date in the cell is before minValue (defaults to
38598      * 'The date in this field must be after {minValue}').
38599      */
38600     minText : "The date in this field must be equal to or after {0}",
38601     /**
38602      * @cfg {String} maxText
38603      * The error text to display when the date in the cell is after maxValue (defaults to
38604      * 'The date in this field must be before {maxValue}').
38605      */
38606     maxText : "The date in this field must be equal to or before {0}",
38607     /**
38608      * @cfg {String} invalidText
38609      * The error text to display when the date in the field is invalid (defaults to
38610      * '{value} is not a valid date - it must be in the format {format}').
38611      */
38612     invalidText : "{0} is not a valid date - it must be in the format {1}",
38613     /**
38614      * @cfg {String} triggerClass
38615      * An additional CSS class used to style the trigger button.  The trigger will always get the
38616      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38617      * which displays a calendar icon).
38618      */
38619     triggerClass : 'x-form-date-trigger',
38620     
38621
38622     /**
38623      * @cfg {Boolean} useIso
38624      * if enabled, then the date field will use a hidden field to store the 
38625      * real value as iso formated date. default (false)
38626      */ 
38627     useIso : false,
38628     /**
38629      * @cfg {String/Object} autoCreate
38630      * A DomHelper element spec, or true for a default element spec (defaults to
38631      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38632      */ 
38633     // private
38634     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38635     
38636     // private
38637     hiddenField: false,
38638     
38639     onRender : function(ct, position)
38640     {
38641         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38642         if (this.useIso) {
38643             //this.el.dom.removeAttribute('name'); 
38644             Roo.log("Changing name?");
38645             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38646             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38647                     'before', true);
38648             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38649             // prevent input submission
38650             this.hiddenName = this.name;
38651         }
38652             
38653             
38654     },
38655     
38656     // private
38657     validateValue : function(value)
38658     {
38659         value = this.formatDate(value);
38660         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38661             Roo.log('super failed');
38662             return false;
38663         }
38664         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38665              return true;
38666         }
38667         var svalue = value;
38668         value = this.parseDate(value);
38669         if(!value){
38670             Roo.log('parse date failed' + svalue);
38671             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38672             return false;
38673         }
38674         var time = value.getTime();
38675         if(this.minValue && time < this.minValue.getTime()){
38676             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38677             return false;
38678         }
38679         if(this.maxValue && time > this.maxValue.getTime()){
38680             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38681             return false;
38682         }
38683         if(this.disabledDays){
38684             var day = value.getDay();
38685             for(var i = 0; i < this.disabledDays.length; i++) {
38686                 if(day === this.disabledDays[i]){
38687                     this.markInvalid(this.disabledDaysText);
38688                     return false;
38689                 }
38690             }
38691         }
38692         var fvalue = this.formatDate(value);
38693         if(this.ddMatch && this.ddMatch.test(fvalue)){
38694             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38695             return false;
38696         }
38697         return true;
38698     },
38699
38700     // private
38701     // Provides logic to override the default TriggerField.validateBlur which just returns true
38702     validateBlur : function(){
38703         return !this.menu || !this.menu.isVisible();
38704     },
38705     
38706     getName: function()
38707     {
38708         // returns hidden if it's set..
38709         if (!this.rendered) {return ''};
38710         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38711         
38712     },
38713
38714     /**
38715      * Returns the current date value of the date field.
38716      * @return {Date} The date value
38717      */
38718     getValue : function(){
38719         
38720         return  this.hiddenField ?
38721                 this.hiddenField.value :
38722                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38723     },
38724
38725     /**
38726      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38727      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38728      * (the default format used is "m/d/y").
38729      * <br />Usage:
38730      * <pre><code>
38731 //All of these calls set the same date value (May 4, 2006)
38732
38733 //Pass a date object:
38734 var dt = new Date('5/4/06');
38735 dateField.setValue(dt);
38736
38737 //Pass a date string (default format):
38738 dateField.setValue('5/4/06');
38739
38740 //Pass a date string (custom format):
38741 dateField.format = 'Y-m-d';
38742 dateField.setValue('2006-5-4');
38743 </code></pre>
38744      * @param {String/Date} date The date or valid date string
38745      */
38746     setValue : function(date){
38747         if (this.hiddenField) {
38748             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38749         }
38750         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38751         // make sure the value field is always stored as a date..
38752         this.value = this.parseDate(date);
38753         
38754         
38755     },
38756
38757     // private
38758     parseDate : function(value){
38759         if(!value || value instanceof Date){
38760             return value;
38761         }
38762         var v = Date.parseDate(value, this.format);
38763          if (!v && this.useIso) {
38764             v = Date.parseDate(value, 'Y-m-d');
38765         }
38766         if(!v && this.altFormats){
38767             if(!this.altFormatsArray){
38768                 this.altFormatsArray = this.altFormats.split("|");
38769             }
38770             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38771                 v = Date.parseDate(value, this.altFormatsArray[i]);
38772             }
38773         }
38774         return v;
38775     },
38776
38777     // private
38778     formatDate : function(date, fmt){
38779         return (!date || !(date instanceof Date)) ?
38780                date : date.dateFormat(fmt || this.format);
38781     },
38782
38783     // private
38784     menuListeners : {
38785         select: function(m, d){
38786             
38787             this.setValue(d);
38788             this.fireEvent('select', this, d);
38789         },
38790         show : function(){ // retain focus styling
38791             this.onFocus();
38792         },
38793         hide : function(){
38794             this.focus.defer(10, this);
38795             var ml = this.menuListeners;
38796             this.menu.un("select", ml.select,  this);
38797             this.menu.un("show", ml.show,  this);
38798             this.menu.un("hide", ml.hide,  this);
38799         }
38800     },
38801
38802     // private
38803     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38804     onTriggerClick : function(){
38805         if(this.disabled){
38806             return;
38807         }
38808         if(this.menu == null){
38809             this.menu = new Roo.menu.DateMenu();
38810         }
38811         Roo.apply(this.menu.picker,  {
38812             showClear: this.allowBlank,
38813             minDate : this.minValue,
38814             maxDate : this.maxValue,
38815             disabledDatesRE : this.ddMatch,
38816             disabledDatesText : this.disabledDatesText,
38817             disabledDays : this.disabledDays,
38818             disabledDaysText : this.disabledDaysText,
38819             format : this.useIso ? 'Y-m-d' : this.format,
38820             minText : String.format(this.minText, this.formatDate(this.minValue)),
38821             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38822         });
38823         this.menu.on(Roo.apply({}, this.menuListeners, {
38824             scope:this
38825         }));
38826         this.menu.picker.setValue(this.getValue() || new Date());
38827         this.menu.show(this.el, "tl-bl?");
38828     },
38829
38830     beforeBlur : function(){
38831         var v = this.parseDate(this.getRawValue());
38832         if(v){
38833             this.setValue(v);
38834         }
38835     },
38836
38837     /*@
38838      * overide
38839      * 
38840      */
38841     isDirty : function() {
38842         if(this.disabled) {
38843             return false;
38844         }
38845         
38846         if(typeof(this.startValue) === 'undefined'){
38847             return false;
38848         }
38849         
38850         return String(this.getValue()) !== String(this.startValue);
38851         
38852     }
38853 });/*
38854  * Based on:
38855  * Ext JS Library 1.1.1
38856  * Copyright(c) 2006-2007, Ext JS, LLC.
38857  *
38858  * Originally Released Under LGPL - original licence link has changed is not relivant.
38859  *
38860  * Fork - LGPL
38861  * <script type="text/javascript">
38862  */
38863  
38864 /**
38865  * @class Roo.form.MonthField
38866  * @extends Roo.form.TriggerField
38867  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38868 * @constructor
38869 * Create a new MonthField
38870 * @param {Object} config
38871  */
38872 Roo.form.MonthField = function(config){
38873     
38874     Roo.form.MonthField.superclass.constructor.call(this, config);
38875     
38876       this.addEvents({
38877          
38878         /**
38879          * @event select
38880          * Fires when a date is selected
38881              * @param {Roo.form.MonthFieeld} combo This combo box
38882              * @param {Date} date The date selected
38883              */
38884         'select' : true
38885          
38886     });
38887     
38888     
38889     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38890     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38891     this.ddMatch = null;
38892     if(this.disabledDates){
38893         var dd = this.disabledDates;
38894         var re = "(?:";
38895         for(var i = 0; i < dd.length; i++){
38896             re += dd[i];
38897             if(i != dd.length-1) re += "|";
38898         }
38899         this.ddMatch = new RegExp(re + ")");
38900     }
38901 };
38902
38903 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38904     /**
38905      * @cfg {String} format
38906      * The default date format string which can be overriden for localization support.  The format must be
38907      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38908      */
38909     format : "M Y",
38910     /**
38911      * @cfg {String} altFormats
38912      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38913      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38914      */
38915     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38916     /**
38917      * @cfg {Array} disabledDays
38918      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38919      */
38920     disabledDays : [0,1,2,3,4,5,6],
38921     /**
38922      * @cfg {String} disabledDaysText
38923      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38924      */
38925     disabledDaysText : "Disabled",
38926     /**
38927      * @cfg {Array} disabledDates
38928      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38929      * expression so they are very powerful. Some examples:
38930      * <ul>
38931      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38932      * <li>["03/08", "09/16"] would disable those days for every year</li>
38933      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38934      * <li>["03/../2006"] would disable every day in March 2006</li>
38935      * <li>["^03"] would disable every day in every March</li>
38936      * </ul>
38937      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38938      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38939      */
38940     disabledDates : null,
38941     /**
38942      * @cfg {String} disabledDatesText
38943      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38944      */
38945     disabledDatesText : "Disabled",
38946     /**
38947      * @cfg {Date/String} minValue
38948      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38949      * valid format (defaults to null).
38950      */
38951     minValue : null,
38952     /**
38953      * @cfg {Date/String} maxValue
38954      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38955      * valid format (defaults to null).
38956      */
38957     maxValue : null,
38958     /**
38959      * @cfg {String} minText
38960      * The error text to display when the date in the cell is before minValue (defaults to
38961      * 'The date in this field must be after {minValue}').
38962      */
38963     minText : "The date in this field must be equal to or after {0}",
38964     /**
38965      * @cfg {String} maxTextf
38966      * The error text to display when the date in the cell is after maxValue (defaults to
38967      * 'The date in this field must be before {maxValue}').
38968      */
38969     maxText : "The date in this field must be equal to or before {0}",
38970     /**
38971      * @cfg {String} invalidText
38972      * The error text to display when the date in the field is invalid (defaults to
38973      * '{value} is not a valid date - it must be in the format {format}').
38974      */
38975     invalidText : "{0} is not a valid date - it must be in the format {1}",
38976     /**
38977      * @cfg {String} triggerClass
38978      * An additional CSS class used to style the trigger button.  The trigger will always get the
38979      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38980      * which displays a calendar icon).
38981      */
38982     triggerClass : 'x-form-date-trigger',
38983     
38984
38985     /**
38986      * @cfg {Boolean} useIso
38987      * if enabled, then the date field will use a hidden field to store the 
38988      * real value as iso formated date. default (true)
38989      */ 
38990     useIso : true,
38991     /**
38992      * @cfg {String/Object} autoCreate
38993      * A DomHelper element spec, or true for a default element spec (defaults to
38994      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38995      */ 
38996     // private
38997     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
38998     
38999     // private
39000     hiddenField: false,
39001     
39002     hideMonthPicker : false,
39003     
39004     onRender : function(ct, position)
39005     {
39006         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39007         if (this.useIso) {
39008             this.el.dom.removeAttribute('name'); 
39009             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39010                     'before', true);
39011             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39012             // prevent input submission
39013             this.hiddenName = this.name;
39014         }
39015             
39016             
39017     },
39018     
39019     // private
39020     validateValue : function(value)
39021     {
39022         value = this.formatDate(value);
39023         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39024             return false;
39025         }
39026         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39027              return true;
39028         }
39029         var svalue = value;
39030         value = this.parseDate(value);
39031         if(!value){
39032             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39033             return false;
39034         }
39035         var time = value.getTime();
39036         if(this.minValue && time < this.minValue.getTime()){
39037             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39038             return false;
39039         }
39040         if(this.maxValue && time > this.maxValue.getTime()){
39041             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39042             return false;
39043         }
39044         /*if(this.disabledDays){
39045             var day = value.getDay();
39046             for(var i = 0; i < this.disabledDays.length; i++) {
39047                 if(day === this.disabledDays[i]){
39048                     this.markInvalid(this.disabledDaysText);
39049                     return false;
39050                 }
39051             }
39052         }
39053         */
39054         var fvalue = this.formatDate(value);
39055         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39056             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39057             return false;
39058         }
39059         */
39060         return true;
39061     },
39062
39063     // private
39064     // Provides logic to override the default TriggerField.validateBlur which just returns true
39065     validateBlur : function(){
39066         return !this.menu || !this.menu.isVisible();
39067     },
39068
39069     /**
39070      * Returns the current date value of the date field.
39071      * @return {Date} The date value
39072      */
39073     getValue : function(){
39074         
39075         
39076         
39077         return  this.hiddenField ?
39078                 this.hiddenField.value :
39079                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39080     },
39081
39082     /**
39083      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39084      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39085      * (the default format used is "m/d/y").
39086      * <br />Usage:
39087      * <pre><code>
39088 //All of these calls set the same date value (May 4, 2006)
39089
39090 //Pass a date object:
39091 var dt = new Date('5/4/06');
39092 monthField.setValue(dt);
39093
39094 //Pass a date string (default format):
39095 monthField.setValue('5/4/06');
39096
39097 //Pass a date string (custom format):
39098 monthField.format = 'Y-m-d';
39099 monthField.setValue('2006-5-4');
39100 </code></pre>
39101      * @param {String/Date} date The date or valid date string
39102      */
39103     setValue : function(date){
39104         Roo.log('month setValue' + date);
39105         // can only be first of month..
39106         
39107         var val = this.parseDate(date);
39108         
39109         if (this.hiddenField) {
39110             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39111         }
39112         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39113         this.value = this.parseDate(date);
39114     },
39115
39116     // private
39117     parseDate : function(value){
39118         if(!value || value instanceof Date){
39119             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39120             return value;
39121         }
39122         var v = Date.parseDate(value, this.format);
39123         if (!v && this.useIso) {
39124             v = Date.parseDate(value, 'Y-m-d');
39125         }
39126         if (v) {
39127             // 
39128             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39129         }
39130         
39131         
39132         if(!v && this.altFormats){
39133             if(!this.altFormatsArray){
39134                 this.altFormatsArray = this.altFormats.split("|");
39135             }
39136             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39137                 v = Date.parseDate(value, this.altFormatsArray[i]);
39138             }
39139         }
39140         return v;
39141     },
39142
39143     // private
39144     formatDate : function(date, fmt){
39145         return (!date || !(date instanceof Date)) ?
39146                date : date.dateFormat(fmt || this.format);
39147     },
39148
39149     // private
39150     menuListeners : {
39151         select: function(m, d){
39152             this.setValue(d);
39153             this.fireEvent('select', this, d);
39154         },
39155         show : function(){ // retain focus styling
39156             this.onFocus();
39157         },
39158         hide : function(){
39159             this.focus.defer(10, this);
39160             var ml = this.menuListeners;
39161             this.menu.un("select", ml.select,  this);
39162             this.menu.un("show", ml.show,  this);
39163             this.menu.un("hide", ml.hide,  this);
39164         }
39165     },
39166     // private
39167     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39168     onTriggerClick : function(){
39169         if(this.disabled){
39170             return;
39171         }
39172         if(this.menu == null){
39173             this.menu = new Roo.menu.DateMenu();
39174            
39175         }
39176         
39177         Roo.apply(this.menu.picker,  {
39178             
39179             showClear: this.allowBlank,
39180             minDate : this.minValue,
39181             maxDate : this.maxValue,
39182             disabledDatesRE : this.ddMatch,
39183             disabledDatesText : this.disabledDatesText,
39184             
39185             format : this.useIso ? 'Y-m-d' : this.format,
39186             minText : String.format(this.minText, this.formatDate(this.minValue)),
39187             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39188             
39189         });
39190          this.menu.on(Roo.apply({}, this.menuListeners, {
39191             scope:this
39192         }));
39193        
39194         
39195         var m = this.menu;
39196         var p = m.picker;
39197         
39198         // hide month picker get's called when we called by 'before hide';
39199         
39200         var ignorehide = true;
39201         p.hideMonthPicker  = function(disableAnim){
39202             if (ignorehide) {
39203                 return;
39204             }
39205              if(this.monthPicker){
39206                 Roo.log("hideMonthPicker called");
39207                 if(disableAnim === true){
39208                     this.monthPicker.hide();
39209                 }else{
39210                     this.monthPicker.slideOut('t', {duration:.2});
39211                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39212                     p.fireEvent("select", this, this.value);
39213                     m.hide();
39214                 }
39215             }
39216         }
39217         
39218         Roo.log('picker set value');
39219         Roo.log(this.getValue());
39220         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39221         m.show(this.el, 'tl-bl?');
39222         ignorehide  = false;
39223         // this will trigger hideMonthPicker..
39224         
39225         
39226         // hidden the day picker
39227         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39228         
39229         
39230         
39231       
39232         
39233         p.showMonthPicker.defer(100, p);
39234     
39235         
39236        
39237     },
39238
39239     beforeBlur : function(){
39240         var v = this.parseDate(this.getRawValue());
39241         if(v){
39242             this.setValue(v);
39243         }
39244     }
39245
39246     /** @cfg {Boolean} grow @hide */
39247     /** @cfg {Number} growMin @hide */
39248     /** @cfg {Number} growMax @hide */
39249     /**
39250      * @hide
39251      * @method autoSize
39252      */
39253 });/*
39254  * Based on:
39255  * Ext JS Library 1.1.1
39256  * Copyright(c) 2006-2007, Ext JS, LLC.
39257  *
39258  * Originally Released Under LGPL - original licence link has changed is not relivant.
39259  *
39260  * Fork - LGPL
39261  * <script type="text/javascript">
39262  */
39263  
39264
39265 /**
39266  * @class Roo.form.ComboBox
39267  * @extends Roo.form.TriggerField
39268  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39269  * @constructor
39270  * Create a new ComboBox.
39271  * @param {Object} config Configuration options
39272  */
39273 Roo.form.ComboBox = function(config){
39274     Roo.form.ComboBox.superclass.constructor.call(this, config);
39275     this.addEvents({
39276         /**
39277          * @event expand
39278          * Fires when the dropdown list is expanded
39279              * @param {Roo.form.ComboBox} combo This combo box
39280              */
39281         'expand' : true,
39282         /**
39283          * @event collapse
39284          * Fires when the dropdown list is collapsed
39285              * @param {Roo.form.ComboBox} combo This combo box
39286              */
39287         'collapse' : true,
39288         /**
39289          * @event beforeselect
39290          * Fires before a list item is selected. Return false to cancel the selection.
39291              * @param {Roo.form.ComboBox} combo This combo box
39292              * @param {Roo.data.Record} record The data record returned from the underlying store
39293              * @param {Number} index The index of the selected item in the dropdown list
39294              */
39295         'beforeselect' : true,
39296         /**
39297          * @event select
39298          * Fires when a list item is selected
39299              * @param {Roo.form.ComboBox} combo This combo box
39300              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39301              * @param {Number} index The index of the selected item in the dropdown list
39302              */
39303         'select' : true,
39304         /**
39305          * @event beforequery
39306          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39307          * The event object passed has these properties:
39308              * @param {Roo.form.ComboBox} combo This combo box
39309              * @param {String} query The query
39310              * @param {Boolean} forceAll true to force "all" query
39311              * @param {Boolean} cancel true to cancel the query
39312              * @param {Object} e The query event object
39313              */
39314         'beforequery': true,
39315          /**
39316          * @event add
39317          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39318              * @param {Roo.form.ComboBox} combo This combo box
39319              */
39320         'add' : true,
39321         /**
39322          * @event edit
39323          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39324              * @param {Roo.form.ComboBox} combo This combo box
39325              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39326              */
39327         'edit' : true
39328         
39329         
39330     });
39331     if(this.transform){
39332         this.allowDomMove = false;
39333         var s = Roo.getDom(this.transform);
39334         if(!this.hiddenName){
39335             this.hiddenName = s.name;
39336         }
39337         if(!this.store){
39338             this.mode = 'local';
39339             var d = [], opts = s.options;
39340             for(var i = 0, len = opts.length;i < len; i++){
39341                 var o = opts[i];
39342                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39343                 if(o.selected) {
39344                     this.value = value;
39345                 }
39346                 d.push([value, o.text]);
39347             }
39348             this.store = new Roo.data.SimpleStore({
39349                 'id': 0,
39350                 fields: ['value', 'text'],
39351                 data : d
39352             });
39353             this.valueField = 'value';
39354             this.displayField = 'text';
39355         }
39356         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39357         if(!this.lazyRender){
39358             this.target = true;
39359             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39360             s.parentNode.removeChild(s); // remove it
39361             this.render(this.el.parentNode);
39362         }else{
39363             s.parentNode.removeChild(s); // remove it
39364         }
39365
39366     }
39367     if (this.store) {
39368         this.store = Roo.factory(this.store, Roo.data);
39369     }
39370     
39371     this.selectedIndex = -1;
39372     if(this.mode == 'local'){
39373         if(config.queryDelay === undefined){
39374             this.queryDelay = 10;
39375         }
39376         if(config.minChars === undefined){
39377             this.minChars = 0;
39378         }
39379     }
39380 };
39381
39382 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39383     /**
39384      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39385      */
39386     /**
39387      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39388      * rendering into an Roo.Editor, defaults to false)
39389      */
39390     /**
39391      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39392      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39393      */
39394     /**
39395      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39396      */
39397     /**
39398      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39399      * the dropdown list (defaults to undefined, with no header element)
39400      */
39401
39402      /**
39403      * @cfg {String/Roo.Template} tpl The template to use to render the output
39404      */
39405      
39406     // private
39407     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39408     /**
39409      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39410      */
39411     listWidth: undefined,
39412     /**
39413      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39414      * mode = 'remote' or 'text' if mode = 'local')
39415      */
39416     displayField: undefined,
39417     /**
39418      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39419      * mode = 'remote' or 'value' if mode = 'local'). 
39420      * Note: use of a valueField requires the user make a selection
39421      * in order for a value to be mapped.
39422      */
39423     valueField: undefined,
39424     
39425     
39426     /**
39427      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39428      * field's data value (defaults to the underlying DOM element's name)
39429      */
39430     hiddenName: undefined,
39431     /**
39432      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39433      */
39434     listClass: '',
39435     /**
39436      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39437      */
39438     selectedClass: 'x-combo-selected',
39439     /**
39440      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39441      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39442      * which displays a downward arrow icon).
39443      */
39444     triggerClass : 'x-form-arrow-trigger',
39445     /**
39446      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39447      */
39448     shadow:'sides',
39449     /**
39450      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39451      * anchor positions (defaults to 'tl-bl')
39452      */
39453     listAlign: 'tl-bl?',
39454     /**
39455      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39456      */
39457     maxHeight: 300,
39458     /**
39459      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39460      * query specified by the allQuery config option (defaults to 'query')
39461      */
39462     triggerAction: 'query',
39463     /**
39464      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39465      * (defaults to 4, does not apply if editable = false)
39466      */
39467     minChars : 4,
39468     /**
39469      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39470      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39471      */
39472     typeAhead: false,
39473     /**
39474      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39475      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39476      */
39477     queryDelay: 500,
39478     /**
39479      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39480      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39481      */
39482     pageSize: 0,
39483     /**
39484      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39485      * when editable = true (defaults to false)
39486      */
39487     selectOnFocus:false,
39488     /**
39489      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39490      */
39491     queryParam: 'query',
39492     /**
39493      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39494      * when mode = 'remote' (defaults to 'Loading...')
39495      */
39496     loadingText: 'Loading...',
39497     /**
39498      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39499      */
39500     resizable: false,
39501     /**
39502      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39503      */
39504     handleHeight : 8,
39505     /**
39506      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39507      * traditional select (defaults to true)
39508      */
39509     editable: true,
39510     /**
39511      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39512      */
39513     allQuery: '',
39514     /**
39515      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39516      */
39517     mode: 'remote',
39518     /**
39519      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39520      * listWidth has a higher value)
39521      */
39522     minListWidth : 70,
39523     /**
39524      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39525      * allow the user to set arbitrary text into the field (defaults to false)
39526      */
39527     forceSelection:false,
39528     /**
39529      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39530      * if typeAhead = true (defaults to 250)
39531      */
39532     typeAheadDelay : 250,
39533     /**
39534      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39535      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39536      */
39537     valueNotFoundText : undefined,
39538     /**
39539      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39540      */
39541     blockFocus : false,
39542     
39543     /**
39544      * @cfg {Boolean} disableClear Disable showing of clear button.
39545      */
39546     disableClear : false,
39547     /**
39548      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39549      */
39550     alwaysQuery : false,
39551     
39552     //private
39553     addicon : false,
39554     editicon: false,
39555     
39556     // element that contains real text value.. (when hidden is used..)
39557      
39558     // private
39559     onRender : function(ct, position){
39560         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39561         if(this.hiddenName){
39562             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39563                     'before', true);
39564             this.hiddenField.value =
39565                 this.hiddenValue !== undefined ? this.hiddenValue :
39566                 this.value !== undefined ? this.value : '';
39567
39568             // prevent input submission
39569             this.el.dom.removeAttribute('name');
39570              
39571              
39572         }
39573         if(Roo.isGecko){
39574             this.el.dom.setAttribute('autocomplete', 'off');
39575         }
39576
39577         var cls = 'x-combo-list';
39578
39579         this.list = new Roo.Layer({
39580             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39581         });
39582
39583         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39584         this.list.setWidth(lw);
39585         this.list.swallowEvent('mousewheel');
39586         this.assetHeight = 0;
39587
39588         if(this.title){
39589             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39590             this.assetHeight += this.header.getHeight();
39591         }
39592
39593         this.innerList = this.list.createChild({cls:cls+'-inner'});
39594         this.innerList.on('mouseover', this.onViewOver, this);
39595         this.innerList.on('mousemove', this.onViewMove, this);
39596         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39597         
39598         if(this.allowBlank && !this.pageSize && !this.disableClear){
39599             this.footer = this.list.createChild({cls:cls+'-ft'});
39600             this.pageTb = new Roo.Toolbar(this.footer);
39601            
39602         }
39603         if(this.pageSize){
39604             this.footer = this.list.createChild({cls:cls+'-ft'});
39605             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39606                     {pageSize: this.pageSize});
39607             
39608         }
39609         
39610         if (this.pageTb && this.allowBlank && !this.disableClear) {
39611             var _this = this;
39612             this.pageTb.add(new Roo.Toolbar.Fill(), {
39613                 cls: 'x-btn-icon x-btn-clear',
39614                 text: '&#160;',
39615                 handler: function()
39616                 {
39617                     _this.collapse();
39618                     _this.clearValue();
39619                     _this.onSelect(false, -1);
39620                 }
39621             });
39622         }
39623         if (this.footer) {
39624             this.assetHeight += this.footer.getHeight();
39625         }
39626         
39627
39628         if(!this.tpl){
39629             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39630         }
39631
39632         this.view = new Roo.View(this.innerList, this.tpl, {
39633             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39634         });
39635
39636         this.view.on('click', this.onViewClick, this);
39637
39638         this.store.on('beforeload', this.onBeforeLoad, this);
39639         this.store.on('load', this.onLoad, this);
39640         this.store.on('loadexception', this.onLoadException, this);
39641
39642         if(this.resizable){
39643             this.resizer = new Roo.Resizable(this.list,  {
39644                pinned:true, handles:'se'
39645             });
39646             this.resizer.on('resize', function(r, w, h){
39647                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39648                 this.listWidth = w;
39649                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39650                 this.restrictHeight();
39651             }, this);
39652             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39653         }
39654         if(!this.editable){
39655             this.editable = true;
39656             this.setEditable(false);
39657         }  
39658         
39659         
39660         if (typeof(this.events.add.listeners) != 'undefined') {
39661             
39662             this.addicon = this.wrap.createChild(
39663                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39664        
39665             this.addicon.on('click', function(e) {
39666                 this.fireEvent('add', this);
39667             }, this);
39668         }
39669         if (typeof(this.events.edit.listeners) != 'undefined') {
39670             
39671             this.editicon = this.wrap.createChild(
39672                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39673             if (this.addicon) {
39674                 this.editicon.setStyle('margin-left', '40px');
39675             }
39676             this.editicon.on('click', function(e) {
39677                 
39678                 // we fire even  if inothing is selected..
39679                 this.fireEvent('edit', this, this.lastData );
39680                 
39681             }, this);
39682         }
39683         
39684         
39685         
39686     },
39687
39688     // private
39689     initEvents : function(){
39690         Roo.form.ComboBox.superclass.initEvents.call(this);
39691
39692         this.keyNav = new Roo.KeyNav(this.el, {
39693             "up" : function(e){
39694                 this.inKeyMode = true;
39695                 this.selectPrev();
39696             },
39697
39698             "down" : function(e){
39699                 if(!this.isExpanded()){
39700                     this.onTriggerClick();
39701                 }else{
39702                     this.inKeyMode = true;
39703                     this.selectNext();
39704                 }
39705             },
39706
39707             "enter" : function(e){
39708                 this.onViewClick();
39709                 //return true;
39710             },
39711
39712             "esc" : function(e){
39713                 this.collapse();
39714             },
39715
39716             "tab" : function(e){
39717                 this.onViewClick(false);
39718                 this.fireEvent("specialkey", this, e);
39719                 return true;
39720             },
39721
39722             scope : this,
39723
39724             doRelay : function(foo, bar, hname){
39725                 if(hname == 'down' || this.scope.isExpanded()){
39726                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39727                 }
39728                 return true;
39729             },
39730
39731             forceKeyDown: true
39732         });
39733         this.queryDelay = Math.max(this.queryDelay || 10,
39734                 this.mode == 'local' ? 10 : 250);
39735         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39736         if(this.typeAhead){
39737             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39738         }
39739         if(this.editable !== false){
39740             this.el.on("keyup", this.onKeyUp, this);
39741         }
39742         if(this.forceSelection){
39743             this.on('blur', this.doForce, this);
39744         }
39745     },
39746
39747     onDestroy : function(){
39748         if(this.view){
39749             this.view.setStore(null);
39750             this.view.el.removeAllListeners();
39751             this.view.el.remove();
39752             this.view.purgeListeners();
39753         }
39754         if(this.list){
39755             this.list.destroy();
39756         }
39757         if(this.store){
39758             this.store.un('beforeload', this.onBeforeLoad, this);
39759             this.store.un('load', this.onLoad, this);
39760             this.store.un('loadexception', this.onLoadException, this);
39761         }
39762         Roo.form.ComboBox.superclass.onDestroy.call(this);
39763     },
39764
39765     // private
39766     fireKey : function(e){
39767         if(e.isNavKeyPress() && !this.list.isVisible()){
39768             this.fireEvent("specialkey", this, e);
39769         }
39770     },
39771
39772     // private
39773     onResize: function(w, h){
39774         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39775         
39776         if(typeof w != 'number'){
39777             // we do not handle it!?!?
39778             return;
39779         }
39780         var tw = this.trigger.getWidth();
39781         tw += this.addicon ? this.addicon.getWidth() : 0;
39782         tw += this.editicon ? this.editicon.getWidth() : 0;
39783         var x = w - tw;
39784         this.el.setWidth( this.adjustWidth('input', x));
39785             
39786         this.trigger.setStyle('left', x+'px');
39787         
39788         if(this.list && this.listWidth === undefined){
39789             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39790             this.list.setWidth(lw);
39791             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39792         }
39793         
39794     
39795         
39796     },
39797
39798     /**
39799      * Allow or prevent the user from directly editing the field text.  If false is passed,
39800      * the user will only be able to select from the items defined in the dropdown list.  This method
39801      * is the runtime equivalent of setting the 'editable' config option at config time.
39802      * @param {Boolean} value True to allow the user to directly edit the field text
39803      */
39804     setEditable : function(value){
39805         if(value == this.editable){
39806             return;
39807         }
39808         this.editable = value;
39809         if(!value){
39810             this.el.dom.setAttribute('readOnly', true);
39811             this.el.on('mousedown', this.onTriggerClick,  this);
39812             this.el.addClass('x-combo-noedit');
39813         }else{
39814             this.el.dom.setAttribute('readOnly', false);
39815             this.el.un('mousedown', this.onTriggerClick,  this);
39816             this.el.removeClass('x-combo-noedit');
39817         }
39818     },
39819
39820     // private
39821     onBeforeLoad : function(){
39822         if(!this.hasFocus){
39823             return;
39824         }
39825         this.innerList.update(this.loadingText ?
39826                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39827         this.restrictHeight();
39828         this.selectedIndex = -1;
39829     },
39830
39831     // private
39832     onLoad : function(){
39833         if(!this.hasFocus){
39834             return;
39835         }
39836         if(this.store.getCount() > 0){
39837             this.expand();
39838             this.restrictHeight();
39839             if(this.lastQuery == this.allQuery){
39840                 if(this.editable){
39841                     this.el.dom.select();
39842                 }
39843                 if(!this.selectByValue(this.value, true)){
39844                     this.select(0, true);
39845                 }
39846             }else{
39847                 this.selectNext();
39848                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39849                     this.taTask.delay(this.typeAheadDelay);
39850                 }
39851             }
39852         }else{
39853             this.onEmptyResults();
39854         }
39855         //this.el.focus();
39856     },
39857     // private
39858     onLoadException : function()
39859     {
39860         this.collapse();
39861         Roo.log(this.store.reader.jsonData);
39862         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39863             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39864         }
39865         
39866         
39867     },
39868     // private
39869     onTypeAhead : function(){
39870         if(this.store.getCount() > 0){
39871             var r = this.store.getAt(0);
39872             var newValue = r.data[this.displayField];
39873             var len = newValue.length;
39874             var selStart = this.getRawValue().length;
39875             if(selStart != len){
39876                 this.setRawValue(newValue);
39877                 this.selectText(selStart, newValue.length);
39878             }
39879         }
39880     },
39881
39882     // private
39883     onSelect : function(record, index){
39884         if(this.fireEvent('beforeselect', this, record, index) !== false){
39885             this.setFromData(index > -1 ? record.data : false);
39886             this.collapse();
39887             this.fireEvent('select', this, record, index);
39888         }
39889     },
39890
39891     /**
39892      * Returns the currently selected field value or empty string if no value is set.
39893      * @return {String} value The selected value
39894      */
39895     getValue : function(){
39896         if(this.valueField){
39897             return typeof this.value != 'undefined' ? this.value : '';
39898         }
39899         return Roo.form.ComboBox.superclass.getValue.call(this);
39900     },
39901
39902     /**
39903      * Clears any text/value currently set in the field
39904      */
39905     clearValue : function(){
39906         if(this.hiddenField){
39907             this.hiddenField.value = '';
39908         }
39909         this.value = '';
39910         this.setRawValue('');
39911         this.lastSelectionText = '';
39912         
39913     },
39914
39915     /**
39916      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39917      * will be displayed in the field.  If the value does not match the data value of an existing item,
39918      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39919      * Otherwise the field will be blank (although the value will still be set).
39920      * @param {String} value The value to match
39921      */
39922     setValue : function(v){
39923         var text = v;
39924         if(this.valueField){
39925             var r = this.findRecord(this.valueField, v);
39926             if(r){
39927                 text = r.data[this.displayField];
39928             }else if(this.valueNotFoundText !== undefined){
39929                 text = this.valueNotFoundText;
39930             }
39931         }
39932         this.lastSelectionText = text;
39933         if(this.hiddenField){
39934             this.hiddenField.value = v;
39935         }
39936         Roo.form.ComboBox.superclass.setValue.call(this, text);
39937         this.value = v;
39938     },
39939     /**
39940      * @property {Object} the last set data for the element
39941      */
39942     
39943     lastData : false,
39944     /**
39945      * Sets the value of the field based on a object which is related to the record format for the store.
39946      * @param {Object} value the value to set as. or false on reset?
39947      */
39948     setFromData : function(o){
39949         var dv = ''; // display value
39950         var vv = ''; // value value..
39951         this.lastData = o;
39952         if (this.displayField) {
39953             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39954         } else {
39955             // this is an error condition!!!
39956             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39957         }
39958         
39959         if(this.valueField){
39960             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39961         }
39962         if(this.hiddenField){
39963             this.hiddenField.value = vv;
39964             
39965             this.lastSelectionText = dv;
39966             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39967             this.value = vv;
39968             return;
39969         }
39970         // no hidden field.. - we store the value in 'value', but still display
39971         // display field!!!!
39972         this.lastSelectionText = dv;
39973         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39974         this.value = vv;
39975         
39976         
39977     },
39978     // private
39979     reset : function(){
39980         // overridden so that last data is reset..
39981         this.setValue(this.resetValue);
39982         this.clearInvalid();
39983         this.lastData = false;
39984         if (this.view) {
39985             this.view.clearSelections();
39986         }
39987     },
39988     // private
39989     findRecord : function(prop, value){
39990         var record;
39991         if(this.store.getCount() > 0){
39992             this.store.each(function(r){
39993                 if(r.data[prop] == value){
39994                     record = r;
39995                     return false;
39996                 }
39997                 return true;
39998             });
39999         }
40000         return record;
40001     },
40002     
40003     getName: function()
40004     {
40005         // returns hidden if it's set..
40006         if (!this.rendered) {return ''};
40007         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40008         
40009     },
40010     // private
40011     onViewMove : function(e, t){
40012         this.inKeyMode = false;
40013     },
40014
40015     // private
40016     onViewOver : function(e, t){
40017         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40018             return;
40019         }
40020         var item = this.view.findItemFromChild(t);
40021         if(item){
40022             var index = this.view.indexOf(item);
40023             this.select(index, false);
40024         }
40025     },
40026
40027     // private
40028     onViewClick : function(doFocus)
40029     {
40030         var index = this.view.getSelectedIndexes()[0];
40031         var r = this.store.getAt(index);
40032         if(r){
40033             this.onSelect(r, index);
40034         }
40035         if(doFocus !== false && !this.blockFocus){
40036             this.el.focus();
40037         }
40038     },
40039
40040     // private
40041     restrictHeight : function(){
40042         this.innerList.dom.style.height = '';
40043         var inner = this.innerList.dom;
40044         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40045         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40046         this.list.beginUpdate();
40047         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40048         this.list.alignTo(this.el, this.listAlign);
40049         this.list.endUpdate();
40050     },
40051
40052     // private
40053     onEmptyResults : function(){
40054         this.collapse();
40055     },
40056
40057     /**
40058      * Returns true if the dropdown list is expanded, else false.
40059      */
40060     isExpanded : function(){
40061         return this.list.isVisible();
40062     },
40063
40064     /**
40065      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40066      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40067      * @param {String} value The data value of the item to select
40068      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40069      * selected item if it is not currently in view (defaults to true)
40070      * @return {Boolean} True if the value matched an item in the list, else false
40071      */
40072     selectByValue : function(v, scrollIntoView){
40073         if(v !== undefined && v !== null){
40074             var r = this.findRecord(this.valueField || this.displayField, v);
40075             if(r){
40076                 this.select(this.store.indexOf(r), scrollIntoView);
40077                 return true;
40078             }
40079         }
40080         return false;
40081     },
40082
40083     /**
40084      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40085      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40086      * @param {Number} index The zero-based index of the list item to select
40087      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40088      * selected item if it is not currently in view (defaults to true)
40089      */
40090     select : function(index, scrollIntoView){
40091         this.selectedIndex = index;
40092         this.view.select(index);
40093         if(scrollIntoView !== false){
40094             var el = this.view.getNode(index);
40095             if(el){
40096                 this.innerList.scrollChildIntoView(el, false);
40097             }
40098         }
40099     },
40100
40101     // private
40102     selectNext : function(){
40103         var ct = this.store.getCount();
40104         if(ct > 0){
40105             if(this.selectedIndex == -1){
40106                 this.select(0);
40107             }else if(this.selectedIndex < ct-1){
40108                 this.select(this.selectedIndex+1);
40109             }
40110         }
40111     },
40112
40113     // private
40114     selectPrev : function(){
40115         var ct = this.store.getCount();
40116         if(ct > 0){
40117             if(this.selectedIndex == -1){
40118                 this.select(0);
40119             }else if(this.selectedIndex != 0){
40120                 this.select(this.selectedIndex-1);
40121             }
40122         }
40123     },
40124
40125     // private
40126     onKeyUp : function(e){
40127         if(this.editable !== false && !e.isSpecialKey()){
40128             this.lastKey = e.getKey();
40129             this.dqTask.delay(this.queryDelay);
40130         }
40131     },
40132
40133     // private
40134     validateBlur : function(){
40135         return !this.list || !this.list.isVisible();   
40136     },
40137
40138     // private
40139     initQuery : function(){
40140         this.doQuery(this.getRawValue());
40141     },
40142
40143     // private
40144     doForce : function(){
40145         if(this.el.dom.value.length > 0){
40146             this.el.dom.value =
40147                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40148              
40149         }
40150     },
40151
40152     /**
40153      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40154      * query allowing the query action to be canceled if needed.
40155      * @param {String} query The SQL query to execute
40156      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40157      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40158      * saved in the current store (defaults to false)
40159      */
40160     doQuery : function(q, forceAll){
40161         if(q === undefined || q === null){
40162             q = '';
40163         }
40164         var qe = {
40165             query: q,
40166             forceAll: forceAll,
40167             combo: this,
40168             cancel:false
40169         };
40170         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40171             return false;
40172         }
40173         q = qe.query;
40174         forceAll = qe.forceAll;
40175         if(forceAll === true || (q.length >= this.minChars)){
40176             if(this.lastQuery != q || this.alwaysQuery){
40177                 this.lastQuery = q;
40178                 if(this.mode == 'local'){
40179                     this.selectedIndex = -1;
40180                     if(forceAll){
40181                         this.store.clearFilter();
40182                     }else{
40183                         this.store.filter(this.displayField, q);
40184                     }
40185                     this.onLoad();
40186                 }else{
40187                     this.store.baseParams[this.queryParam] = q;
40188                     this.store.load({
40189                         params: this.getParams(q)
40190                     });
40191                     this.expand();
40192                 }
40193             }else{
40194                 this.selectedIndex = -1;
40195                 this.onLoad();   
40196             }
40197         }
40198     },
40199
40200     // private
40201     getParams : function(q){
40202         var p = {};
40203         //p[this.queryParam] = q;
40204         if(this.pageSize){
40205             p.start = 0;
40206             p.limit = this.pageSize;
40207         }
40208         return p;
40209     },
40210
40211     /**
40212      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40213      */
40214     collapse : function(){
40215         if(!this.isExpanded()){
40216             return;
40217         }
40218         this.list.hide();
40219         Roo.get(document).un('mousedown', this.collapseIf, this);
40220         Roo.get(document).un('mousewheel', this.collapseIf, this);
40221         if (!this.editable) {
40222             Roo.get(document).un('keydown', this.listKeyPress, this);
40223         }
40224         this.fireEvent('collapse', this);
40225     },
40226
40227     // private
40228     collapseIf : function(e){
40229         if(!e.within(this.wrap) && !e.within(this.list)){
40230             this.collapse();
40231         }
40232     },
40233
40234     /**
40235      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40236      */
40237     expand : function(){
40238         if(this.isExpanded() || !this.hasFocus){
40239             return;
40240         }
40241         this.list.alignTo(this.el, this.listAlign);
40242         this.list.show();
40243         Roo.get(document).on('mousedown', this.collapseIf, this);
40244         Roo.get(document).on('mousewheel', this.collapseIf, this);
40245         if (!this.editable) {
40246             Roo.get(document).on('keydown', this.listKeyPress, this);
40247         }
40248         
40249         this.fireEvent('expand', this);
40250     },
40251
40252     // private
40253     // Implements the default empty TriggerField.onTriggerClick function
40254     onTriggerClick : function(){
40255         if(this.disabled){
40256             return;
40257         }
40258         if(this.isExpanded()){
40259             this.collapse();
40260             if (!this.blockFocus) {
40261                 this.el.focus();
40262             }
40263             
40264         }else {
40265             this.hasFocus = true;
40266             if(this.triggerAction == 'all') {
40267                 this.doQuery(this.allQuery, true);
40268             } else {
40269                 this.doQuery(this.getRawValue());
40270             }
40271             if (!this.blockFocus) {
40272                 this.el.focus();
40273             }
40274         }
40275     },
40276     listKeyPress : function(e)
40277     {
40278         //Roo.log('listkeypress');
40279         // scroll to first matching element based on key pres..
40280         if (e.isSpecialKey()) {
40281             return false;
40282         }
40283         var k = String.fromCharCode(e.getKey()).toUpperCase();
40284         //Roo.log(k);
40285         var match  = false;
40286         var csel = this.view.getSelectedNodes();
40287         var cselitem = false;
40288         if (csel.length) {
40289             var ix = this.view.indexOf(csel[0]);
40290             cselitem  = this.store.getAt(ix);
40291             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40292                 cselitem = false;
40293             }
40294             
40295         }
40296         
40297         this.store.each(function(v) { 
40298             if (cselitem) {
40299                 // start at existing selection.
40300                 if (cselitem.id == v.id) {
40301                     cselitem = false;
40302                 }
40303                 return;
40304             }
40305                 
40306             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40307                 match = this.store.indexOf(v);
40308                 return false;
40309             }
40310         }, this);
40311         
40312         if (match === false) {
40313             return true; // no more action?
40314         }
40315         // scroll to?
40316         this.view.select(match);
40317         var sn = Roo.get(this.view.getSelectedNodes()[0])
40318         sn.scrollIntoView(sn.dom.parentNode, false);
40319     }
40320
40321     /** 
40322     * @cfg {Boolean} grow 
40323     * @hide 
40324     */
40325     /** 
40326     * @cfg {Number} growMin 
40327     * @hide 
40328     */
40329     /** 
40330     * @cfg {Number} growMax 
40331     * @hide 
40332     */
40333     /**
40334      * @hide
40335      * @method autoSize
40336      */
40337 });/*
40338  * Copyright(c) 2010-2012, Roo J Solutions Limited
40339  *
40340  * Licence LGPL
40341  *
40342  */
40343
40344 /**
40345  * @class Roo.form.ComboBoxArray
40346  * @extends Roo.form.TextField
40347  * A facebook style adder... for lists of email / people / countries  etc...
40348  * pick multiple items from a combo box, and shows each one.
40349  *
40350  *  Fred [x]  Brian [x]  [Pick another |v]
40351  *
40352  *
40353  *  For this to work: it needs various extra information
40354  *    - normal combo problay has
40355  *      name, hiddenName
40356  *    + displayField, valueField
40357  *
40358  *    For our purpose...
40359  *
40360  *
40361  *   If we change from 'extends' to wrapping...
40362  *   
40363  *  
40364  *
40365  
40366  
40367  * @constructor
40368  * Create a new ComboBoxArray.
40369  * @param {Object} config Configuration options
40370  */
40371  
40372
40373 Roo.form.ComboBoxArray = function(config)
40374 {
40375     this.addEvents({
40376         /**
40377          * @event beforeremove
40378          * Fires before remove the value from the list
40379              * @param {Roo.form.ComboBoxArray} _self This combo box array
40380              * @param {Roo.form.ComboBoxArray.Item} item removed item
40381              */
40382         'beforeremove' : true,
40383         /**
40384          * @event remove
40385          * Fires when remove the value from the list
40386              * @param {Roo.form.ComboBoxArray} _self This combo box array
40387              * @param {Roo.form.ComboBoxArray.Item} item removed item
40388              */
40389         'remove' : true
40390         
40391         
40392     });
40393     
40394     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40395     
40396     this.items = new Roo.util.MixedCollection(false);
40397     
40398     // construct the child combo...
40399     
40400     
40401     
40402     
40403    
40404     
40405 }
40406
40407  
40408 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40409
40410     /**
40411      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40412      */
40413     
40414     lastData : false,
40415     
40416     // behavies liek a hiddne field
40417     inputType:      'hidden',
40418     /**
40419      * @cfg {Number} width The width of the box that displays the selected element
40420      */ 
40421     width:          300,
40422
40423     
40424     
40425     /**
40426      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40427      */
40428     name : false,
40429     /**
40430      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40431      */
40432     hiddenName : false,
40433     
40434     
40435     // private the array of items that are displayed..
40436     items  : false,
40437     // private - the hidden field el.
40438     hiddenEl : false,
40439     // private - the filed el..
40440     el : false,
40441     
40442     //validateValue : function() { return true; }, // all values are ok!
40443     //onAddClick: function() { },
40444     
40445     onRender : function(ct, position) 
40446     {
40447         
40448         // create the standard hidden element
40449         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40450         
40451         
40452         // give fake names to child combo;
40453         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40454         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40455         
40456         this.combo = Roo.factory(this.combo, Roo.form);
40457         this.combo.onRender(ct, position);
40458         if (typeof(this.combo.width) != 'undefined') {
40459             this.combo.onResize(this.combo.width,0);
40460         }
40461         
40462         this.combo.initEvents();
40463         
40464         // assigned so form know we need to do this..
40465         this.store          = this.combo.store;
40466         this.valueField     = this.combo.valueField;
40467         this.displayField   = this.combo.displayField ;
40468         
40469         
40470         this.combo.wrap.addClass('x-cbarray-grp');
40471         
40472         var cbwrap = this.combo.wrap.createChild(
40473             {tag: 'div', cls: 'x-cbarray-cb'},
40474             this.combo.el.dom
40475         );
40476         
40477              
40478         this.hiddenEl = this.combo.wrap.createChild({
40479             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40480         });
40481         this.el = this.combo.wrap.createChild({
40482             tag: 'input',  type:'hidden' , name: this.name, value : ''
40483         });
40484          //   this.el.dom.removeAttribute("name");
40485         
40486         
40487         this.outerWrap = this.combo.wrap;
40488         this.wrap = cbwrap;
40489         
40490         this.outerWrap.setWidth(this.width);
40491         this.outerWrap.dom.removeChild(this.el.dom);
40492         
40493         this.wrap.dom.appendChild(this.el.dom);
40494         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40495         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40496         
40497         this.combo.trigger.setStyle('position','relative');
40498         this.combo.trigger.setStyle('left', '0px');
40499         this.combo.trigger.setStyle('top', '2px');
40500         
40501         this.combo.el.setStyle('vertical-align', 'text-bottom');
40502         
40503         //this.trigger.setStyle('vertical-align', 'top');
40504         
40505         // this should use the code from combo really... on('add' ....)
40506         if (this.adder) {
40507             
40508         
40509             this.adder = this.outerWrap.createChild(
40510                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40511             var _t = this;
40512             this.adder.on('click', function(e) {
40513                 _t.fireEvent('adderclick', this, e);
40514             }, _t);
40515         }
40516         //var _t = this;
40517         //this.adder.on('click', this.onAddClick, _t);
40518         
40519         
40520         this.combo.on('select', function(cb, rec, ix) {
40521             this.addItem(rec.data);
40522             
40523             cb.setValue('');
40524             cb.el.dom.value = '';
40525             //cb.lastData = rec.data;
40526             // add to list
40527             
40528         }, this);
40529         
40530         
40531     },
40532     
40533     
40534     getName: function()
40535     {
40536         // returns hidden if it's set..
40537         if (!this.rendered) {return ''};
40538         return  this.hiddenName ? this.hiddenName : this.name;
40539         
40540     },
40541     
40542     
40543     onResize: function(w, h){
40544         
40545         return;
40546         // not sure if this is needed..
40547         //this.combo.onResize(w,h);
40548         
40549         if(typeof w != 'number'){
40550             // we do not handle it!?!?
40551             return;
40552         }
40553         var tw = this.combo.trigger.getWidth();
40554         tw += this.addicon ? this.addicon.getWidth() : 0;
40555         tw += this.editicon ? this.editicon.getWidth() : 0;
40556         var x = w - tw;
40557         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40558             
40559         this.combo.trigger.setStyle('left', '0px');
40560         
40561         if(this.list && this.listWidth === undefined){
40562             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40563             this.list.setWidth(lw);
40564             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40565         }
40566         
40567     
40568         
40569     },
40570     
40571     addItem: function(rec)
40572     {
40573         var valueField = this.combo.valueField;
40574         var displayField = this.combo.displayField;
40575         if (this.items.indexOfKey(rec[valueField]) > -1) {
40576             //console.log("GOT " + rec.data.id);
40577             return;
40578         }
40579         
40580         var x = new Roo.form.ComboBoxArray.Item({
40581             //id : rec[this.idField],
40582             data : rec,
40583             displayField : displayField ,
40584             tipField : displayField ,
40585             cb : this
40586         });
40587         // use the 
40588         this.items.add(rec[valueField],x);
40589         // add it before the element..
40590         this.updateHiddenEl();
40591         x.render(this.outerWrap, this.wrap.dom);
40592         // add the image handler..
40593     },
40594     
40595     updateHiddenEl : function()
40596     {
40597         this.validate();
40598         if (!this.hiddenEl) {
40599             return;
40600         }
40601         var ar = [];
40602         var idField = this.combo.valueField;
40603         
40604         this.items.each(function(f) {
40605             ar.push(f.data[idField]);
40606            
40607         });
40608         this.hiddenEl.dom.value = ar.join(',');
40609         this.validate();
40610     },
40611     
40612     reset : function()
40613     {
40614         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40615         this.items.each(function(f) {
40616            f.remove(); 
40617         });
40618         this.el.dom.value = '';
40619         if (this.hiddenEl) {
40620             this.hiddenEl.dom.value = '';
40621         }
40622         
40623     },
40624     getValue: function()
40625     {
40626         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40627     },
40628     setValue: function(v) // not a valid action - must use addItems..
40629     {
40630          
40631         this.reset();
40632         
40633         
40634         
40635         if (this.store.isLocal && (typeof(v) == 'string')) {
40636             // then we can use the store to find the values..
40637             // comma seperated at present.. this needs to allow JSON based encoding..
40638             this.hiddenEl.value  = v;
40639             var v_ar = [];
40640             Roo.each(v.split(','), function(k) {
40641                 Roo.log("CHECK " + this.valueField + ',' + k);
40642                 var li = this.store.query(this.valueField, k);
40643                 if (!li.length) {
40644                     return;
40645                 }
40646                 var add = {};
40647                 add[this.valueField] = k;
40648                 add[this.displayField] = li.item(0).data[this.displayField];
40649                 
40650                 this.addItem(add);
40651             }, this) 
40652              
40653         }
40654         if (typeof(v) == 'object' ) {
40655             // then let's assume it's an array of objects..
40656             Roo.each(v, function(l) {
40657                 this.addItem(l);
40658             }, this);
40659              
40660         }
40661         
40662         
40663     },
40664     setFromData: function(v)
40665     {
40666         // this recieves an object, if setValues is called.
40667         this.reset();
40668         this.el.dom.value = v[this.displayField];
40669         this.hiddenEl.dom.value = v[this.valueField];
40670         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40671             return;
40672         }
40673         var kv = v[this.valueField];
40674         var dv = v[this.displayField];
40675         kv = typeof(kv) != 'string' ? '' : kv;
40676         dv = typeof(dv) != 'string' ? '' : dv;
40677         
40678         
40679         var keys = kv.split(',');
40680         var display = dv.split(',');
40681         for (var i = 0 ; i < keys.length; i++) {
40682             
40683             add = {};
40684             add[this.valueField] = keys[i];
40685             add[this.displayField] = display[i];
40686             this.addItem(add);
40687         }
40688       
40689         
40690     },
40691     
40692     /**
40693      * Validates the combox array value
40694      * @return {Boolean} True if the value is valid, else false
40695      */
40696     validate : function(){
40697         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40698             this.clearInvalid();
40699             return true;
40700         }
40701         return false;
40702     },
40703     
40704     validateValue : function(value){
40705         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40706         
40707     },
40708     
40709     /*@
40710      * overide
40711      * 
40712      */
40713     isDirty : function() {
40714         if(this.disabled) {
40715             return false;
40716         }
40717         
40718         try {
40719             var d = Roo.decode(String(this.originalValue));
40720         } catch (e) {
40721             return String(this.getValue()) !== String(this.originalValue);
40722         }
40723         
40724         var originalValue = [];
40725         
40726         for (var i = 0; i < d.length; i++){
40727             originalValue.push(d[i][this.valueField]);
40728         }
40729         
40730         return String(this.getValue()) !== String(originalValue.join(','));
40731         
40732     }
40733     
40734 });
40735
40736
40737
40738 /**
40739  * @class Roo.form.ComboBoxArray.Item
40740  * @extends Roo.BoxComponent
40741  * A selected item in the list
40742  *  Fred [x]  Brian [x]  [Pick another |v]
40743  * 
40744  * @constructor
40745  * Create a new item.
40746  * @param {Object} config Configuration options
40747  */
40748  
40749 Roo.form.ComboBoxArray.Item = function(config) {
40750     config.id = Roo.id();
40751     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40752 }
40753
40754 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40755     data : {},
40756     cb: false,
40757     displayField : false,
40758     tipField : false,
40759     
40760     
40761     defaultAutoCreate : {
40762         tag: 'div',
40763         cls: 'x-cbarray-item',
40764         cn : [ 
40765             { tag: 'div' },
40766             {
40767                 tag: 'img',
40768                 width:16,
40769                 height : 16,
40770                 src : Roo.BLANK_IMAGE_URL ,
40771                 align: 'center'
40772             }
40773         ]
40774         
40775     },
40776     
40777  
40778     onRender : function(ct, position)
40779     {
40780         Roo.form.Field.superclass.onRender.call(this, ct, position);
40781         
40782         if(!this.el){
40783             var cfg = this.getAutoCreate();
40784             this.el = ct.createChild(cfg, position);
40785         }
40786         
40787         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40788         
40789         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40790             this.cb.renderer(this.data) :
40791             String.format('{0}',this.data[this.displayField]);
40792         
40793             
40794         this.el.child('div').dom.setAttribute('qtip',
40795                         String.format('{0}',this.data[this.tipField])
40796         );
40797         
40798         this.el.child('img').on('click', this.remove, this);
40799         
40800     },
40801    
40802     remove : function()
40803     {
40804         if(this.cb.disabled){
40805             return;
40806         }
40807         
40808         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40809             this.cb.items.remove(this);
40810             this.el.child('img').un('click', this.remove, this);
40811             this.el.remove();
40812             this.cb.updateHiddenEl();
40813
40814             this.cb.fireEvent('remove', this.cb, this);
40815         }
40816         
40817     }
40818 });/*
40819  * Based on:
40820  * Ext JS Library 1.1.1
40821  * Copyright(c) 2006-2007, Ext JS, LLC.
40822  *
40823  * Originally Released Under LGPL - original licence link has changed is not relivant.
40824  *
40825  * Fork - LGPL
40826  * <script type="text/javascript">
40827  */
40828 /**
40829  * @class Roo.form.Checkbox
40830  * @extends Roo.form.Field
40831  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40832  * @constructor
40833  * Creates a new Checkbox
40834  * @param {Object} config Configuration options
40835  */
40836 Roo.form.Checkbox = function(config){
40837     Roo.form.Checkbox.superclass.constructor.call(this, config);
40838     this.addEvents({
40839         /**
40840          * @event check
40841          * Fires when the checkbox is checked or unchecked.
40842              * @param {Roo.form.Checkbox} this This checkbox
40843              * @param {Boolean} checked The new checked value
40844              */
40845         check : true
40846     });
40847 };
40848
40849 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40850     /**
40851      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40852      */
40853     focusClass : undefined,
40854     /**
40855      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40856      */
40857     fieldClass: "x-form-field",
40858     /**
40859      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40860      */
40861     checked: false,
40862     /**
40863      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40864      * {tag: "input", type: "checkbox", autocomplete: "off"})
40865      */
40866     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40867     /**
40868      * @cfg {String} boxLabel The text that appears beside the checkbox
40869      */
40870     boxLabel : "",
40871     /**
40872      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40873      */  
40874     inputValue : '1',
40875     /**
40876      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40877      */
40878      valueOff: '0', // value when not checked..
40879
40880     actionMode : 'viewEl', 
40881     //
40882     // private
40883     itemCls : 'x-menu-check-item x-form-item',
40884     groupClass : 'x-menu-group-item',
40885     inputType : 'hidden',
40886     
40887     
40888     inSetChecked: false, // check that we are not calling self...
40889     
40890     inputElement: false, // real input element?
40891     basedOn: false, // ????
40892     
40893     isFormField: true, // not sure where this is needed!!!!
40894
40895     onResize : function(){
40896         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40897         if(!this.boxLabel){
40898             this.el.alignTo(this.wrap, 'c-c');
40899         }
40900     },
40901
40902     initEvents : function(){
40903         Roo.form.Checkbox.superclass.initEvents.call(this);
40904         this.el.on("click", this.onClick,  this);
40905         this.el.on("change", this.onClick,  this);
40906     },
40907
40908
40909     getResizeEl : function(){
40910         return this.wrap;
40911     },
40912
40913     getPositionEl : function(){
40914         return this.wrap;
40915     },
40916
40917     // private
40918     onRender : function(ct, position){
40919         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40920         /*
40921         if(this.inputValue !== undefined){
40922             this.el.dom.value = this.inputValue;
40923         }
40924         */
40925         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40926         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40927         var viewEl = this.wrap.createChild({ 
40928             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40929         this.viewEl = viewEl;   
40930         this.wrap.on('click', this.onClick,  this); 
40931         
40932         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40933         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40934         
40935         
40936         
40937         if(this.boxLabel){
40938             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40939         //    viewEl.on('click', this.onClick,  this); 
40940         }
40941         //if(this.checked){
40942             this.setChecked(this.checked);
40943         //}else{
40944             //this.checked = this.el.dom;
40945         //}
40946
40947     },
40948
40949     // private
40950     initValue : Roo.emptyFn,
40951
40952     /**
40953      * Returns the checked state of the checkbox.
40954      * @return {Boolean} True if checked, else false
40955      */
40956     getValue : function(){
40957         if(this.el){
40958             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40959         }
40960         return this.valueOff;
40961         
40962     },
40963
40964         // private
40965     onClick : function(){ 
40966         if (this.disabled) {
40967             return;
40968         }
40969         this.setChecked(!this.checked);
40970
40971         //if(this.el.dom.checked != this.checked){
40972         //    this.setValue(this.el.dom.checked);
40973        // }
40974     },
40975
40976     /**
40977      * Sets the checked state of the checkbox.
40978      * On is always based on a string comparison between inputValue and the param.
40979      * @param {Boolean/String} value - the value to set 
40980      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40981      */
40982     setValue : function(v,suppressEvent){
40983         
40984         
40985         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40986         //if(this.el && this.el.dom){
40987         //    this.el.dom.checked = this.checked;
40988         //    this.el.dom.defaultChecked = this.checked;
40989         //}
40990         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40991         //this.fireEvent("check", this, this.checked);
40992     },
40993     // private..
40994     setChecked : function(state,suppressEvent)
40995     {
40996         if (this.inSetChecked) {
40997             this.checked = state;
40998             return;
40999         }
41000         
41001     
41002         if(this.wrap){
41003             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41004         }
41005         this.checked = state;
41006         if(suppressEvent !== true){
41007             this.fireEvent('check', this, state);
41008         }
41009         this.inSetChecked = true;
41010         this.el.dom.value = state ? this.inputValue : this.valueOff;
41011         this.inSetChecked = false;
41012         
41013     },
41014     // handle setting of hidden value by some other method!!?!?
41015     setFromHidden: function()
41016     {
41017         if(!this.el){
41018             return;
41019         }
41020         //console.log("SET FROM HIDDEN");
41021         //alert('setFrom hidden');
41022         this.setValue(this.el.dom.value);
41023     },
41024     
41025     onDestroy : function()
41026     {
41027         if(this.viewEl){
41028             Roo.get(this.viewEl).remove();
41029         }
41030          
41031         Roo.form.Checkbox.superclass.onDestroy.call(this);
41032     }
41033
41034 });/*
41035  * Based on:
41036  * Ext JS Library 1.1.1
41037  * Copyright(c) 2006-2007, Ext JS, LLC.
41038  *
41039  * Originally Released Under LGPL - original licence link has changed is not relivant.
41040  *
41041  * Fork - LGPL
41042  * <script type="text/javascript">
41043  */
41044  
41045 /**
41046  * @class Roo.form.Radio
41047  * @extends Roo.form.Checkbox
41048  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41049  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41050  * @constructor
41051  * Creates a new Radio
41052  * @param {Object} config Configuration options
41053  */
41054 Roo.form.Radio = function(){
41055     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41056 };
41057 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41058     inputType: 'radio',
41059
41060     /**
41061      * If this radio is part of a group, it will return the selected value
41062      * @return {String}
41063      */
41064     getGroupValue : function(){
41065         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41066     },
41067     
41068     
41069     onRender : function(ct, position){
41070         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41071         
41072         if(this.inputValue !== undefined){
41073             this.el.dom.value = this.inputValue;
41074         }
41075          
41076         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41077         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41078         //var viewEl = this.wrap.createChild({ 
41079         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41080         //this.viewEl = viewEl;   
41081         //this.wrap.on('click', this.onClick,  this); 
41082         
41083         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41084         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41085         
41086         
41087         
41088         if(this.boxLabel){
41089             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41090         //    viewEl.on('click', this.onClick,  this); 
41091         }
41092          if(this.checked){
41093             this.el.dom.checked =   'checked' ;
41094         }
41095          
41096     } 
41097     
41098     
41099 });//<script type="text/javascript">
41100
41101 /*
41102  * Based  Ext JS Library 1.1.1
41103  * Copyright(c) 2006-2007, Ext JS, LLC.
41104  * LGPL
41105  *
41106  */
41107  
41108 /**
41109  * @class Roo.HtmlEditorCore
41110  * @extends Roo.Component
41111  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41112  *
41113  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41114  */
41115
41116 Roo.HtmlEditorCore = function(config){
41117     
41118     
41119     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41120     
41121     
41122     this.addEvents({
41123         /**
41124          * @event initialize
41125          * Fires when the editor is fully initialized (including the iframe)
41126          * @param {Roo.HtmlEditorCore} this
41127          */
41128         initialize: true,
41129         /**
41130          * @event activate
41131          * Fires when the editor is first receives the focus. Any insertion must wait
41132          * until after this event.
41133          * @param {Roo.HtmlEditorCore} this
41134          */
41135         activate: true,
41136          /**
41137          * @event beforesync
41138          * Fires before the textarea is updated with content from the editor iframe. Return false
41139          * to cancel the sync.
41140          * @param {Roo.HtmlEditorCore} this
41141          * @param {String} html
41142          */
41143         beforesync: true,
41144          /**
41145          * @event beforepush
41146          * Fires before the iframe editor is updated with content from the textarea. Return false
41147          * to cancel the push.
41148          * @param {Roo.HtmlEditorCore} this
41149          * @param {String} html
41150          */
41151         beforepush: true,
41152          /**
41153          * @event sync
41154          * Fires when the textarea is updated with content from the editor iframe.
41155          * @param {Roo.HtmlEditorCore} this
41156          * @param {String} html
41157          */
41158         sync: true,
41159          /**
41160          * @event push
41161          * Fires when the iframe editor is updated with content from the textarea.
41162          * @param {Roo.HtmlEditorCore} this
41163          * @param {String} html
41164          */
41165         push: true,
41166         
41167         /**
41168          * @event editorevent
41169          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41170          * @param {Roo.HtmlEditorCore} this
41171          */
41172         editorevent: true
41173         
41174     });
41175     
41176     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41177     
41178     // defaults : white / black...
41179     this.applyBlacklists();
41180     
41181     
41182     
41183 };
41184
41185
41186 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41187
41188
41189      /**
41190      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41191      */
41192     
41193     owner : false,
41194     
41195      /**
41196      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41197      *                        Roo.resizable.
41198      */
41199     resizable : false,
41200      /**
41201      * @cfg {Number} height (in pixels)
41202      */   
41203     height: 300,
41204    /**
41205      * @cfg {Number} width (in pixels)
41206      */   
41207     width: 500,
41208     
41209     /**
41210      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41211      * 
41212      */
41213     stylesheets: false,
41214     
41215     // id of frame..
41216     frameId: false,
41217     
41218     // private properties
41219     validationEvent : false,
41220     deferHeight: true,
41221     initialized : false,
41222     activated : false,
41223     sourceEditMode : false,
41224     onFocus : Roo.emptyFn,
41225     iframePad:3,
41226     hideMode:'offsets',
41227     
41228     clearUp: true,
41229     
41230     // blacklist + whitelisted elements..
41231     black: false,
41232     white: false,
41233      
41234     
41235
41236     /**
41237      * Protected method that will not generally be called directly. It
41238      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41239      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41240      */
41241     getDocMarkup : function(){
41242         // body styles..
41243         var st = '';
41244         
41245         // inherit styels from page...?? 
41246         if (this.stylesheets === false) {
41247             
41248             Roo.get(document.head).select('style').each(function(node) {
41249                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41250             });
41251             
41252             Roo.get(document.head).select('link').each(function(node) { 
41253                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41254             });
41255             
41256         } else if (!this.stylesheets.length) {
41257                 // simple..
41258                 st = '<style type="text/css">' +
41259                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41260                    '</style>';
41261         } else { 
41262             
41263         }
41264         
41265         st +=  '<style type="text/css">' +
41266             'IMG { cursor: pointer } ' +
41267         '</style>';
41268
41269         
41270         return '<html><head>' + st  +
41271             //<style type="text/css">' +
41272             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41273             //'</style>' +
41274             ' </head><body class="roo-htmleditor-body"></body></html>';
41275     },
41276
41277     // private
41278     onRender : function(ct, position)
41279     {
41280         var _t = this;
41281         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41282         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41283         
41284         
41285         this.el.dom.style.border = '0 none';
41286         this.el.dom.setAttribute('tabIndex', -1);
41287         this.el.addClass('x-hidden hide');
41288         
41289         
41290         
41291         if(Roo.isIE){ // fix IE 1px bogus margin
41292             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41293         }
41294        
41295         
41296         this.frameId = Roo.id();
41297         
41298          
41299         
41300         var iframe = this.owner.wrap.createChild({
41301             tag: 'iframe',
41302             cls: 'form-control', // bootstrap..
41303             id: this.frameId,
41304             name: this.frameId,
41305             frameBorder : 'no',
41306             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41307         }, this.el
41308         );
41309         
41310         
41311         this.iframe = iframe.dom;
41312
41313          this.assignDocWin();
41314         
41315         this.doc.designMode = 'on';
41316        
41317         this.doc.open();
41318         this.doc.write(this.getDocMarkup());
41319         this.doc.close();
41320
41321         
41322         var task = { // must defer to wait for browser to be ready
41323             run : function(){
41324                 //console.log("run task?" + this.doc.readyState);
41325                 this.assignDocWin();
41326                 if(this.doc.body || this.doc.readyState == 'complete'){
41327                     try {
41328                         this.doc.designMode="on";
41329                     } catch (e) {
41330                         return;
41331                     }
41332                     Roo.TaskMgr.stop(task);
41333                     this.initEditor.defer(10, this);
41334                 }
41335             },
41336             interval : 10,
41337             duration: 10000,
41338             scope: this
41339         };
41340         Roo.TaskMgr.start(task);
41341
41342     },
41343
41344     // private
41345     onResize : function(w, h)
41346     {
41347          Roo.log('resize: ' +w + ',' + h );
41348         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41349         if(!this.iframe){
41350             return;
41351         }
41352         if(typeof w == 'number'){
41353             
41354             this.iframe.style.width = w + 'px';
41355         }
41356         if(typeof h == 'number'){
41357             
41358             this.iframe.style.height = h + 'px';
41359             if(this.doc){
41360                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41361             }
41362         }
41363         
41364     },
41365
41366     /**
41367      * Toggles the editor between standard and source edit mode.
41368      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41369      */
41370     toggleSourceEdit : function(sourceEditMode){
41371         
41372         this.sourceEditMode = sourceEditMode === true;
41373         
41374         if(this.sourceEditMode){
41375  
41376             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41377             
41378         }else{
41379             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41380             //this.iframe.className = '';
41381             this.deferFocus();
41382         }
41383         //this.setSize(this.owner.wrap.getSize());
41384         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41385     },
41386
41387     
41388   
41389
41390     /**
41391      * Protected method that will not generally be called directly. If you need/want
41392      * custom HTML cleanup, this is the method you should override.
41393      * @param {String} html The HTML to be cleaned
41394      * return {String} The cleaned HTML
41395      */
41396     cleanHtml : function(html){
41397         html = String(html);
41398         if(html.length > 5){
41399             if(Roo.isSafari){ // strip safari nonsense
41400                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41401             }
41402         }
41403         if(html == '&nbsp;'){
41404             html = '';
41405         }
41406         return html;
41407     },
41408
41409     /**
41410      * HTML Editor -> Textarea
41411      * Protected method that will not generally be called directly. Syncs the contents
41412      * of the editor iframe with the textarea.
41413      */
41414     syncValue : function(){
41415         if(this.initialized){
41416             var bd = (this.doc.body || this.doc.documentElement);
41417             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41418             var html = bd.innerHTML;
41419             if(Roo.isSafari){
41420                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41421                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41422                 if(m && m[1]){
41423                     html = '<div style="'+m[0]+'">' + html + '</div>';
41424                 }
41425             }
41426             html = this.cleanHtml(html);
41427             // fix up the special chars.. normaly like back quotes in word...
41428             // however we do not want to do this with chinese..
41429             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41430                 var cc = b.charCodeAt();
41431                 if (
41432                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41433                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41434                     (cc >= 0xf900 && cc < 0xfb00 )
41435                 ) {
41436                         return b;
41437                 }
41438                 return "&#"+cc+";" 
41439             });
41440             if(this.owner.fireEvent('beforesync', this, html) !== false){
41441                 this.el.dom.value = html;
41442                 this.owner.fireEvent('sync', this, html);
41443             }
41444         }
41445     },
41446
41447     /**
41448      * Protected method that will not generally be called directly. Pushes the value of the textarea
41449      * into the iframe editor.
41450      */
41451     pushValue : function(){
41452         if(this.initialized){
41453             var v = this.el.dom.value.trim();
41454             
41455 //            if(v.length < 1){
41456 //                v = '&#160;';
41457 //            }
41458             
41459             if(this.owner.fireEvent('beforepush', this, v) !== false){
41460                 var d = (this.doc.body || this.doc.documentElement);
41461                 d.innerHTML = v;
41462                 this.cleanUpPaste();
41463                 this.el.dom.value = d.innerHTML;
41464                 this.owner.fireEvent('push', this, v);
41465             }
41466         }
41467     },
41468
41469     // private
41470     deferFocus : function(){
41471         this.focus.defer(10, this);
41472     },
41473
41474     // doc'ed in Field
41475     focus : function(){
41476         if(this.win && !this.sourceEditMode){
41477             this.win.focus();
41478         }else{
41479             this.el.focus();
41480         }
41481     },
41482     
41483     assignDocWin: function()
41484     {
41485         var iframe = this.iframe;
41486         
41487          if(Roo.isIE){
41488             this.doc = iframe.contentWindow.document;
41489             this.win = iframe.contentWindow;
41490         } else {
41491 //            if (!Roo.get(this.frameId)) {
41492 //                return;
41493 //            }
41494 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41495 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41496             
41497             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41498                 return;
41499             }
41500             
41501             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41502             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41503         }
41504     },
41505     
41506     // private
41507     initEditor : function(){
41508         //console.log("INIT EDITOR");
41509         this.assignDocWin();
41510         
41511         
41512         
41513         this.doc.designMode="on";
41514         this.doc.open();
41515         this.doc.write(this.getDocMarkup());
41516         this.doc.close();
41517         
41518         var dbody = (this.doc.body || this.doc.documentElement);
41519         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41520         // this copies styles from the containing element into thsi one..
41521         // not sure why we need all of this..
41522         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41523         
41524         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41525         //ss['background-attachment'] = 'fixed'; // w3c
41526         dbody.bgProperties = 'fixed'; // ie
41527         //Roo.DomHelper.applyStyles(dbody, ss);
41528         Roo.EventManager.on(this.doc, {
41529             //'mousedown': this.onEditorEvent,
41530             'mouseup': this.onEditorEvent,
41531             'dblclick': this.onEditorEvent,
41532             'click': this.onEditorEvent,
41533             'keyup': this.onEditorEvent,
41534             buffer:100,
41535             scope: this
41536         });
41537         if(Roo.isGecko){
41538             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41539         }
41540         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41541             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41542         }
41543         this.initialized = true;
41544
41545         this.owner.fireEvent('initialize', this);
41546         this.pushValue();
41547     },
41548
41549     // private
41550     onDestroy : function(){
41551         
41552         
41553         
41554         if(this.rendered){
41555             
41556             //for (var i =0; i < this.toolbars.length;i++) {
41557             //    // fixme - ask toolbars for heights?
41558             //    this.toolbars[i].onDestroy();
41559            // }
41560             
41561             //this.wrap.dom.innerHTML = '';
41562             //this.wrap.remove();
41563         }
41564     },
41565
41566     // private
41567     onFirstFocus : function(){
41568         
41569         this.assignDocWin();
41570         
41571         
41572         this.activated = true;
41573          
41574     
41575         if(Roo.isGecko){ // prevent silly gecko errors
41576             this.win.focus();
41577             var s = this.win.getSelection();
41578             if(!s.focusNode || s.focusNode.nodeType != 3){
41579                 var r = s.getRangeAt(0);
41580                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41581                 r.collapse(true);
41582                 this.deferFocus();
41583             }
41584             try{
41585                 this.execCmd('useCSS', true);
41586                 this.execCmd('styleWithCSS', false);
41587             }catch(e){}
41588         }
41589         this.owner.fireEvent('activate', this);
41590     },
41591
41592     // private
41593     adjustFont: function(btn){
41594         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41595         //if(Roo.isSafari){ // safari
41596         //    adjust *= 2;
41597        // }
41598         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41599         if(Roo.isSafari){ // safari
41600             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41601             v =  (v < 10) ? 10 : v;
41602             v =  (v > 48) ? 48 : v;
41603             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41604             
41605         }
41606         
41607         
41608         v = Math.max(1, v+adjust);
41609         
41610         this.execCmd('FontSize', v  );
41611     },
41612
41613     onEditorEvent : function(e)
41614     {
41615         this.owner.fireEvent('editorevent', this, e);
41616       //  this.updateToolbar();
41617         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41618     },
41619
41620     insertTag : function(tg)
41621     {
41622         // could be a bit smarter... -> wrap the current selected tRoo..
41623         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41624             
41625             range = this.createRange(this.getSelection());
41626             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41627             wrappingNode.appendChild(range.extractContents());
41628             range.insertNode(wrappingNode);
41629
41630             return;
41631             
41632             
41633             
41634         }
41635         this.execCmd("formatblock",   tg);
41636         
41637     },
41638     
41639     insertText : function(txt)
41640     {
41641         
41642         
41643         var range = this.createRange();
41644         range.deleteContents();
41645                //alert(Sender.getAttribute('label'));
41646                
41647         range.insertNode(this.doc.createTextNode(txt));
41648     } ,
41649     
41650      
41651
41652     /**
41653      * Executes a Midas editor command on the editor document and performs necessary focus and
41654      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41655      * @param {String} cmd The Midas command
41656      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41657      */
41658     relayCmd : function(cmd, value){
41659         this.win.focus();
41660         this.execCmd(cmd, value);
41661         this.owner.fireEvent('editorevent', this);
41662         //this.updateToolbar();
41663         this.owner.deferFocus();
41664     },
41665
41666     /**
41667      * Executes a Midas editor command directly on the editor document.
41668      * For visual commands, you should use {@link #relayCmd} instead.
41669      * <b>This should only be called after the editor is initialized.</b>
41670      * @param {String} cmd The Midas command
41671      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41672      */
41673     execCmd : function(cmd, value){
41674         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41675         this.syncValue();
41676     },
41677  
41678  
41679    
41680     /**
41681      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41682      * to insert tRoo.
41683      * @param {String} text | dom node.. 
41684      */
41685     insertAtCursor : function(text)
41686     {
41687         
41688         
41689         
41690         if(!this.activated){
41691             return;
41692         }
41693         /*
41694         if(Roo.isIE){
41695             this.win.focus();
41696             var r = this.doc.selection.createRange();
41697             if(r){
41698                 r.collapse(true);
41699                 r.pasteHTML(text);
41700                 this.syncValue();
41701                 this.deferFocus();
41702             
41703             }
41704             return;
41705         }
41706         */
41707         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41708             this.win.focus();
41709             
41710             
41711             // from jquery ui (MIT licenced)
41712             var range, node;
41713             var win = this.win;
41714             
41715             if (win.getSelection && win.getSelection().getRangeAt) {
41716                 range = win.getSelection().getRangeAt(0);
41717                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41718                 range.insertNode(node);
41719             } else if (win.document.selection && win.document.selection.createRange) {
41720                 // no firefox support
41721                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41722                 win.document.selection.createRange().pasteHTML(txt);
41723             } else {
41724                 // no firefox support
41725                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41726                 this.execCmd('InsertHTML', txt);
41727             } 
41728             
41729             this.syncValue();
41730             
41731             this.deferFocus();
41732         }
41733     },
41734  // private
41735     mozKeyPress : function(e){
41736         if(e.ctrlKey){
41737             var c = e.getCharCode(), cmd;
41738           
41739             if(c > 0){
41740                 c = String.fromCharCode(c).toLowerCase();
41741                 switch(c){
41742                     case 'b':
41743                         cmd = 'bold';
41744                         break;
41745                     case 'i':
41746                         cmd = 'italic';
41747                         break;
41748                     
41749                     case 'u':
41750                         cmd = 'underline';
41751                         break;
41752                     
41753                     case 'v':
41754                         this.cleanUpPaste.defer(100, this);
41755                         return;
41756                         
41757                 }
41758                 if(cmd){
41759                     this.win.focus();
41760                     this.execCmd(cmd);
41761                     this.deferFocus();
41762                     e.preventDefault();
41763                 }
41764                 
41765             }
41766         }
41767     },
41768
41769     // private
41770     fixKeys : function(){ // load time branching for fastest keydown performance
41771         if(Roo.isIE){
41772             return function(e){
41773                 var k = e.getKey(), r;
41774                 if(k == e.TAB){
41775                     e.stopEvent();
41776                     r = this.doc.selection.createRange();
41777                     if(r){
41778                         r.collapse(true);
41779                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41780                         this.deferFocus();
41781                     }
41782                     return;
41783                 }
41784                 
41785                 if(k == e.ENTER){
41786                     r = this.doc.selection.createRange();
41787                     if(r){
41788                         var target = r.parentElement();
41789                         if(!target || target.tagName.toLowerCase() != 'li'){
41790                             e.stopEvent();
41791                             r.pasteHTML('<br />');
41792                             r.collapse(false);
41793                             r.select();
41794                         }
41795                     }
41796                 }
41797                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41798                     this.cleanUpPaste.defer(100, this);
41799                     return;
41800                 }
41801                 
41802                 
41803             };
41804         }else if(Roo.isOpera){
41805             return function(e){
41806                 var k = e.getKey();
41807                 if(k == e.TAB){
41808                     e.stopEvent();
41809                     this.win.focus();
41810                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41811                     this.deferFocus();
41812                 }
41813                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41814                     this.cleanUpPaste.defer(100, this);
41815                     return;
41816                 }
41817                 
41818             };
41819         }else if(Roo.isSafari){
41820             return function(e){
41821                 var k = e.getKey();
41822                 
41823                 if(k == e.TAB){
41824                     e.stopEvent();
41825                     this.execCmd('InsertText','\t');
41826                     this.deferFocus();
41827                     return;
41828                 }
41829                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41830                     this.cleanUpPaste.defer(100, this);
41831                     return;
41832                 }
41833                 
41834              };
41835         }
41836     }(),
41837     
41838     getAllAncestors: function()
41839     {
41840         var p = this.getSelectedNode();
41841         var a = [];
41842         if (!p) {
41843             a.push(p); // push blank onto stack..
41844             p = this.getParentElement();
41845         }
41846         
41847         
41848         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41849             a.push(p);
41850             p = p.parentNode;
41851         }
41852         a.push(this.doc.body);
41853         return a;
41854     },
41855     lastSel : false,
41856     lastSelNode : false,
41857     
41858     
41859     getSelection : function() 
41860     {
41861         this.assignDocWin();
41862         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41863     },
41864     
41865     getSelectedNode: function() 
41866     {
41867         // this may only work on Gecko!!!
41868         
41869         // should we cache this!!!!
41870         
41871         
41872         
41873          
41874         var range = this.createRange(this.getSelection()).cloneRange();
41875         
41876         if (Roo.isIE) {
41877             var parent = range.parentElement();
41878             while (true) {
41879                 var testRange = range.duplicate();
41880                 testRange.moveToElementText(parent);
41881                 if (testRange.inRange(range)) {
41882                     break;
41883                 }
41884                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41885                     break;
41886                 }
41887                 parent = parent.parentElement;
41888             }
41889             return parent;
41890         }
41891         
41892         // is ancestor a text element.
41893         var ac =  range.commonAncestorContainer;
41894         if (ac.nodeType == 3) {
41895             ac = ac.parentNode;
41896         }
41897         
41898         var ar = ac.childNodes;
41899          
41900         var nodes = [];
41901         var other_nodes = [];
41902         var has_other_nodes = false;
41903         for (var i=0;i<ar.length;i++) {
41904             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41905                 continue;
41906             }
41907             // fullly contained node.
41908             
41909             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41910                 nodes.push(ar[i]);
41911                 continue;
41912             }
41913             
41914             // probably selected..
41915             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41916                 other_nodes.push(ar[i]);
41917                 continue;
41918             }
41919             // outer..
41920             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41921                 continue;
41922             }
41923             
41924             
41925             has_other_nodes = true;
41926         }
41927         if (!nodes.length && other_nodes.length) {
41928             nodes= other_nodes;
41929         }
41930         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41931             return false;
41932         }
41933         
41934         return nodes[0];
41935     },
41936     createRange: function(sel)
41937     {
41938         // this has strange effects when using with 
41939         // top toolbar - not sure if it's a great idea.
41940         //this.editor.contentWindow.focus();
41941         if (typeof sel != "undefined") {
41942             try {
41943                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41944             } catch(e) {
41945                 return this.doc.createRange();
41946             }
41947         } else {
41948             return this.doc.createRange();
41949         }
41950     },
41951     getParentElement: function()
41952     {
41953         
41954         this.assignDocWin();
41955         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41956         
41957         var range = this.createRange(sel);
41958          
41959         try {
41960             var p = range.commonAncestorContainer;
41961             while (p.nodeType == 3) { // text node
41962                 p = p.parentNode;
41963             }
41964             return p;
41965         } catch (e) {
41966             return null;
41967         }
41968     
41969     },
41970     /***
41971      *
41972      * Range intersection.. the hard stuff...
41973      *  '-1' = before
41974      *  '0' = hits..
41975      *  '1' = after.
41976      *         [ -- selected range --- ]
41977      *   [fail]                        [fail]
41978      *
41979      *    basically..
41980      *      if end is before start or  hits it. fail.
41981      *      if start is after end or hits it fail.
41982      *
41983      *   if either hits (but other is outside. - then it's not 
41984      *   
41985      *    
41986      **/
41987     
41988     
41989     // @see http://www.thismuchiknow.co.uk/?p=64.
41990     rangeIntersectsNode : function(range, node)
41991     {
41992         var nodeRange = node.ownerDocument.createRange();
41993         try {
41994             nodeRange.selectNode(node);
41995         } catch (e) {
41996             nodeRange.selectNodeContents(node);
41997         }
41998     
41999         var rangeStartRange = range.cloneRange();
42000         rangeStartRange.collapse(true);
42001     
42002         var rangeEndRange = range.cloneRange();
42003         rangeEndRange.collapse(false);
42004     
42005         var nodeStartRange = nodeRange.cloneRange();
42006         nodeStartRange.collapse(true);
42007     
42008         var nodeEndRange = nodeRange.cloneRange();
42009         nodeEndRange.collapse(false);
42010     
42011         return rangeStartRange.compareBoundaryPoints(
42012                  Range.START_TO_START, nodeEndRange) == -1 &&
42013                rangeEndRange.compareBoundaryPoints(
42014                  Range.START_TO_START, nodeStartRange) == 1;
42015         
42016          
42017     },
42018     rangeCompareNode : function(range, node)
42019     {
42020         var nodeRange = node.ownerDocument.createRange();
42021         try {
42022             nodeRange.selectNode(node);
42023         } catch (e) {
42024             nodeRange.selectNodeContents(node);
42025         }
42026         
42027         
42028         range.collapse(true);
42029     
42030         nodeRange.collapse(true);
42031      
42032         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42033         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42034          
42035         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42036         
42037         var nodeIsBefore   =  ss == 1;
42038         var nodeIsAfter    = ee == -1;
42039         
42040         if (nodeIsBefore && nodeIsAfter)
42041             return 0; // outer
42042         if (!nodeIsBefore && nodeIsAfter)
42043             return 1; //right trailed.
42044         
42045         if (nodeIsBefore && !nodeIsAfter)
42046             return 2;  // left trailed.
42047         // fully contined.
42048         return 3;
42049     },
42050
42051     // private? - in a new class?
42052     cleanUpPaste :  function()
42053     {
42054         // cleans up the whole document..
42055         Roo.log('cleanuppaste');
42056         
42057         this.cleanUpChildren(this.doc.body);
42058         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42059         if (clean != this.doc.body.innerHTML) {
42060             this.doc.body.innerHTML = clean;
42061         }
42062         
42063     },
42064     
42065     cleanWordChars : function(input) {// change the chars to hex code
42066         var he = Roo.HtmlEditorCore;
42067         
42068         var output = input;
42069         Roo.each(he.swapCodes, function(sw) { 
42070             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42071             
42072             output = output.replace(swapper, sw[1]);
42073         });
42074         
42075         return output;
42076     },
42077     
42078     
42079     cleanUpChildren : function (n)
42080     {
42081         if (!n.childNodes.length) {
42082             return;
42083         }
42084         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42085            this.cleanUpChild(n.childNodes[i]);
42086         }
42087     },
42088     
42089     
42090         
42091     
42092     cleanUpChild : function (node)
42093     {
42094         var ed = this;
42095         //console.log(node);
42096         if (node.nodeName == "#text") {
42097             // clean up silly Windows -- stuff?
42098             return; 
42099         }
42100         if (node.nodeName == "#comment") {
42101             node.parentNode.removeChild(node);
42102             // clean up silly Windows -- stuff?
42103             return; 
42104         }
42105         var lcname = node.tagName.toLowerCase();
42106         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42107         // whitelist of tags..
42108         
42109         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42110             // remove node.
42111             node.parentNode.removeChild(node);
42112             return;
42113             
42114         }
42115         
42116         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42117         
42118         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42119         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42120         
42121         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42122         //    remove_keep_children = true;
42123         //}
42124         
42125         if (remove_keep_children) {
42126             this.cleanUpChildren(node);
42127             // inserts everything just before this node...
42128             while (node.childNodes.length) {
42129                 var cn = node.childNodes[0];
42130                 node.removeChild(cn);
42131                 node.parentNode.insertBefore(cn, node);
42132             }
42133             node.parentNode.removeChild(node);
42134             return;
42135         }
42136         
42137         if (!node.attributes || !node.attributes.length) {
42138             this.cleanUpChildren(node);
42139             return;
42140         }
42141         
42142         function cleanAttr(n,v)
42143         {
42144             
42145             if (v.match(/^\./) || v.match(/^\//)) {
42146                 return;
42147             }
42148             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42149                 return;
42150             }
42151             if (v.match(/^#/)) {
42152                 return;
42153             }
42154 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42155             node.removeAttribute(n);
42156             
42157         }
42158         
42159         var cwhite = this.cwhite;
42160         var cblack = this.cblack;
42161             
42162         function cleanStyle(n,v)
42163         {
42164             if (v.match(/expression/)) { //XSS?? should we even bother..
42165                 node.removeAttribute(n);
42166                 return;
42167             }
42168             
42169             var parts = v.split(/;/);
42170             var clean = [];
42171             
42172             Roo.each(parts, function(p) {
42173                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42174                 if (!p.length) {
42175                     return true;
42176                 }
42177                 var l = p.split(':').shift().replace(/\s+/g,'');
42178                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42179                 
42180                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42181 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42182                     //node.removeAttribute(n);
42183                     return true;
42184                 }
42185                 //Roo.log()
42186                 // only allow 'c whitelisted system attributes'
42187                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42188 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42189                     //node.removeAttribute(n);
42190                     return true;
42191                 }
42192                 
42193                 
42194                  
42195                 
42196                 clean.push(p);
42197                 return true;
42198             });
42199             if (clean.length) { 
42200                 node.setAttribute(n, clean.join(';'));
42201             } else {
42202                 node.removeAttribute(n);
42203             }
42204             
42205         }
42206         
42207         
42208         for (var i = node.attributes.length-1; i > -1 ; i--) {
42209             var a = node.attributes[i];
42210             //console.log(a);
42211             
42212             if (a.name.toLowerCase().substr(0,2)=='on')  {
42213                 node.removeAttribute(a.name);
42214                 continue;
42215             }
42216             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42217                 node.removeAttribute(a.name);
42218                 continue;
42219             }
42220             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42221                 cleanAttr(a.name,a.value); // fixme..
42222                 continue;
42223             }
42224             if (a.name == 'style') {
42225                 cleanStyle(a.name,a.value);
42226                 continue;
42227             }
42228             /// clean up MS crap..
42229             // tecnically this should be a list of valid class'es..
42230             
42231             
42232             if (a.name == 'class') {
42233                 if (a.value.match(/^Mso/)) {
42234                     node.className = '';
42235                 }
42236                 
42237                 if (a.value.match(/body/)) {
42238                     node.className = '';
42239                 }
42240                 continue;
42241             }
42242             
42243             // style cleanup!?
42244             // class cleanup?
42245             
42246         }
42247         
42248         
42249         this.cleanUpChildren(node);
42250         
42251         
42252     },
42253     
42254     /**
42255      * Clean up MS wordisms...
42256      */
42257     cleanWord : function(node)
42258     {
42259         
42260         
42261         if (!node) {
42262             this.cleanWord(this.doc.body);
42263             return;
42264         }
42265         if (node.nodeName == "#text") {
42266             // clean up silly Windows -- stuff?
42267             return; 
42268         }
42269         if (node.nodeName == "#comment") {
42270             node.parentNode.removeChild(node);
42271             // clean up silly Windows -- stuff?
42272             return; 
42273         }
42274         
42275         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42276             node.parentNode.removeChild(node);
42277             return;
42278         }
42279         
42280         // remove - but keep children..
42281         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42282             while (node.childNodes.length) {
42283                 var cn = node.childNodes[0];
42284                 node.removeChild(cn);
42285                 node.parentNode.insertBefore(cn, node);
42286             }
42287             node.parentNode.removeChild(node);
42288             this.iterateChildren(node, this.cleanWord);
42289             return;
42290         }
42291         // clean styles
42292         if (node.className.length) {
42293             
42294             var cn = node.className.split(/\W+/);
42295             var cna = [];
42296             Roo.each(cn, function(cls) {
42297                 if (cls.match(/Mso[a-zA-Z]+/)) {
42298                     return;
42299                 }
42300                 cna.push(cls);
42301             });
42302             node.className = cna.length ? cna.join(' ') : '';
42303             if (!cna.length) {
42304                 node.removeAttribute("class");
42305             }
42306         }
42307         
42308         if (node.hasAttribute("lang")) {
42309             node.removeAttribute("lang");
42310         }
42311         
42312         if (node.hasAttribute("style")) {
42313             
42314             var styles = node.getAttribute("style").split(";");
42315             var nstyle = [];
42316             Roo.each(styles, function(s) {
42317                 if (!s.match(/:/)) {
42318                     return;
42319                 }
42320                 var kv = s.split(":");
42321                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42322                     return;
42323                 }
42324                 // what ever is left... we allow.
42325                 nstyle.push(s);
42326             });
42327             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42328             if (!nstyle.length) {
42329                 node.removeAttribute('style');
42330             }
42331         }
42332         this.iterateChildren(node, this.cleanWord);
42333         
42334         
42335         
42336     },
42337     /**
42338      * iterateChildren of a Node, calling fn each time, using this as the scole..
42339      * @param {DomNode} node node to iterate children of.
42340      * @param {Function} fn method of this class to call on each item.
42341      */
42342     iterateChildren : function(node, fn)
42343     {
42344         if (!node.childNodes.length) {
42345                 return;
42346         }
42347         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42348            fn.call(this, node.childNodes[i])
42349         }
42350     },
42351     
42352     
42353     /**
42354      * cleanTableWidths.
42355      *
42356      * Quite often pasting from word etc.. results in tables with column and widths.
42357      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42358      *
42359      */
42360     cleanTableWidths : function(node)
42361     {
42362          
42363          
42364         if (!node) {
42365             this.cleanTableWidths(this.doc.body);
42366             return;
42367         }
42368         
42369         // ignore list...
42370         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42371             return; 
42372         }
42373         Roo.log(node.tagName);
42374         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42375             this.iterateChildren(node, this.cleanTableWidths);
42376             return;
42377         }
42378         if (node.hasAttribute('width')) {
42379             node.removeAttribute('width');
42380         }
42381         
42382          
42383         if (node.hasAttribute("style")) {
42384             // pretty basic...
42385             
42386             var styles = node.getAttribute("style").split(";");
42387             var nstyle = [];
42388             Roo.each(styles, function(s) {
42389                 if (!s.match(/:/)) {
42390                     return;
42391                 }
42392                 var kv = s.split(":");
42393                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42394                     return;
42395                 }
42396                 // what ever is left... we allow.
42397                 nstyle.push(s);
42398             });
42399             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42400             if (!nstyle.length) {
42401                 node.removeAttribute('style');
42402             }
42403         }
42404         
42405         this.iterateChildren(node, this.cleanTableWidths);
42406         
42407         
42408     },
42409     
42410     
42411     
42412     
42413     domToHTML : function(currentElement, depth, nopadtext) {
42414         
42415         depth = depth || 0;
42416         nopadtext = nopadtext || false;
42417     
42418         if (!currentElement) {
42419             return this.domToHTML(this.doc.body);
42420         }
42421         
42422         //Roo.log(currentElement);
42423         var j;
42424         var allText = false;
42425         var nodeName = currentElement.nodeName;
42426         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42427         
42428         if  (nodeName == '#text') {
42429             
42430             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42431         }
42432         
42433         
42434         var ret = '';
42435         if (nodeName != 'BODY') {
42436              
42437             var i = 0;
42438             // Prints the node tagName, such as <A>, <IMG>, etc
42439             if (tagName) {
42440                 var attr = [];
42441                 for(i = 0; i < currentElement.attributes.length;i++) {
42442                     // quoting?
42443                     var aname = currentElement.attributes.item(i).name;
42444                     if (!currentElement.attributes.item(i).value.length) {
42445                         continue;
42446                     }
42447                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42448                 }
42449                 
42450                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42451             } 
42452             else {
42453                 
42454                 // eack
42455             }
42456         } else {
42457             tagName = false;
42458         }
42459         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42460             return ret;
42461         }
42462         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42463             nopadtext = true;
42464         }
42465         
42466         
42467         // Traverse the tree
42468         i = 0;
42469         var currentElementChild = currentElement.childNodes.item(i);
42470         var allText = true;
42471         var innerHTML  = '';
42472         lastnode = '';
42473         while (currentElementChild) {
42474             // Formatting code (indent the tree so it looks nice on the screen)
42475             var nopad = nopadtext;
42476             if (lastnode == 'SPAN') {
42477                 nopad  = true;
42478             }
42479             // text
42480             if  (currentElementChild.nodeName == '#text') {
42481                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42482                 toadd = nopadtext ? toadd : toadd.trim();
42483                 if (!nopad && toadd.length > 80) {
42484                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42485                 }
42486                 innerHTML  += toadd;
42487                 
42488                 i++;
42489                 currentElementChild = currentElement.childNodes.item(i);
42490                 lastNode = '';
42491                 continue;
42492             }
42493             allText = false;
42494             
42495             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42496                 
42497             // Recursively traverse the tree structure of the child node
42498             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42499             lastnode = currentElementChild.nodeName;
42500             i++;
42501             currentElementChild=currentElement.childNodes.item(i);
42502         }
42503         
42504         ret += innerHTML;
42505         
42506         if (!allText) {
42507                 // The remaining code is mostly for formatting the tree
42508             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42509         }
42510         
42511         
42512         if (tagName) {
42513             ret+= "</"+tagName+">";
42514         }
42515         return ret;
42516         
42517     },
42518         
42519     applyBlacklists : function()
42520     {
42521         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42522         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42523         
42524         this.white = [];
42525         this.black = [];
42526         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42527             if (b.indexOf(tag) > -1) {
42528                 return;
42529             }
42530             this.white.push(tag);
42531             
42532         }, this);
42533         
42534         Roo.each(w, function(tag) {
42535             if (b.indexOf(tag) > -1) {
42536                 return;
42537             }
42538             if (this.white.indexOf(tag) > -1) {
42539                 return;
42540             }
42541             this.white.push(tag);
42542             
42543         }, this);
42544         
42545         
42546         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42547             if (w.indexOf(tag) > -1) {
42548                 return;
42549             }
42550             this.black.push(tag);
42551             
42552         }, this);
42553         
42554         Roo.each(b, function(tag) {
42555             if (w.indexOf(tag) > -1) {
42556                 return;
42557             }
42558             if (this.black.indexOf(tag) > -1) {
42559                 return;
42560             }
42561             this.black.push(tag);
42562             
42563         }, this);
42564         
42565         
42566         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42567         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42568         
42569         this.cwhite = [];
42570         this.cblack = [];
42571         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42572             if (b.indexOf(tag) > -1) {
42573                 return;
42574             }
42575             this.cwhite.push(tag);
42576             
42577         }, this);
42578         
42579         Roo.each(w, function(tag) {
42580             if (b.indexOf(tag) > -1) {
42581                 return;
42582             }
42583             if (this.cwhite.indexOf(tag) > -1) {
42584                 return;
42585             }
42586             this.cwhite.push(tag);
42587             
42588         }, this);
42589         
42590         
42591         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42592             if (w.indexOf(tag) > -1) {
42593                 return;
42594             }
42595             this.cblack.push(tag);
42596             
42597         }, this);
42598         
42599         Roo.each(b, function(tag) {
42600             if (w.indexOf(tag) > -1) {
42601                 return;
42602             }
42603             if (this.cblack.indexOf(tag) > -1) {
42604                 return;
42605             }
42606             this.cblack.push(tag);
42607             
42608         }, this);
42609     },
42610     
42611     setStylesheets : function(stylesheets)
42612     {
42613         if(typeof(stylesheets) == 'string'){
42614             Roo.get(this.iframe.contentDocument.head).createChild({
42615                 tag : 'link',
42616                 rel : 'stylesheet',
42617                 type : 'text/css',
42618                 href : stylesheets
42619             });
42620             
42621             return;
42622         }
42623         var _this = this;
42624      
42625         Roo.each(stylesheets, function(s) {
42626             if(!s.length){
42627                 return;
42628             }
42629             
42630             Roo.get(_this.iframe.contentDocument.head).createChild({
42631                 tag : 'link',
42632                 rel : 'stylesheet',
42633                 type : 'text/css',
42634                 href : s
42635             });
42636         });
42637
42638         
42639     },
42640     
42641     removeStylesheets : function()
42642     {
42643         var _this = this;
42644         
42645         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42646             s.remove();
42647         });
42648     }
42649     
42650     // hide stuff that is not compatible
42651     /**
42652      * @event blur
42653      * @hide
42654      */
42655     /**
42656      * @event change
42657      * @hide
42658      */
42659     /**
42660      * @event focus
42661      * @hide
42662      */
42663     /**
42664      * @event specialkey
42665      * @hide
42666      */
42667     /**
42668      * @cfg {String} fieldClass @hide
42669      */
42670     /**
42671      * @cfg {String} focusClass @hide
42672      */
42673     /**
42674      * @cfg {String} autoCreate @hide
42675      */
42676     /**
42677      * @cfg {String} inputType @hide
42678      */
42679     /**
42680      * @cfg {String} invalidClass @hide
42681      */
42682     /**
42683      * @cfg {String} invalidText @hide
42684      */
42685     /**
42686      * @cfg {String} msgFx @hide
42687      */
42688     /**
42689      * @cfg {String} validateOnBlur @hide
42690      */
42691 });
42692
42693 Roo.HtmlEditorCore.white = [
42694         'area', 'br', 'img', 'input', 'hr', 'wbr',
42695         
42696        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42697        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42698        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42699        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42700        'table',   'ul',         'xmp', 
42701        
42702        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42703       'thead',   'tr', 
42704      
42705       'dir', 'menu', 'ol', 'ul', 'dl',
42706        
42707       'embed',  'object'
42708 ];
42709
42710
42711 Roo.HtmlEditorCore.black = [
42712     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42713         'applet', // 
42714         'base',   'basefont', 'bgsound', 'blink',  'body', 
42715         'frame',  'frameset', 'head',    'html',   'ilayer', 
42716         'iframe', 'layer',  'link',     'meta',    'object',   
42717         'script', 'style' ,'title',  'xml' // clean later..
42718 ];
42719 Roo.HtmlEditorCore.clean = [
42720     'script', 'style', 'title', 'xml'
42721 ];
42722 Roo.HtmlEditorCore.remove = [
42723     'font'
42724 ];
42725 // attributes..
42726
42727 Roo.HtmlEditorCore.ablack = [
42728     'on'
42729 ];
42730     
42731 Roo.HtmlEditorCore.aclean = [ 
42732     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42733 ];
42734
42735 // protocols..
42736 Roo.HtmlEditorCore.pwhite= [
42737         'http',  'https',  'mailto'
42738 ];
42739
42740 // white listed style attributes.
42741 Roo.HtmlEditorCore.cwhite= [
42742       //  'text-align', /// default is to allow most things..
42743       
42744          
42745 //        'font-size'//??
42746 ];
42747
42748 // black listed style attributes.
42749 Roo.HtmlEditorCore.cblack= [
42750       //  'font-size' -- this can be set by the project 
42751 ];
42752
42753
42754 Roo.HtmlEditorCore.swapCodes   =[ 
42755     [    8211, "--" ], 
42756     [    8212, "--" ], 
42757     [    8216,  "'" ],  
42758     [    8217, "'" ],  
42759     [    8220, '"' ],  
42760     [    8221, '"' ],  
42761     [    8226, "*" ],  
42762     [    8230, "..." ]
42763 ]; 
42764
42765     //<script type="text/javascript">
42766
42767 /*
42768  * Ext JS Library 1.1.1
42769  * Copyright(c) 2006-2007, Ext JS, LLC.
42770  * Licence LGPL
42771  * 
42772  */
42773  
42774  
42775 Roo.form.HtmlEditor = function(config){
42776     
42777     
42778     
42779     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42780     
42781     if (!this.toolbars) {
42782         this.toolbars = [];
42783     }
42784     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42785     
42786     
42787 };
42788
42789 /**
42790  * @class Roo.form.HtmlEditor
42791  * @extends Roo.form.Field
42792  * Provides a lightweight HTML Editor component.
42793  *
42794  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42795  * 
42796  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42797  * supported by this editor.</b><br/><br/>
42798  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42799  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42800  */
42801 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42802     /**
42803      * @cfg {Boolean} clearUp
42804      */
42805     clearUp : true,
42806       /**
42807      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42808      */
42809     toolbars : false,
42810    
42811      /**
42812      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42813      *                        Roo.resizable.
42814      */
42815     resizable : false,
42816      /**
42817      * @cfg {Number} height (in pixels)
42818      */   
42819     height: 300,
42820    /**
42821      * @cfg {Number} width (in pixels)
42822      */   
42823     width: 500,
42824     
42825     /**
42826      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42827      * 
42828      */
42829     stylesheets: false,
42830     
42831     
42832      /**
42833      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42834      * 
42835      */
42836     cblack: false,
42837     /**
42838      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42839      * 
42840      */
42841     cwhite: false,
42842     
42843      /**
42844      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42845      * 
42846      */
42847     black: false,
42848     /**
42849      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42850      * 
42851      */
42852     white: false,
42853     
42854     // id of frame..
42855     frameId: false,
42856     
42857     // private properties
42858     validationEvent : false,
42859     deferHeight: true,
42860     initialized : false,
42861     activated : false,
42862     
42863     onFocus : Roo.emptyFn,
42864     iframePad:3,
42865     hideMode:'offsets',
42866     
42867     actionMode : 'container', // defaults to hiding it...
42868     
42869     defaultAutoCreate : { // modified by initCompnoent..
42870         tag: "textarea",
42871         style:"width:500px;height:300px;",
42872         autocomplete: "new-password"
42873     },
42874
42875     // private
42876     initComponent : function(){
42877         this.addEvents({
42878             /**
42879              * @event initialize
42880              * Fires when the editor is fully initialized (including the iframe)
42881              * @param {HtmlEditor} this
42882              */
42883             initialize: true,
42884             /**
42885              * @event activate
42886              * Fires when the editor is first receives the focus. Any insertion must wait
42887              * until after this event.
42888              * @param {HtmlEditor} this
42889              */
42890             activate: true,
42891              /**
42892              * @event beforesync
42893              * Fires before the textarea is updated with content from the editor iframe. Return false
42894              * to cancel the sync.
42895              * @param {HtmlEditor} this
42896              * @param {String} html
42897              */
42898             beforesync: true,
42899              /**
42900              * @event beforepush
42901              * Fires before the iframe editor is updated with content from the textarea. Return false
42902              * to cancel the push.
42903              * @param {HtmlEditor} this
42904              * @param {String} html
42905              */
42906             beforepush: true,
42907              /**
42908              * @event sync
42909              * Fires when the textarea is updated with content from the editor iframe.
42910              * @param {HtmlEditor} this
42911              * @param {String} html
42912              */
42913             sync: true,
42914              /**
42915              * @event push
42916              * Fires when the iframe editor is updated with content from the textarea.
42917              * @param {HtmlEditor} this
42918              * @param {String} html
42919              */
42920             push: true,
42921              /**
42922              * @event editmodechange
42923              * Fires when the editor switches edit modes
42924              * @param {HtmlEditor} this
42925              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42926              */
42927             editmodechange: true,
42928             /**
42929              * @event editorevent
42930              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42931              * @param {HtmlEditor} this
42932              */
42933             editorevent: true,
42934             /**
42935              * @event firstfocus
42936              * Fires when on first focus - needed by toolbars..
42937              * @param {HtmlEditor} this
42938              */
42939             firstfocus: true,
42940             /**
42941              * @event autosave
42942              * Auto save the htmlEditor value as a file into Events
42943              * @param {HtmlEditor} this
42944              */
42945             autosave: true,
42946             /**
42947              * @event savedpreview
42948              * preview the saved version of htmlEditor
42949              * @param {HtmlEditor} this
42950              */
42951             savedpreview: true,
42952             
42953             /**
42954             * @event stylesheetsclick
42955             * Fires when press the Sytlesheets button
42956             * @param {Roo.HtmlEditorCore} this
42957             */
42958             stylesheetsclick: true
42959         });
42960         this.defaultAutoCreate =  {
42961             tag: "textarea",
42962             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42963             autocomplete: "new-password"
42964         };
42965     },
42966
42967     /**
42968      * Protected method that will not generally be called directly. It
42969      * is called when the editor creates its toolbar. Override this method if you need to
42970      * add custom toolbar buttons.
42971      * @param {HtmlEditor} editor
42972      */
42973     createToolbar : function(editor){
42974         Roo.log("create toolbars");
42975         if (!editor.toolbars || !editor.toolbars.length) {
42976             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42977         }
42978         
42979         for (var i =0 ; i < editor.toolbars.length;i++) {
42980             editor.toolbars[i] = Roo.factory(
42981                     typeof(editor.toolbars[i]) == 'string' ?
42982                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42983                 Roo.form.HtmlEditor);
42984             editor.toolbars[i].init(editor);
42985         }
42986          
42987         
42988     },
42989
42990      
42991     // private
42992     onRender : function(ct, position)
42993     {
42994         var _t = this;
42995         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42996         
42997         this.wrap = this.el.wrap({
42998             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42999         });
43000         
43001         this.editorcore.onRender(ct, position);
43002          
43003         if (this.resizable) {
43004             this.resizeEl = new Roo.Resizable(this.wrap, {
43005                 pinned : true,
43006                 wrap: true,
43007                 dynamic : true,
43008                 minHeight : this.height,
43009                 height: this.height,
43010                 handles : this.resizable,
43011                 width: this.width,
43012                 listeners : {
43013                     resize : function(r, w, h) {
43014                         _t.onResize(w,h); // -something
43015                     }
43016                 }
43017             });
43018             
43019         }
43020         this.createToolbar(this);
43021        
43022         
43023         if(!this.width){
43024             this.setSize(this.wrap.getSize());
43025         }
43026         if (this.resizeEl) {
43027             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43028             // should trigger onReize..
43029         }
43030         
43031         this.keyNav = new Roo.KeyNav(this.el, {
43032             
43033             "tab" : function(e){
43034                 e.preventDefault();
43035                 
43036                 var value = this.getValue();
43037                 
43038                 var start = this.el.dom.selectionStart;
43039                 var end = this.el.dom.selectionEnd;
43040                 
43041                 if(!e.shiftKey){
43042                     
43043                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43044                     this.el.dom.setSelectionRange(end + 1, end + 1);
43045                     return;
43046                 }
43047                 
43048                 var f = value.substring(0, start).split("\t");
43049                 
43050                 if(f.pop().length != 0){
43051                     return;
43052                 }
43053                 
43054                 this.setValue(f.join("\t") + value.substring(end));
43055                 this.el.dom.setSelectionRange(start - 1, start - 1);
43056                 
43057             },
43058             
43059             "home" : function(e){
43060                 e.preventDefault();
43061                 
43062                 var curr = this.el.dom.selectionStart;
43063                 var lines = this.getValue().split("\n");
43064                 
43065                 if(!lines.length){
43066                     return;
43067                 }
43068                 
43069                 if(e.ctrlKey){
43070                     this.el.dom.setSelectionRange(0, 0);
43071                     return;
43072                 }
43073                 
43074                 var pos = 0;
43075                 
43076                 for (var i = 0; i < lines.length;i++) {
43077                     pos += lines[i].length;
43078                     
43079                     if(i != 0){
43080                         pos += 1;
43081                     }
43082                     
43083                     if(pos < curr){
43084                         continue;
43085                     }
43086                     
43087                     pos -= lines[i].length;
43088                     
43089                     break;
43090                 }
43091                 
43092                 if(!e.shiftKey){
43093                     this.el.dom.setSelectionRange(pos, pos);
43094                     return;
43095                 }
43096                 
43097                 this.el.dom.selectionStart = pos;
43098                 this.el.dom.selectionEnd = curr;
43099             },
43100             
43101             "end" : function(e){
43102                 e.preventDefault();
43103                 
43104                 var curr = this.el.dom.selectionStart;
43105                 var lines = this.getValue().split("\n");
43106                 
43107                 if(!lines.length){
43108                     return;
43109                 }
43110                 
43111                 if(e.ctrlKey){
43112                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43113                     return;
43114                 }
43115                 
43116                 var pos = 0;
43117                 
43118                 for (var i = 0; i < lines.length;i++) {
43119                     
43120                     pos += lines[i].length;
43121                     
43122                     if(i != 0){
43123                         pos += 1;
43124                     }
43125                     
43126                     if(pos < curr){
43127                         continue;
43128                     }
43129                     
43130                     break;
43131                 }
43132                 
43133                 if(!e.shiftKey){
43134                     this.el.dom.setSelectionRange(pos, pos);
43135                     return;
43136                 }
43137                 
43138                 this.el.dom.selectionStart = curr;
43139                 this.el.dom.selectionEnd = pos;
43140             },
43141
43142             scope : this,
43143
43144             doRelay : function(foo, bar, hname){
43145                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43146             },
43147
43148             forceKeyDown: true
43149         });
43150         
43151 //        if(this.autosave && this.w){
43152 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43153 //        }
43154     },
43155
43156     // private
43157     onResize : function(w, h)
43158     {
43159         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43160         var ew = false;
43161         var eh = false;
43162         
43163         if(this.el ){
43164             if(typeof w == 'number'){
43165                 var aw = w - this.wrap.getFrameWidth('lr');
43166                 this.el.setWidth(this.adjustWidth('textarea', aw));
43167                 ew = aw;
43168             }
43169             if(typeof h == 'number'){
43170                 var tbh = 0;
43171                 for (var i =0; i < this.toolbars.length;i++) {
43172                     // fixme - ask toolbars for heights?
43173                     tbh += this.toolbars[i].tb.el.getHeight();
43174                     if (this.toolbars[i].footer) {
43175                         tbh += this.toolbars[i].footer.el.getHeight();
43176                     }
43177                 }
43178                 
43179                 
43180                 
43181                 
43182                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43183                 ah -= 5; // knock a few pixes off for look..
43184 //                Roo.log(ah);
43185                 this.el.setHeight(this.adjustWidth('textarea', ah));
43186                 var eh = ah;
43187             }
43188         }
43189         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43190         this.editorcore.onResize(ew,eh);
43191         
43192     },
43193
43194     /**
43195      * Toggles the editor between standard and source edit mode.
43196      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43197      */
43198     toggleSourceEdit : function(sourceEditMode)
43199     {
43200         this.editorcore.toggleSourceEdit(sourceEditMode);
43201         
43202         if(this.editorcore.sourceEditMode){
43203             Roo.log('editor - showing textarea');
43204             
43205 //            Roo.log('in');
43206 //            Roo.log(this.syncValue());
43207             this.editorcore.syncValue();
43208             this.el.removeClass('x-hidden');
43209             this.el.dom.removeAttribute('tabIndex');
43210             this.el.focus();
43211             
43212             for (var i = 0; i < this.toolbars.length; i++) {
43213                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43214                     this.toolbars[i].tb.hide();
43215                     this.toolbars[i].footer.hide();
43216                 }
43217             }
43218             
43219         }else{
43220             Roo.log('editor - hiding textarea');
43221 //            Roo.log('out')
43222 //            Roo.log(this.pushValue()); 
43223             this.editorcore.pushValue();
43224             
43225             this.el.addClass('x-hidden');
43226             this.el.dom.setAttribute('tabIndex', -1);
43227             
43228             for (var i = 0; i < this.toolbars.length; i++) {
43229                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43230                     this.toolbars[i].tb.show();
43231                     this.toolbars[i].footer.show();
43232                 }
43233             }
43234             
43235             //this.deferFocus();
43236         }
43237         
43238         this.setSize(this.wrap.getSize());
43239         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43240         
43241         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43242     },
43243  
43244     // private (for BoxComponent)
43245     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43246
43247     // private (for BoxComponent)
43248     getResizeEl : function(){
43249         return this.wrap;
43250     },
43251
43252     // private (for BoxComponent)
43253     getPositionEl : function(){
43254         return this.wrap;
43255     },
43256
43257     // private
43258     initEvents : function(){
43259         this.originalValue = this.getValue();
43260     },
43261
43262     /**
43263      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43264      * @method
43265      */
43266     markInvalid : Roo.emptyFn,
43267     /**
43268      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43269      * @method
43270      */
43271     clearInvalid : Roo.emptyFn,
43272
43273     setValue : function(v){
43274         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43275         this.editorcore.pushValue();
43276     },
43277
43278      
43279     // private
43280     deferFocus : function(){
43281         this.focus.defer(10, this);
43282     },
43283
43284     // doc'ed in Field
43285     focus : function(){
43286         this.editorcore.focus();
43287         
43288     },
43289       
43290
43291     // private
43292     onDestroy : function(){
43293         
43294         
43295         
43296         if(this.rendered){
43297             
43298             for (var i =0; i < this.toolbars.length;i++) {
43299                 // fixme - ask toolbars for heights?
43300                 this.toolbars[i].onDestroy();
43301             }
43302             
43303             this.wrap.dom.innerHTML = '';
43304             this.wrap.remove();
43305         }
43306     },
43307
43308     // private
43309     onFirstFocus : function(){
43310         //Roo.log("onFirstFocus");
43311         this.editorcore.onFirstFocus();
43312          for (var i =0; i < this.toolbars.length;i++) {
43313             this.toolbars[i].onFirstFocus();
43314         }
43315         
43316     },
43317     
43318     // private
43319     syncValue : function()
43320     {
43321         this.editorcore.syncValue();
43322     },
43323     
43324     pushValue : function()
43325     {
43326         this.editorcore.pushValue();
43327     },
43328     
43329     setStylesheets : function(stylesheets)
43330     {
43331         this.editorcore.setStylesheets(stylesheets);
43332     },
43333     
43334     removeStylesheets : function()
43335     {
43336         this.editorcore.removeStylesheets();
43337     }
43338      
43339     
43340     // hide stuff that is not compatible
43341     /**
43342      * @event blur
43343      * @hide
43344      */
43345     /**
43346      * @event change
43347      * @hide
43348      */
43349     /**
43350      * @event focus
43351      * @hide
43352      */
43353     /**
43354      * @event specialkey
43355      * @hide
43356      */
43357     /**
43358      * @cfg {String} fieldClass @hide
43359      */
43360     /**
43361      * @cfg {String} focusClass @hide
43362      */
43363     /**
43364      * @cfg {String} autoCreate @hide
43365      */
43366     /**
43367      * @cfg {String} inputType @hide
43368      */
43369     /**
43370      * @cfg {String} invalidClass @hide
43371      */
43372     /**
43373      * @cfg {String} invalidText @hide
43374      */
43375     /**
43376      * @cfg {String} msgFx @hide
43377      */
43378     /**
43379      * @cfg {String} validateOnBlur @hide
43380      */
43381 });
43382  
43383     // <script type="text/javascript">
43384 /*
43385  * Based on
43386  * Ext JS Library 1.1.1
43387  * Copyright(c) 2006-2007, Ext JS, LLC.
43388  *  
43389  
43390  */
43391
43392 /**
43393  * @class Roo.form.HtmlEditorToolbar1
43394  * Basic Toolbar
43395  * 
43396  * Usage:
43397  *
43398  new Roo.form.HtmlEditor({
43399     ....
43400     toolbars : [
43401         new Roo.form.HtmlEditorToolbar1({
43402             disable : { fonts: 1 , format: 1, ..., ... , ...],
43403             btns : [ .... ]
43404         })
43405     }
43406      
43407  * 
43408  * @cfg {Object} disable List of elements to disable..
43409  * @cfg {Array} btns List of additional buttons.
43410  * 
43411  * 
43412  * NEEDS Extra CSS? 
43413  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43414  */
43415  
43416 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43417 {
43418     
43419     Roo.apply(this, config);
43420     
43421     // default disabled, based on 'good practice'..
43422     this.disable = this.disable || {};
43423     Roo.applyIf(this.disable, {
43424         fontSize : true,
43425         colors : true,
43426         specialElements : true
43427     });
43428     
43429     
43430     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43431     // dont call parent... till later.
43432 }
43433
43434 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43435     
43436     tb: false,
43437     
43438     rendered: false,
43439     
43440     editor : false,
43441     editorcore : false,
43442     /**
43443      * @cfg {Object} disable  List of toolbar elements to disable
43444          
43445      */
43446     disable : false,
43447     
43448     
43449      /**
43450      * @cfg {String} createLinkText The default text for the create link prompt
43451      */
43452     createLinkText : 'Please enter the URL for the link:',
43453     /**
43454      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43455      */
43456     defaultLinkValue : 'http:/'+'/',
43457    
43458     
43459       /**
43460      * @cfg {Array} fontFamilies An array of available font families
43461      */
43462     fontFamilies : [
43463         'Arial',
43464         'Courier New',
43465         'Tahoma',
43466         'Times New Roman',
43467         'Verdana'
43468     ],
43469     
43470     specialChars : [
43471            "&#169;",
43472           "&#174;",     
43473           "&#8482;",    
43474           "&#163;" ,    
43475          // "&#8212;",    
43476           "&#8230;",    
43477           "&#247;" ,    
43478         //  "&#225;" ,     ?? a acute?
43479            "&#8364;"    , //Euro
43480        //   "&#8220;"    ,
43481         //  "&#8221;"    ,
43482         //  "&#8226;"    ,
43483           "&#176;"  //   , // degrees
43484
43485          // "&#233;"     , // e ecute
43486          // "&#250;"     , // u ecute?
43487     ],
43488     
43489     specialElements : [
43490         {
43491             text: "Insert Table",
43492             xtype: 'MenuItem',
43493             xns : Roo.Menu,
43494             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43495                 
43496         },
43497         {    
43498             text: "Insert Image",
43499             xtype: 'MenuItem',
43500             xns : Roo.Menu,
43501             ihtml : '<img src="about:blank"/>'
43502             
43503         }
43504         
43505          
43506     ],
43507     
43508     
43509     inputElements : [ 
43510             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43511             "input:submit", "input:button", "select", "textarea", "label" ],
43512     formats : [
43513         ["p"] ,  
43514         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43515         ["pre"],[ "code"], 
43516         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43517         ['div'],['span']
43518     ],
43519     
43520     cleanStyles : [
43521         "font-size"
43522     ],
43523      /**
43524      * @cfg {String} defaultFont default font to use.
43525      */
43526     defaultFont: 'tahoma',
43527    
43528     fontSelect : false,
43529     
43530     
43531     formatCombo : false,
43532     
43533     init : function(editor)
43534     {
43535         this.editor = editor;
43536         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43537         var editorcore = this.editorcore;
43538         
43539         var _t = this;
43540         
43541         var fid = editorcore.frameId;
43542         var etb = this;
43543         function btn(id, toggle, handler){
43544             var xid = fid + '-'+ id ;
43545             return {
43546                 id : xid,
43547                 cmd : id,
43548                 cls : 'x-btn-icon x-edit-'+id,
43549                 enableToggle:toggle !== false,
43550                 scope: _t, // was editor...
43551                 handler:handler||_t.relayBtnCmd,
43552                 clickEvent:'mousedown',
43553                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43554                 tabIndex:-1
43555             };
43556         }
43557         
43558         
43559         
43560         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43561         this.tb = tb;
43562          // stop form submits
43563         tb.el.on('click', function(e){
43564             e.preventDefault(); // what does this do?
43565         });
43566
43567         if(!this.disable.font) { // && !Roo.isSafari){
43568             /* why no safari for fonts 
43569             editor.fontSelect = tb.el.createChild({
43570                 tag:'select',
43571                 tabIndex: -1,
43572                 cls:'x-font-select',
43573                 html: this.createFontOptions()
43574             });
43575             
43576             editor.fontSelect.on('change', function(){
43577                 var font = editor.fontSelect.dom.value;
43578                 editor.relayCmd('fontname', font);
43579                 editor.deferFocus();
43580             }, editor);
43581             
43582             tb.add(
43583                 editor.fontSelect.dom,
43584                 '-'
43585             );
43586             */
43587             
43588         };
43589         if(!this.disable.formats){
43590             this.formatCombo = new Roo.form.ComboBox({
43591                 store: new Roo.data.SimpleStore({
43592                     id : 'tag',
43593                     fields: ['tag'],
43594                     data : this.formats // from states.js
43595                 }),
43596                 blockFocus : true,
43597                 name : '',
43598                 //autoCreate : {tag: "div",  size: "20"},
43599                 displayField:'tag',
43600                 typeAhead: false,
43601                 mode: 'local',
43602                 editable : false,
43603                 triggerAction: 'all',
43604                 emptyText:'Add tag',
43605                 selectOnFocus:true,
43606                 width:135,
43607                 listeners : {
43608                     'select': function(c, r, i) {
43609                         editorcore.insertTag(r.get('tag'));
43610                         editor.focus();
43611                     }
43612                 }
43613
43614             });
43615             tb.addField(this.formatCombo);
43616             
43617         }
43618         
43619         if(!this.disable.format){
43620             tb.add(
43621                 btn('bold'),
43622                 btn('italic'),
43623                 btn('underline')
43624             );
43625         };
43626         if(!this.disable.fontSize){
43627             tb.add(
43628                 '-',
43629                 
43630                 
43631                 btn('increasefontsize', false, editorcore.adjustFont),
43632                 btn('decreasefontsize', false, editorcore.adjustFont)
43633             );
43634         };
43635         
43636         
43637         if(!this.disable.colors){
43638             tb.add(
43639                 '-', {
43640                     id:editorcore.frameId +'-forecolor',
43641                     cls:'x-btn-icon x-edit-forecolor',
43642                     clickEvent:'mousedown',
43643                     tooltip: this.buttonTips['forecolor'] || undefined,
43644                     tabIndex:-1,
43645                     menu : new Roo.menu.ColorMenu({
43646                         allowReselect: true,
43647                         focus: Roo.emptyFn,
43648                         value:'000000',
43649                         plain:true,
43650                         selectHandler: function(cp, color){
43651                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43652                             editor.deferFocus();
43653                         },
43654                         scope: editorcore,
43655                         clickEvent:'mousedown'
43656                     })
43657                 }, {
43658                     id:editorcore.frameId +'backcolor',
43659                     cls:'x-btn-icon x-edit-backcolor',
43660                     clickEvent:'mousedown',
43661                     tooltip: this.buttonTips['backcolor'] || undefined,
43662                     tabIndex:-1,
43663                     menu : new Roo.menu.ColorMenu({
43664                         focus: Roo.emptyFn,
43665                         value:'FFFFFF',
43666                         plain:true,
43667                         allowReselect: true,
43668                         selectHandler: function(cp, color){
43669                             if(Roo.isGecko){
43670                                 editorcore.execCmd('useCSS', false);
43671                                 editorcore.execCmd('hilitecolor', color);
43672                                 editorcore.execCmd('useCSS', true);
43673                                 editor.deferFocus();
43674                             }else{
43675                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43676                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43677                                 editor.deferFocus();
43678                             }
43679                         },
43680                         scope:editorcore,
43681                         clickEvent:'mousedown'
43682                     })
43683                 }
43684             );
43685         };
43686         // now add all the items...
43687         
43688
43689         if(!this.disable.alignments){
43690             tb.add(
43691                 '-',
43692                 btn('justifyleft'),
43693                 btn('justifycenter'),
43694                 btn('justifyright')
43695             );
43696         };
43697
43698         //if(!Roo.isSafari){
43699             if(!this.disable.links){
43700                 tb.add(
43701                     '-',
43702                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43703                 );
43704             };
43705
43706             if(!this.disable.lists){
43707                 tb.add(
43708                     '-',
43709                     btn('insertorderedlist'),
43710                     btn('insertunorderedlist')
43711                 );
43712             }
43713             if(!this.disable.sourceEdit){
43714                 tb.add(
43715                     '-',
43716                     btn('sourceedit', true, function(btn){
43717                         this.toggleSourceEdit(btn.pressed);
43718                     })
43719                 );
43720             }
43721         //}
43722         
43723         var smenu = { };
43724         // special menu.. - needs to be tidied up..
43725         if (!this.disable.special) {
43726             smenu = {
43727                 text: "&#169;",
43728                 cls: 'x-edit-none',
43729                 
43730                 menu : {
43731                     items : []
43732                 }
43733             };
43734             for (var i =0; i < this.specialChars.length; i++) {
43735                 smenu.menu.items.push({
43736                     
43737                     html: this.specialChars[i],
43738                     handler: function(a,b) {
43739                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43740                         //editor.insertAtCursor(a.html);
43741                         
43742                     },
43743                     tabIndex:-1
43744                 });
43745             }
43746             
43747             
43748             tb.add(smenu);
43749             
43750             
43751         }
43752         
43753         var cmenu = { };
43754         if (!this.disable.cleanStyles) {
43755             cmenu = {
43756                 cls: 'x-btn-icon x-btn-clear',
43757                 
43758                 menu : {
43759                     items : []
43760                 }
43761             };
43762             for (var i =0; i < this.cleanStyles.length; i++) {
43763                 cmenu.menu.items.push({
43764                     actiontype : this.cleanStyles[i],
43765                     html: 'Remove ' + this.cleanStyles[i],
43766                     handler: function(a,b) {
43767 //                        Roo.log(a);
43768 //                        Roo.log(b);
43769                         var c = Roo.get(editorcore.doc.body);
43770                         c.select('[style]').each(function(s) {
43771                             s.dom.style.removeProperty(a.actiontype);
43772                         });
43773                         editorcore.syncValue();
43774                     },
43775                     tabIndex:-1
43776                 });
43777             }
43778              cmenu.menu.items.push({
43779                 actiontype : 'tablewidths',
43780                 html: 'Remove Table Widths',
43781                 handler: function(a,b) {
43782                     editorcore.cleanTableWidths();
43783                     editorcore.syncValue();
43784                 },
43785                 tabIndex:-1
43786             });
43787             cmenu.menu.items.push({
43788                 actiontype : 'word',
43789                 html: 'Remove MS Word Formating',
43790                 handler: function(a,b) {
43791                     editorcore.cleanWord();
43792                     editorcore.syncValue();
43793                 },
43794                 tabIndex:-1
43795             });
43796             
43797             cmenu.menu.items.push({
43798                 actiontype : 'all',
43799                 html: 'Remove All Styles',
43800                 handler: function(a,b) {
43801                     
43802                     var c = Roo.get(editorcore.doc.body);
43803                     c.select('[style]').each(function(s) {
43804                         s.dom.removeAttribute('style');
43805                     });
43806                     editorcore.syncValue();
43807                 },
43808                 tabIndex:-1
43809             });
43810             
43811             cmenu.menu.items.push({
43812                 actiontype : 'all',
43813                 html: 'Remove All CSS Classes',
43814                 handler: function(a,b) {
43815                     
43816                     var c = Roo.get(editorcore.doc.body);
43817                     c.select('[class]').each(function(s) {
43818                         s.dom.className = '';
43819                     });
43820                     editorcore.syncValue();
43821                 },
43822                 tabIndex:-1
43823             });
43824             
43825              cmenu.menu.items.push({
43826                 actiontype : 'tidy',
43827                 html: 'Tidy HTML Source',
43828                 handler: function(a,b) {
43829                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43830                     editorcore.syncValue();
43831                 },
43832                 tabIndex:-1
43833             });
43834             
43835             
43836             tb.add(cmenu);
43837         }
43838          
43839         if (!this.disable.specialElements) {
43840             var semenu = {
43841                 text: "Other;",
43842                 cls: 'x-edit-none',
43843                 menu : {
43844                     items : []
43845                 }
43846             };
43847             for (var i =0; i < this.specialElements.length; i++) {
43848                 semenu.menu.items.push(
43849                     Roo.apply({ 
43850                         handler: function(a,b) {
43851                             editor.insertAtCursor(this.ihtml);
43852                         }
43853                     }, this.specialElements[i])
43854                 );
43855                     
43856             }
43857             
43858             tb.add(semenu);
43859             
43860             
43861         }
43862          
43863         
43864         if (this.btns) {
43865             for(var i =0; i< this.btns.length;i++) {
43866                 var b = Roo.factory(this.btns[i],Roo.form);
43867                 b.cls =  'x-edit-none';
43868                 
43869                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43870                     b.cls += ' x-init-enable';
43871                 }
43872                 
43873                 b.scope = editorcore;
43874                 tb.add(b);
43875             }
43876         
43877         }
43878         
43879         
43880         
43881         // disable everything...
43882         
43883         this.tb.items.each(function(item){
43884             
43885            if(
43886                 item.id != editorcore.frameId+ '-sourceedit' && 
43887                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43888             ){
43889                 
43890                 item.disable();
43891             }
43892         });
43893         this.rendered = true;
43894         
43895         // the all the btns;
43896         editor.on('editorevent', this.updateToolbar, this);
43897         // other toolbars need to implement this..
43898         //editor.on('editmodechange', this.updateToolbar, this);
43899     },
43900     
43901     
43902     relayBtnCmd : function(btn) {
43903         this.editorcore.relayCmd(btn.cmd);
43904     },
43905     // private used internally
43906     createLink : function(){
43907         Roo.log("create link?");
43908         var url = prompt(this.createLinkText, this.defaultLinkValue);
43909         if(url && url != 'http:/'+'/'){
43910             this.editorcore.relayCmd('createlink', url);
43911         }
43912     },
43913
43914     
43915     /**
43916      * Protected method that will not generally be called directly. It triggers
43917      * a toolbar update by reading the markup state of the current selection in the editor.
43918      */
43919     updateToolbar: function(){
43920
43921         if(!this.editorcore.activated){
43922             this.editor.onFirstFocus();
43923             return;
43924         }
43925
43926         var btns = this.tb.items.map, 
43927             doc = this.editorcore.doc,
43928             frameId = this.editorcore.frameId;
43929
43930         if(!this.disable.font && !Roo.isSafari){
43931             /*
43932             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43933             if(name != this.fontSelect.dom.value){
43934                 this.fontSelect.dom.value = name;
43935             }
43936             */
43937         }
43938         if(!this.disable.format){
43939             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43940             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43941             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43942         }
43943         if(!this.disable.alignments){
43944             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43945             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43946             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43947         }
43948         if(!Roo.isSafari && !this.disable.lists){
43949             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43950             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43951         }
43952         
43953         var ans = this.editorcore.getAllAncestors();
43954         if (this.formatCombo) {
43955             
43956             
43957             var store = this.formatCombo.store;
43958             this.formatCombo.setValue("");
43959             for (var i =0; i < ans.length;i++) {
43960                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43961                     // select it..
43962                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43963                     break;
43964                 }
43965             }
43966         }
43967         
43968         
43969         
43970         // hides menus... - so this cant be on a menu...
43971         Roo.menu.MenuMgr.hideAll();
43972
43973         //this.editorsyncValue();
43974     },
43975    
43976     
43977     createFontOptions : function(){
43978         var buf = [], fs = this.fontFamilies, ff, lc;
43979         
43980         
43981         
43982         for(var i = 0, len = fs.length; i< len; i++){
43983             ff = fs[i];
43984             lc = ff.toLowerCase();
43985             buf.push(
43986                 '<option value="',lc,'" style="font-family:',ff,';"',
43987                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43988                     ff,
43989                 '</option>'
43990             );
43991         }
43992         return buf.join('');
43993     },
43994     
43995     toggleSourceEdit : function(sourceEditMode){
43996         
43997         Roo.log("toolbar toogle");
43998         if(sourceEditMode === undefined){
43999             sourceEditMode = !this.sourceEditMode;
44000         }
44001         this.sourceEditMode = sourceEditMode === true;
44002         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44003         // just toggle the button?
44004         if(btn.pressed !== this.sourceEditMode){
44005             btn.toggle(this.sourceEditMode);
44006             return;
44007         }
44008         
44009         if(sourceEditMode){
44010             Roo.log("disabling buttons");
44011             this.tb.items.each(function(item){
44012                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44013                     item.disable();
44014                 }
44015             });
44016           
44017         }else{
44018             Roo.log("enabling buttons");
44019             if(this.editorcore.initialized){
44020                 this.tb.items.each(function(item){
44021                     item.enable();
44022                 });
44023             }
44024             
44025         }
44026         Roo.log("calling toggole on editor");
44027         // tell the editor that it's been pressed..
44028         this.editor.toggleSourceEdit(sourceEditMode);
44029        
44030     },
44031      /**
44032      * Object collection of toolbar tooltips for the buttons in the editor. The key
44033      * is the command id associated with that button and the value is a valid QuickTips object.
44034      * For example:
44035 <pre><code>
44036 {
44037     bold : {
44038         title: 'Bold (Ctrl+B)',
44039         text: 'Make the selected text bold.',
44040         cls: 'x-html-editor-tip'
44041     },
44042     italic : {
44043         title: 'Italic (Ctrl+I)',
44044         text: 'Make the selected text italic.',
44045         cls: 'x-html-editor-tip'
44046     },
44047     ...
44048 </code></pre>
44049     * @type Object
44050      */
44051     buttonTips : {
44052         bold : {
44053             title: 'Bold (Ctrl+B)',
44054             text: 'Make the selected text bold.',
44055             cls: 'x-html-editor-tip'
44056         },
44057         italic : {
44058             title: 'Italic (Ctrl+I)',
44059             text: 'Make the selected text italic.',
44060             cls: 'x-html-editor-tip'
44061         },
44062         underline : {
44063             title: 'Underline (Ctrl+U)',
44064             text: 'Underline the selected text.',
44065             cls: 'x-html-editor-tip'
44066         },
44067         increasefontsize : {
44068             title: 'Grow Text',
44069             text: 'Increase the font size.',
44070             cls: 'x-html-editor-tip'
44071         },
44072         decreasefontsize : {
44073             title: 'Shrink Text',
44074             text: 'Decrease the font size.',
44075             cls: 'x-html-editor-tip'
44076         },
44077         backcolor : {
44078             title: 'Text Highlight Color',
44079             text: 'Change the background color of the selected text.',
44080             cls: 'x-html-editor-tip'
44081         },
44082         forecolor : {
44083             title: 'Font Color',
44084             text: 'Change the color of the selected text.',
44085             cls: 'x-html-editor-tip'
44086         },
44087         justifyleft : {
44088             title: 'Align Text Left',
44089             text: 'Align text to the left.',
44090             cls: 'x-html-editor-tip'
44091         },
44092         justifycenter : {
44093             title: 'Center Text',
44094             text: 'Center text in the editor.',
44095             cls: 'x-html-editor-tip'
44096         },
44097         justifyright : {
44098             title: 'Align Text Right',
44099             text: 'Align text to the right.',
44100             cls: 'x-html-editor-tip'
44101         },
44102         insertunorderedlist : {
44103             title: 'Bullet List',
44104             text: 'Start a bulleted list.',
44105             cls: 'x-html-editor-tip'
44106         },
44107         insertorderedlist : {
44108             title: 'Numbered List',
44109             text: 'Start a numbered list.',
44110             cls: 'x-html-editor-tip'
44111         },
44112         createlink : {
44113             title: 'Hyperlink',
44114             text: 'Make the selected text a hyperlink.',
44115             cls: 'x-html-editor-tip'
44116         },
44117         sourceedit : {
44118             title: 'Source Edit',
44119             text: 'Switch to source editing mode.',
44120             cls: 'x-html-editor-tip'
44121         }
44122     },
44123     // private
44124     onDestroy : function(){
44125         if(this.rendered){
44126             
44127             this.tb.items.each(function(item){
44128                 if(item.menu){
44129                     item.menu.removeAll();
44130                     if(item.menu.el){
44131                         item.menu.el.destroy();
44132                     }
44133                 }
44134                 item.destroy();
44135             });
44136              
44137         }
44138     },
44139     onFirstFocus: function() {
44140         this.tb.items.each(function(item){
44141            item.enable();
44142         });
44143     }
44144 });
44145
44146
44147
44148
44149 // <script type="text/javascript">
44150 /*
44151  * Based on
44152  * Ext JS Library 1.1.1
44153  * Copyright(c) 2006-2007, Ext JS, LLC.
44154  *  
44155  
44156  */
44157
44158  
44159 /**
44160  * @class Roo.form.HtmlEditor.ToolbarContext
44161  * Context Toolbar
44162  * 
44163  * Usage:
44164  *
44165  new Roo.form.HtmlEditor({
44166     ....
44167     toolbars : [
44168         { xtype: 'ToolbarStandard', styles : {} }
44169         { xtype: 'ToolbarContext', disable : {} }
44170     ]
44171 })
44172
44173      
44174  * 
44175  * @config : {Object} disable List of elements to disable.. (not done yet.)
44176  * @config : {Object} styles  Map of styles available.
44177  * 
44178  */
44179
44180 Roo.form.HtmlEditor.ToolbarContext = function(config)
44181 {
44182     
44183     Roo.apply(this, config);
44184     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44185     // dont call parent... till later.
44186     this.styles = this.styles || {};
44187 }
44188
44189  
44190
44191 Roo.form.HtmlEditor.ToolbarContext.types = {
44192     'IMG' : {
44193         width : {
44194             title: "Width",
44195             width: 40
44196         },
44197         height:  {
44198             title: "Height",
44199             width: 40
44200         },
44201         align: {
44202             title: "Align",
44203             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44204             width : 80
44205             
44206         },
44207         border: {
44208             title: "Border",
44209             width: 40
44210         },
44211         alt: {
44212             title: "Alt",
44213             width: 120
44214         },
44215         src : {
44216             title: "Src",
44217             width: 220
44218         }
44219         
44220     },
44221     'A' : {
44222         name : {
44223             title: "Name",
44224             width: 50
44225         },
44226         target:  {
44227             title: "Target",
44228             width: 120
44229         },
44230         href:  {
44231             title: "Href",
44232             width: 220
44233         } // border?
44234         
44235     },
44236     'TABLE' : {
44237         rows : {
44238             title: "Rows",
44239             width: 20
44240         },
44241         cols : {
44242             title: "Cols",
44243             width: 20
44244         },
44245         width : {
44246             title: "Width",
44247             width: 40
44248         },
44249         height : {
44250             title: "Height",
44251             width: 40
44252         },
44253         border : {
44254             title: "Border",
44255             width: 20
44256         }
44257     },
44258     'TD' : {
44259         width : {
44260             title: "Width",
44261             width: 40
44262         },
44263         height : {
44264             title: "Height",
44265             width: 40
44266         },   
44267         align: {
44268             title: "Align",
44269             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44270             width: 80
44271         },
44272         valign: {
44273             title: "Valign",
44274             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44275             width: 80
44276         },
44277         colspan: {
44278             title: "Colspan",
44279             width: 20
44280             
44281         },
44282          'font-family'  : {
44283             title : "Font",
44284             style : 'fontFamily',
44285             displayField: 'display',
44286             optname : 'font-family',
44287             width: 140
44288         }
44289     },
44290     'INPUT' : {
44291         name : {
44292             title: "name",
44293             width: 120
44294         },
44295         value : {
44296             title: "Value",
44297             width: 120
44298         },
44299         width : {
44300             title: "Width",
44301             width: 40
44302         }
44303     },
44304     'LABEL' : {
44305         'for' : {
44306             title: "For",
44307             width: 120
44308         }
44309     },
44310     'TEXTAREA' : {
44311           name : {
44312             title: "name",
44313             width: 120
44314         },
44315         rows : {
44316             title: "Rows",
44317             width: 20
44318         },
44319         cols : {
44320             title: "Cols",
44321             width: 20
44322         }
44323     },
44324     'SELECT' : {
44325         name : {
44326             title: "name",
44327             width: 120
44328         },
44329         selectoptions : {
44330             title: "Options",
44331             width: 200
44332         }
44333     },
44334     
44335     // should we really allow this??
44336     // should this just be 
44337     'BODY' : {
44338         title : {
44339             title: "Title",
44340             width: 200,
44341             disabled : true
44342         }
44343     },
44344     'SPAN' : {
44345         'font-family'  : {
44346             title : "Font",
44347             style : 'fontFamily',
44348             displayField: 'display',
44349             optname : 'font-family',
44350             width: 140
44351         }
44352     },
44353     'DIV' : {
44354         'font-family'  : {
44355             title : "Font",
44356             style : 'fontFamily',
44357             displayField: 'display',
44358             optname : 'font-family',
44359             width: 140
44360         }
44361     },
44362      'P' : {
44363         'font-family'  : {
44364             title : "Font",
44365             style : 'fontFamily',
44366             displayField: 'display',
44367             optname : 'font-family',
44368             width: 140
44369         }
44370     },
44371     
44372     '*' : {
44373         // empty..
44374     }
44375
44376 };
44377
44378 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44379 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44380
44381 Roo.form.HtmlEditor.ToolbarContext.options = {
44382         'font-family'  : [ 
44383                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44384                 [ 'Courier New', 'Courier New'],
44385                 [ 'Tahoma', 'Tahoma'],
44386                 [ 'Times New Roman,serif', 'Times'],
44387                 [ 'Verdana','Verdana' ]
44388         ]
44389 };
44390
44391 // fixme - these need to be configurable..
44392  
44393
44394 Roo.form.HtmlEditor.ToolbarContext.types
44395
44396
44397 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44398     
44399     tb: false,
44400     
44401     rendered: false,
44402     
44403     editor : false,
44404     editorcore : false,
44405     /**
44406      * @cfg {Object} disable  List of toolbar elements to disable
44407          
44408      */
44409     disable : false,
44410     /**
44411      * @cfg {Object} styles List of styles 
44412      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44413      *
44414      * These must be defined in the page, so they get rendered correctly..
44415      * .headline { }
44416      * TD.underline { }
44417      * 
44418      */
44419     styles : false,
44420     
44421     options: false,
44422     
44423     toolbars : false,
44424     
44425     init : function(editor)
44426     {
44427         this.editor = editor;
44428         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44429         var editorcore = this.editorcore;
44430         
44431         var fid = editorcore.frameId;
44432         var etb = this;
44433         function btn(id, toggle, handler){
44434             var xid = fid + '-'+ id ;
44435             return {
44436                 id : xid,
44437                 cmd : id,
44438                 cls : 'x-btn-icon x-edit-'+id,
44439                 enableToggle:toggle !== false,
44440                 scope: editorcore, // was editor...
44441                 handler:handler||editorcore.relayBtnCmd,
44442                 clickEvent:'mousedown',
44443                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44444                 tabIndex:-1
44445             };
44446         }
44447         // create a new element.
44448         var wdiv = editor.wrap.createChild({
44449                 tag: 'div'
44450             }, editor.wrap.dom.firstChild.nextSibling, true);
44451         
44452         // can we do this more than once??
44453         
44454          // stop form submits
44455       
44456  
44457         // disable everything...
44458         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44459         this.toolbars = {};
44460            
44461         for (var i in  ty) {
44462           
44463             this.toolbars[i] = this.buildToolbar(ty[i],i);
44464         }
44465         this.tb = this.toolbars.BODY;
44466         this.tb.el.show();
44467         this.buildFooter();
44468         this.footer.show();
44469         editor.on('hide', function( ) { this.footer.hide() }, this);
44470         editor.on('show', function( ) { this.footer.show() }, this);
44471         
44472          
44473         this.rendered = true;
44474         
44475         // the all the btns;
44476         editor.on('editorevent', this.updateToolbar, this);
44477         // other toolbars need to implement this..
44478         //editor.on('editmodechange', this.updateToolbar, this);
44479     },
44480     
44481     
44482     
44483     /**
44484      * Protected method that will not generally be called directly. It triggers
44485      * a toolbar update by reading the markup state of the current selection in the editor.
44486      *
44487      * Note you can force an update by calling on('editorevent', scope, false)
44488      */
44489     updateToolbar: function(editor,ev,sel){
44490
44491         //Roo.log(ev);
44492         // capture mouse up - this is handy for selecting images..
44493         // perhaps should go somewhere else...
44494         if(!this.editorcore.activated){
44495              this.editor.onFirstFocus();
44496             return;
44497         }
44498         
44499         
44500         
44501         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44502         // selectNode - might want to handle IE?
44503         if (ev &&
44504             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44505             ev.target && ev.target.tagName == 'IMG') {
44506             // they have click on an image...
44507             // let's see if we can change the selection...
44508             sel = ev.target;
44509          
44510               var nodeRange = sel.ownerDocument.createRange();
44511             try {
44512                 nodeRange.selectNode(sel);
44513             } catch (e) {
44514                 nodeRange.selectNodeContents(sel);
44515             }
44516             //nodeRange.collapse(true);
44517             var s = this.editorcore.win.getSelection();
44518             s.removeAllRanges();
44519             s.addRange(nodeRange);
44520         }  
44521         
44522       
44523         var updateFooter = sel ? false : true;
44524         
44525         
44526         var ans = this.editorcore.getAllAncestors();
44527         
44528         // pick
44529         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44530         
44531         if (!sel) { 
44532             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44533             sel = sel ? sel : this.editorcore.doc.body;
44534             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44535             
44536         }
44537         // pick a menu that exists..
44538         var tn = sel.tagName.toUpperCase();
44539         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44540         
44541         tn = sel.tagName.toUpperCase();
44542         
44543         var lastSel = this.tb.selectedNode
44544         
44545         this.tb.selectedNode = sel;
44546         
44547         // if current menu does not match..
44548         
44549         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44550                 
44551             this.tb.el.hide();
44552             ///console.log("show: " + tn);
44553             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44554             this.tb.el.show();
44555             // update name
44556             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44557             
44558             
44559             // update attributes
44560             if (this.tb.fields) {
44561                 this.tb.fields.each(function(e) {
44562                     if (e.stylename) {
44563                         e.setValue(sel.style[e.stylename]);
44564                         return;
44565                     } 
44566                    e.setValue(sel.getAttribute(e.attrname));
44567                 });
44568             }
44569             
44570             var hasStyles = false;
44571             for(var i in this.styles) {
44572                 hasStyles = true;
44573                 break;
44574             }
44575             
44576             // update styles
44577             if (hasStyles) { 
44578                 var st = this.tb.fields.item(0);
44579                 
44580                 st.store.removeAll();
44581                
44582                 
44583                 var cn = sel.className.split(/\s+/);
44584                 
44585                 var avs = [];
44586                 if (this.styles['*']) {
44587                     
44588                     Roo.each(this.styles['*'], function(v) {
44589                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44590                     });
44591                 }
44592                 if (this.styles[tn]) { 
44593                     Roo.each(this.styles[tn], function(v) {
44594                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44595                     });
44596                 }
44597                 
44598                 st.store.loadData(avs);
44599                 st.collapse();
44600                 st.setValue(cn);
44601             }
44602             // flag our selected Node.
44603             this.tb.selectedNode = sel;
44604            
44605            
44606             Roo.menu.MenuMgr.hideAll();
44607
44608         }
44609         
44610         if (!updateFooter) {
44611             //this.footDisp.dom.innerHTML = ''; 
44612             return;
44613         }
44614         // update the footer
44615         //
44616         var html = '';
44617         
44618         this.footerEls = ans.reverse();
44619         Roo.each(this.footerEls, function(a,i) {
44620             if (!a) { return; }
44621             html += html.length ? ' &gt; '  :  '';
44622             
44623             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44624             
44625         });
44626        
44627         // 
44628         var sz = this.footDisp.up('td').getSize();
44629         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44630         this.footDisp.dom.style.marginLeft = '5px';
44631         
44632         this.footDisp.dom.style.overflow = 'hidden';
44633         
44634         this.footDisp.dom.innerHTML = html;
44635             
44636         //this.editorsyncValue();
44637     },
44638      
44639     
44640    
44641        
44642     // private
44643     onDestroy : function(){
44644         if(this.rendered){
44645             
44646             this.tb.items.each(function(item){
44647                 if(item.menu){
44648                     item.menu.removeAll();
44649                     if(item.menu.el){
44650                         item.menu.el.destroy();
44651                     }
44652                 }
44653                 item.destroy();
44654             });
44655              
44656         }
44657     },
44658     onFirstFocus: function() {
44659         // need to do this for all the toolbars..
44660         this.tb.items.each(function(item){
44661            item.enable();
44662         });
44663     },
44664     buildToolbar: function(tlist, nm)
44665     {
44666         var editor = this.editor;
44667         var editorcore = this.editorcore;
44668          // create a new element.
44669         var wdiv = editor.wrap.createChild({
44670                 tag: 'div'
44671             }, editor.wrap.dom.firstChild.nextSibling, true);
44672         
44673        
44674         var tb = new Roo.Toolbar(wdiv);
44675         // add the name..
44676         
44677         tb.add(nm+ ":&nbsp;");
44678         
44679         var styles = [];
44680         for(var i in this.styles) {
44681             styles.push(i);
44682         }
44683         
44684         // styles...
44685         if (styles && styles.length) {
44686             
44687             // this needs a multi-select checkbox...
44688             tb.addField( new Roo.form.ComboBox({
44689                 store: new Roo.data.SimpleStore({
44690                     id : 'val',
44691                     fields: ['val', 'selected'],
44692                     data : [] 
44693                 }),
44694                 name : '-roo-edit-className',
44695                 attrname : 'className',
44696                 displayField: 'val',
44697                 typeAhead: false,
44698                 mode: 'local',
44699                 editable : false,
44700                 triggerAction: 'all',
44701                 emptyText:'Select Style',
44702                 selectOnFocus:true,
44703                 width: 130,
44704                 listeners : {
44705                     'select': function(c, r, i) {
44706                         // initial support only for on class per el..
44707                         tb.selectedNode.className =  r ? r.get('val') : '';
44708                         editorcore.syncValue();
44709                     }
44710                 }
44711     
44712             }));
44713         }
44714         
44715         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44716         var tbops = tbc.options;
44717         
44718         for (var i in tlist) {
44719             
44720             var item = tlist[i];
44721             tb.add(item.title + ":&nbsp;");
44722             
44723             
44724             //optname == used so you can configure the options available..
44725             var opts = item.opts ? item.opts : false;
44726             if (item.optname) {
44727                 opts = tbops[item.optname];
44728            
44729             }
44730             
44731             if (opts) {
44732                 // opts == pulldown..
44733                 tb.addField( new Roo.form.ComboBox({
44734                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44735                         id : 'val',
44736                         fields: ['val', 'display'],
44737                         data : opts  
44738                     }),
44739                     name : '-roo-edit-' + i,
44740                     attrname : i,
44741                     stylename : item.style ? item.style : false,
44742                     displayField: item.displayField ? item.displayField : 'val',
44743                     valueField :  'val',
44744                     typeAhead: false,
44745                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44746                     editable : false,
44747                     triggerAction: 'all',
44748                     emptyText:'Select',
44749                     selectOnFocus:true,
44750                     width: item.width ? item.width  : 130,
44751                     listeners : {
44752                         'select': function(c, r, i) {
44753                             if (c.stylename) {
44754                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44755                                 return;
44756                             }
44757                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44758                         }
44759                     }
44760
44761                 }));
44762                 continue;
44763                     
44764                  
44765                 
44766                 tb.addField( new Roo.form.TextField({
44767                     name: i,
44768                     width: 100,
44769                     //allowBlank:false,
44770                     value: ''
44771                 }));
44772                 continue;
44773             }
44774             tb.addField( new Roo.form.TextField({
44775                 name: '-roo-edit-' + i,
44776                 attrname : i,
44777                 
44778                 width: item.width,
44779                 //allowBlank:true,
44780                 value: '',
44781                 listeners: {
44782                     'change' : function(f, nv, ov) {
44783                         tb.selectedNode.setAttribute(f.attrname, nv);
44784                     }
44785                 }
44786             }));
44787              
44788         }
44789         
44790         var _this = this;
44791         
44792         if(nm == 'BODY'){
44793             tb.addSeparator();
44794         
44795             tb.addButton( {
44796                 text: 'Stylesheets',
44797
44798                 listeners : {
44799                     click : function ()
44800                     {
44801                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44802                     }
44803                 }
44804             });
44805         }
44806         
44807         tb.addFill();
44808         tb.addButton( {
44809             text: 'Remove Tag',
44810     
44811             listeners : {
44812                 click : function ()
44813                 {
44814                     // remove
44815                     // undo does not work.
44816                      
44817                     var sn = tb.selectedNode;
44818                     
44819                     var pn = sn.parentNode;
44820                     
44821                     var stn =  sn.childNodes[0];
44822                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44823                     while (sn.childNodes.length) {
44824                         var node = sn.childNodes[0];
44825                         sn.removeChild(node);
44826                         //Roo.log(node);
44827                         pn.insertBefore(node, sn);
44828                         
44829                     }
44830                     pn.removeChild(sn);
44831                     var range = editorcore.createRange();
44832         
44833                     range.setStart(stn,0);
44834                     range.setEnd(en,0); //????
44835                     //range.selectNode(sel);
44836                     
44837                     
44838                     var selection = editorcore.getSelection();
44839                     selection.removeAllRanges();
44840                     selection.addRange(range);
44841                     
44842                     
44843                     
44844                     //_this.updateToolbar(null, null, pn);
44845                     _this.updateToolbar(null, null, null);
44846                     _this.footDisp.dom.innerHTML = ''; 
44847                 }
44848             }
44849             
44850                     
44851                 
44852             
44853         });
44854         
44855         
44856         tb.el.on('click', function(e){
44857             e.preventDefault(); // what does this do?
44858         });
44859         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44860         tb.el.hide();
44861         tb.name = nm;
44862         // dont need to disable them... as they will get hidden
44863         return tb;
44864          
44865         
44866     },
44867     buildFooter : function()
44868     {
44869         
44870         var fel = this.editor.wrap.createChild();
44871         this.footer = new Roo.Toolbar(fel);
44872         // toolbar has scrolly on left / right?
44873         var footDisp= new Roo.Toolbar.Fill();
44874         var _t = this;
44875         this.footer.add(
44876             {
44877                 text : '&lt;',
44878                 xtype: 'Button',
44879                 handler : function() {
44880                     _t.footDisp.scrollTo('left',0,true)
44881                 }
44882             }
44883         );
44884         this.footer.add( footDisp );
44885         this.footer.add( 
44886             {
44887                 text : '&gt;',
44888                 xtype: 'Button',
44889                 handler : function() {
44890                     // no animation..
44891                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44892                 }
44893             }
44894         );
44895         var fel = Roo.get(footDisp.el);
44896         fel.addClass('x-editor-context');
44897         this.footDispWrap = fel; 
44898         this.footDispWrap.overflow  = 'hidden';
44899         
44900         this.footDisp = fel.createChild();
44901         this.footDispWrap.on('click', this.onContextClick, this)
44902         
44903         
44904     },
44905     onContextClick : function (ev,dom)
44906     {
44907         ev.preventDefault();
44908         var  cn = dom.className;
44909         //Roo.log(cn);
44910         if (!cn.match(/x-ed-loc-/)) {
44911             return;
44912         }
44913         var n = cn.split('-').pop();
44914         var ans = this.footerEls;
44915         var sel = ans[n];
44916         
44917          // pick
44918         var range = this.editorcore.createRange();
44919         
44920         range.selectNodeContents(sel);
44921         //range.selectNode(sel);
44922         
44923         
44924         var selection = this.editorcore.getSelection();
44925         selection.removeAllRanges();
44926         selection.addRange(range);
44927         
44928         
44929         
44930         this.updateToolbar(null, null, sel);
44931         
44932         
44933     }
44934     
44935     
44936     
44937     
44938     
44939 });
44940
44941
44942
44943
44944
44945 /*
44946  * Based on:
44947  * Ext JS Library 1.1.1
44948  * Copyright(c) 2006-2007, Ext JS, LLC.
44949  *
44950  * Originally Released Under LGPL - original licence link has changed is not relivant.
44951  *
44952  * Fork - LGPL
44953  * <script type="text/javascript">
44954  */
44955  
44956 /**
44957  * @class Roo.form.BasicForm
44958  * @extends Roo.util.Observable
44959  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44960  * @constructor
44961  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44962  * @param {Object} config Configuration options
44963  */
44964 Roo.form.BasicForm = function(el, config){
44965     this.allItems = [];
44966     this.childForms = [];
44967     Roo.apply(this, config);
44968     /*
44969      * The Roo.form.Field items in this form.
44970      * @type MixedCollection
44971      */
44972      
44973      
44974     this.items = new Roo.util.MixedCollection(false, function(o){
44975         return o.id || (o.id = Roo.id());
44976     });
44977     this.addEvents({
44978         /**
44979          * @event beforeaction
44980          * Fires before any action is performed. Return false to cancel the action.
44981          * @param {Form} this
44982          * @param {Action} action The action to be performed
44983          */
44984         beforeaction: true,
44985         /**
44986          * @event actionfailed
44987          * Fires when an action fails.
44988          * @param {Form} this
44989          * @param {Action} action The action that failed
44990          */
44991         actionfailed : true,
44992         /**
44993          * @event actioncomplete
44994          * Fires when an action is completed.
44995          * @param {Form} this
44996          * @param {Action} action The action that completed
44997          */
44998         actioncomplete : true
44999     });
45000     if(el){
45001         this.initEl(el);
45002     }
45003     Roo.form.BasicForm.superclass.constructor.call(this);
45004 };
45005
45006 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45007     /**
45008      * @cfg {String} method
45009      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45010      */
45011     /**
45012      * @cfg {DataReader} reader
45013      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45014      * This is optional as there is built-in support for processing JSON.
45015      */
45016     /**
45017      * @cfg {DataReader} errorReader
45018      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45019      * This is completely optional as there is built-in support for processing JSON.
45020      */
45021     /**
45022      * @cfg {String} url
45023      * The URL to use for form actions if one isn't supplied in the action options.
45024      */
45025     /**
45026      * @cfg {Boolean} fileUpload
45027      * Set to true if this form is a file upload.
45028      */
45029      
45030     /**
45031      * @cfg {Object} baseParams
45032      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45033      */
45034      /**
45035      
45036     /**
45037      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45038      */
45039     timeout: 30,
45040
45041     // private
45042     activeAction : null,
45043
45044     /**
45045      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45046      * or setValues() data instead of when the form was first created.
45047      */
45048     trackResetOnLoad : false,
45049     
45050     
45051     /**
45052      * childForms - used for multi-tab forms
45053      * @type {Array}
45054      */
45055     childForms : false,
45056     
45057     /**
45058      * allItems - full list of fields.
45059      * @type {Array}
45060      */
45061     allItems : false,
45062     
45063     /**
45064      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45065      * element by passing it or its id or mask the form itself by passing in true.
45066      * @type Mixed
45067      */
45068     waitMsgTarget : false,
45069
45070     // private
45071     initEl : function(el){
45072         this.el = Roo.get(el);
45073         this.id = this.el.id || Roo.id();
45074         this.el.on('submit', this.onSubmit, this);
45075         this.el.addClass('x-form');
45076     },
45077
45078     // private
45079     onSubmit : function(e){
45080         e.stopEvent();
45081     },
45082
45083     /**
45084      * Returns true if client-side validation on the form is successful.
45085      * @return Boolean
45086      */
45087     isValid : function(){
45088         var valid = true;
45089         this.items.each(function(f){
45090            if(!f.validate()){
45091                valid = false;
45092            }
45093         });
45094         return valid;
45095     },
45096
45097     /**
45098      * Returns true if any fields in this form have changed since their original load.
45099      * @return Boolean
45100      */
45101     isDirty : function(){
45102         var dirty = false;
45103         this.items.each(function(f){
45104            if(f.isDirty()){
45105                dirty = true;
45106                return false;
45107            }
45108         });
45109         return dirty;
45110     },
45111
45112     /**
45113      * Performs a predefined action (submit or load) or custom actions you define on this form.
45114      * @param {String} actionName The name of the action type
45115      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45116      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45117      * accept other config options):
45118      * <pre>
45119 Property          Type             Description
45120 ----------------  ---------------  ----------------------------------------------------------------------------------
45121 url               String           The url for the action (defaults to the form's url)
45122 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45123 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45124 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45125                                    validate the form on the client (defaults to false)
45126      * </pre>
45127      * @return {BasicForm} this
45128      */
45129     doAction : function(action, options){
45130         if(typeof action == 'string'){
45131             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45132         }
45133         if(this.fireEvent('beforeaction', this, action) !== false){
45134             this.beforeAction(action);
45135             action.run.defer(100, action);
45136         }
45137         return this;
45138     },
45139
45140     /**
45141      * Shortcut to do a submit action.
45142      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45143      * @return {BasicForm} this
45144      */
45145     submit : function(options){
45146         this.doAction('submit', options);
45147         return this;
45148     },
45149
45150     /**
45151      * Shortcut to do a load action.
45152      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45153      * @return {BasicForm} this
45154      */
45155     load : function(options){
45156         this.doAction('load', options);
45157         return this;
45158     },
45159
45160     /**
45161      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45162      * @param {Record} record The record to edit
45163      * @return {BasicForm} this
45164      */
45165     updateRecord : function(record){
45166         record.beginEdit();
45167         var fs = record.fields;
45168         fs.each(function(f){
45169             var field = this.findField(f.name);
45170             if(field){
45171                 record.set(f.name, field.getValue());
45172             }
45173         }, this);
45174         record.endEdit();
45175         return this;
45176     },
45177
45178     /**
45179      * Loads an Roo.data.Record into this form.
45180      * @param {Record} record The record to load
45181      * @return {BasicForm} this
45182      */
45183     loadRecord : function(record){
45184         this.setValues(record.data);
45185         return this;
45186     },
45187
45188     // private
45189     beforeAction : function(action){
45190         var o = action.options;
45191         
45192        
45193         if(this.waitMsgTarget === true){
45194             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45195         }else if(this.waitMsgTarget){
45196             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45197             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45198         }else {
45199             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45200         }
45201          
45202     },
45203
45204     // private
45205     afterAction : function(action, success){
45206         this.activeAction = null;
45207         var o = action.options;
45208         
45209         if(this.waitMsgTarget === true){
45210             this.el.unmask();
45211         }else if(this.waitMsgTarget){
45212             this.waitMsgTarget.unmask();
45213         }else{
45214             Roo.MessageBox.updateProgress(1);
45215             Roo.MessageBox.hide();
45216         }
45217          
45218         if(success){
45219             if(o.reset){
45220                 this.reset();
45221             }
45222             Roo.callback(o.success, o.scope, [this, action]);
45223             this.fireEvent('actioncomplete', this, action);
45224             
45225         }else{
45226             
45227             // failure condition..
45228             // we have a scenario where updates need confirming.
45229             // eg. if a locking scenario exists..
45230             // we look for { errors : { needs_confirm : true }} in the response.
45231             if (
45232                 (typeof(action.result) != 'undefined')  &&
45233                 (typeof(action.result.errors) != 'undefined')  &&
45234                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45235            ){
45236                 var _t = this;
45237                 Roo.MessageBox.confirm(
45238                     "Change requires confirmation",
45239                     action.result.errorMsg,
45240                     function(r) {
45241                         if (r != 'yes') {
45242                             return;
45243                         }
45244                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45245                     }
45246                     
45247                 );
45248                 
45249                 
45250                 
45251                 return;
45252             }
45253             
45254             Roo.callback(o.failure, o.scope, [this, action]);
45255             // show an error message if no failed handler is set..
45256             if (!this.hasListener('actionfailed')) {
45257                 Roo.MessageBox.alert("Error",
45258                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45259                         action.result.errorMsg :
45260                         "Saving Failed, please check your entries or try again"
45261                 );
45262             }
45263             
45264             this.fireEvent('actionfailed', this, action);
45265         }
45266         
45267     },
45268
45269     /**
45270      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45271      * @param {String} id The value to search for
45272      * @return Field
45273      */
45274     findField : function(id){
45275         var field = this.items.get(id);
45276         if(!field){
45277             this.items.each(function(f){
45278                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45279                     field = f;
45280                     return false;
45281                 }
45282             });
45283         }
45284         return field || null;
45285     },
45286
45287     /**
45288      * Add a secondary form to this one, 
45289      * Used to provide tabbed forms. One form is primary, with hidden values 
45290      * which mirror the elements from the other forms.
45291      * 
45292      * @param {Roo.form.Form} form to add.
45293      * 
45294      */
45295     addForm : function(form)
45296     {
45297        
45298         if (this.childForms.indexOf(form) > -1) {
45299             // already added..
45300             return;
45301         }
45302         this.childForms.push(form);
45303         var n = '';
45304         Roo.each(form.allItems, function (fe) {
45305             
45306             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45307             if (this.findField(n)) { // already added..
45308                 return;
45309             }
45310             var add = new Roo.form.Hidden({
45311                 name : n
45312             });
45313             add.render(this.el);
45314             
45315             this.add( add );
45316         }, this);
45317         
45318     },
45319     /**
45320      * Mark fields in this form invalid in bulk.
45321      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45322      * @return {BasicForm} this
45323      */
45324     markInvalid : function(errors){
45325         if(errors instanceof Array){
45326             for(var i = 0, len = errors.length; i < len; i++){
45327                 var fieldError = errors[i];
45328                 var f = this.findField(fieldError.id);
45329                 if(f){
45330                     f.markInvalid(fieldError.msg);
45331                 }
45332             }
45333         }else{
45334             var field, id;
45335             for(id in errors){
45336                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45337                     field.markInvalid(errors[id]);
45338                 }
45339             }
45340         }
45341         Roo.each(this.childForms || [], function (f) {
45342             f.markInvalid(errors);
45343         });
45344         
45345         return this;
45346     },
45347
45348     /**
45349      * Set values for fields in this form in bulk.
45350      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45351      * @return {BasicForm} this
45352      */
45353     setValues : function(values){
45354         if(values instanceof Array){ // array of objects
45355             for(var i = 0, len = values.length; i < len; i++){
45356                 var v = values[i];
45357                 var f = this.findField(v.id);
45358                 if(f){
45359                     f.setValue(v.value);
45360                     if(this.trackResetOnLoad){
45361                         f.originalValue = f.getValue();
45362                     }
45363                 }
45364             }
45365         }else{ // object hash
45366             var field, id;
45367             for(id in values){
45368                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45369                     
45370                     if (field.setFromData && 
45371                         field.valueField && 
45372                         field.displayField &&
45373                         // combos' with local stores can 
45374                         // be queried via setValue()
45375                         // to set their value..
45376                         (field.store && !field.store.isLocal)
45377                         ) {
45378                         // it's a combo
45379                         var sd = { };
45380                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45381                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45382                         field.setFromData(sd);
45383                         
45384                     } else {
45385                         field.setValue(values[id]);
45386                     }
45387                     
45388                     
45389                     if(this.trackResetOnLoad){
45390                         field.originalValue = field.getValue();
45391                     }
45392                 }
45393             }
45394         }
45395          
45396         Roo.each(this.childForms || [], function (f) {
45397             f.setValues(values);
45398         });
45399                 
45400         return this;
45401     },
45402
45403     /**
45404      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45405      * they are returned as an array.
45406      * @param {Boolean} asString
45407      * @return {Object}
45408      */
45409     getValues : function(asString){
45410         if (this.childForms) {
45411             // copy values from the child forms
45412             Roo.each(this.childForms, function (f) {
45413                 this.setValues(f.getValues());
45414             }, this);
45415         }
45416         
45417         
45418         
45419         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45420         if(asString === true){
45421             return fs;
45422         }
45423         return Roo.urlDecode(fs);
45424     },
45425     
45426     /**
45427      * Returns the fields in this form as an object with key/value pairs. 
45428      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45429      * @return {Object}
45430      */
45431     getFieldValues : function(with_hidden)
45432     {
45433         if (this.childForms) {
45434             // copy values from the child forms
45435             // should this call getFieldValues - probably not as we do not currently copy
45436             // hidden fields when we generate..
45437             Roo.each(this.childForms, function (f) {
45438                 this.setValues(f.getValues());
45439             }, this);
45440         }
45441         
45442         var ret = {};
45443         this.items.each(function(f){
45444             if (!f.getName()) {
45445                 return;
45446             }
45447             var v = f.getValue();
45448             if (f.inputType =='radio') {
45449                 if (typeof(ret[f.getName()]) == 'undefined') {
45450                     ret[f.getName()] = ''; // empty..
45451                 }
45452                 
45453                 if (!f.el.dom.checked) {
45454                     return;
45455                     
45456                 }
45457                 v = f.el.dom.value;
45458                 
45459             }
45460             
45461             // not sure if this supported any more..
45462             if ((typeof(v) == 'object') && f.getRawValue) {
45463                 v = f.getRawValue() ; // dates..
45464             }
45465             // combo boxes where name != hiddenName...
45466             if (f.name != f.getName()) {
45467                 ret[f.name] = f.getRawValue();
45468             }
45469             ret[f.getName()] = v;
45470         });
45471         
45472         return ret;
45473     },
45474
45475     /**
45476      * Clears all invalid messages in this form.
45477      * @return {BasicForm} this
45478      */
45479     clearInvalid : function(){
45480         this.items.each(function(f){
45481            f.clearInvalid();
45482         });
45483         
45484         Roo.each(this.childForms || [], function (f) {
45485             f.clearInvalid();
45486         });
45487         
45488         
45489         return this;
45490     },
45491
45492     /**
45493      * Resets this form.
45494      * @return {BasicForm} this
45495      */
45496     reset : function(){
45497         this.items.each(function(f){
45498             f.reset();
45499         });
45500         
45501         Roo.each(this.childForms || [], function (f) {
45502             f.reset();
45503         });
45504        
45505         
45506         return this;
45507     },
45508
45509     /**
45510      * Add Roo.form components to this form.
45511      * @param {Field} field1
45512      * @param {Field} field2 (optional)
45513      * @param {Field} etc (optional)
45514      * @return {BasicForm} this
45515      */
45516     add : function(){
45517         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45518         return this;
45519     },
45520
45521
45522     /**
45523      * Removes a field from the items collection (does NOT remove its markup).
45524      * @param {Field} field
45525      * @return {BasicForm} this
45526      */
45527     remove : function(field){
45528         this.items.remove(field);
45529         return this;
45530     },
45531
45532     /**
45533      * Looks at the fields in this form, checks them for an id attribute,
45534      * and calls applyTo on the existing dom element with that id.
45535      * @return {BasicForm} this
45536      */
45537     render : function(){
45538         this.items.each(function(f){
45539             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45540                 f.applyTo(f.id);
45541             }
45542         });
45543         return this;
45544     },
45545
45546     /**
45547      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45548      * @param {Object} values
45549      * @return {BasicForm} this
45550      */
45551     applyToFields : function(o){
45552         this.items.each(function(f){
45553            Roo.apply(f, o);
45554         });
45555         return this;
45556     },
45557
45558     /**
45559      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45560      * @param {Object} values
45561      * @return {BasicForm} this
45562      */
45563     applyIfToFields : function(o){
45564         this.items.each(function(f){
45565            Roo.applyIf(f, o);
45566         });
45567         return this;
45568     }
45569 });
45570
45571 // back compat
45572 Roo.BasicForm = Roo.form.BasicForm;/*
45573  * Based on:
45574  * Ext JS Library 1.1.1
45575  * Copyright(c) 2006-2007, Ext JS, LLC.
45576  *
45577  * Originally Released Under LGPL - original licence link has changed is not relivant.
45578  *
45579  * Fork - LGPL
45580  * <script type="text/javascript">
45581  */
45582
45583 /**
45584  * @class Roo.form.Form
45585  * @extends Roo.form.BasicForm
45586  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45587  * @constructor
45588  * @param {Object} config Configuration options
45589  */
45590 Roo.form.Form = function(config){
45591     var xitems =  [];
45592     if (config.items) {
45593         xitems = config.items;
45594         delete config.items;
45595     }
45596    
45597     
45598     Roo.form.Form.superclass.constructor.call(this, null, config);
45599     this.url = this.url || this.action;
45600     if(!this.root){
45601         this.root = new Roo.form.Layout(Roo.applyIf({
45602             id: Roo.id()
45603         }, config));
45604     }
45605     this.active = this.root;
45606     /**
45607      * Array of all the buttons that have been added to this form via {@link addButton}
45608      * @type Array
45609      */
45610     this.buttons = [];
45611     this.allItems = [];
45612     this.addEvents({
45613         /**
45614          * @event clientvalidation
45615          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45616          * @param {Form} this
45617          * @param {Boolean} valid true if the form has passed client-side validation
45618          */
45619         clientvalidation: true,
45620         /**
45621          * @event rendered
45622          * Fires when the form is rendered
45623          * @param {Roo.form.Form} form
45624          */
45625         rendered : true
45626     });
45627     
45628     if (this.progressUrl) {
45629             // push a hidden field onto the list of fields..
45630             this.addxtype( {
45631                     xns: Roo.form, 
45632                     xtype : 'Hidden', 
45633                     name : 'UPLOAD_IDENTIFIER' 
45634             });
45635         }
45636         
45637     
45638     Roo.each(xitems, this.addxtype, this);
45639     
45640     
45641     
45642 };
45643
45644 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45645     /**
45646      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45647      */
45648     /**
45649      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45650      */
45651     /**
45652      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45653      */
45654     buttonAlign:'center',
45655
45656     /**
45657      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45658      */
45659     minButtonWidth:75,
45660
45661     /**
45662      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45663      * This property cascades to child containers if not set.
45664      */
45665     labelAlign:'left',
45666
45667     /**
45668      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45669      * fires a looping event with that state. This is required to bind buttons to the valid
45670      * state using the config value formBind:true on the button.
45671      */
45672     monitorValid : false,
45673
45674     /**
45675      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45676      */
45677     monitorPoll : 200,
45678     
45679     /**
45680      * @cfg {String} progressUrl - Url to return progress data 
45681      */
45682     
45683     progressUrl : false,
45684   
45685     /**
45686      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45687      * fields are added and the column is closed. If no fields are passed the column remains open
45688      * until end() is called.
45689      * @param {Object} config The config to pass to the column
45690      * @param {Field} field1 (optional)
45691      * @param {Field} field2 (optional)
45692      * @param {Field} etc (optional)
45693      * @return Column The column container object
45694      */
45695     column : function(c){
45696         var col = new Roo.form.Column(c);
45697         this.start(col);
45698         if(arguments.length > 1){ // duplicate code required because of Opera
45699             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45700             this.end();
45701         }
45702         return col;
45703     },
45704
45705     /**
45706      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45707      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45708      * until end() is called.
45709      * @param {Object} config The config to pass to the fieldset
45710      * @param {Field} field1 (optional)
45711      * @param {Field} field2 (optional)
45712      * @param {Field} etc (optional)
45713      * @return FieldSet The fieldset container object
45714      */
45715     fieldset : function(c){
45716         var fs = new Roo.form.FieldSet(c);
45717         this.start(fs);
45718         if(arguments.length > 1){ // duplicate code required because of Opera
45719             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45720             this.end();
45721         }
45722         return fs;
45723     },
45724
45725     /**
45726      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45727      * fields are added and the container is closed. If no fields are passed the container remains open
45728      * until end() is called.
45729      * @param {Object} config The config to pass to the Layout
45730      * @param {Field} field1 (optional)
45731      * @param {Field} field2 (optional)
45732      * @param {Field} etc (optional)
45733      * @return Layout The container object
45734      */
45735     container : function(c){
45736         var l = new Roo.form.Layout(c);
45737         this.start(l);
45738         if(arguments.length > 1){ // duplicate code required because of Opera
45739             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45740             this.end();
45741         }
45742         return l;
45743     },
45744
45745     /**
45746      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45747      * @param {Object} container A Roo.form.Layout or subclass of Layout
45748      * @return {Form} this
45749      */
45750     start : function(c){
45751         // cascade label info
45752         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45753         this.active.stack.push(c);
45754         c.ownerCt = this.active;
45755         this.active = c;
45756         return this;
45757     },
45758
45759     /**
45760      * Closes the current open container
45761      * @return {Form} this
45762      */
45763     end : function(){
45764         if(this.active == this.root){
45765             return this;
45766         }
45767         this.active = this.active.ownerCt;
45768         return this;
45769     },
45770
45771     /**
45772      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45773      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45774      * as the label of the field.
45775      * @param {Field} field1
45776      * @param {Field} field2 (optional)
45777      * @param {Field} etc. (optional)
45778      * @return {Form} this
45779      */
45780     add : function(){
45781         this.active.stack.push.apply(this.active.stack, arguments);
45782         this.allItems.push.apply(this.allItems,arguments);
45783         var r = [];
45784         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45785             if(a[i].isFormField){
45786                 r.push(a[i]);
45787             }
45788         }
45789         if(r.length > 0){
45790             Roo.form.Form.superclass.add.apply(this, r);
45791         }
45792         return this;
45793     },
45794     
45795
45796     
45797     
45798     
45799      /**
45800      * Find any element that has been added to a form, using it's ID or name
45801      * This can include framesets, columns etc. along with regular fields..
45802      * @param {String} id - id or name to find.
45803      
45804      * @return {Element} e - or false if nothing found.
45805      */
45806     findbyId : function(id)
45807     {
45808         var ret = false;
45809         if (!id) {
45810             return ret;
45811         }
45812         Roo.each(this.allItems, function(f){
45813             if (f.id == id || f.name == id ){
45814                 ret = f;
45815                 return false;
45816             }
45817         });
45818         return ret;
45819     },
45820
45821     
45822     
45823     /**
45824      * Render this form into the passed container. This should only be called once!
45825      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45826      * @return {Form} this
45827      */
45828     render : function(ct)
45829     {
45830         
45831         
45832         
45833         ct = Roo.get(ct);
45834         var o = this.autoCreate || {
45835             tag: 'form',
45836             method : this.method || 'POST',
45837             id : this.id || Roo.id()
45838         };
45839         this.initEl(ct.createChild(o));
45840
45841         this.root.render(this.el);
45842         
45843        
45844              
45845         this.items.each(function(f){
45846             f.render('x-form-el-'+f.id);
45847         });
45848
45849         if(this.buttons.length > 0){
45850             // tables are required to maintain order and for correct IE layout
45851             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45852                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45853                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45854             }}, null, true);
45855             var tr = tb.getElementsByTagName('tr')[0];
45856             for(var i = 0, len = this.buttons.length; i < len; i++) {
45857                 var b = this.buttons[i];
45858                 var td = document.createElement('td');
45859                 td.className = 'x-form-btn-td';
45860                 b.render(tr.appendChild(td));
45861             }
45862         }
45863         if(this.monitorValid){ // initialize after render
45864             this.startMonitoring();
45865         }
45866         this.fireEvent('rendered', this);
45867         return this;
45868     },
45869
45870     /**
45871      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45872      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45873      * object or a valid Roo.DomHelper element config
45874      * @param {Function} handler The function called when the button is clicked
45875      * @param {Object} scope (optional) The scope of the handler function
45876      * @return {Roo.Button}
45877      */
45878     addButton : function(config, handler, scope){
45879         var bc = {
45880             handler: handler,
45881             scope: scope,
45882             minWidth: this.minButtonWidth,
45883             hideParent:true
45884         };
45885         if(typeof config == "string"){
45886             bc.text = config;
45887         }else{
45888             Roo.apply(bc, config);
45889         }
45890         var btn = new Roo.Button(null, bc);
45891         this.buttons.push(btn);
45892         return btn;
45893     },
45894
45895      /**
45896      * Adds a series of form elements (using the xtype property as the factory method.
45897      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45898      * @param {Object} config 
45899      */
45900     
45901     addxtype : function()
45902     {
45903         var ar = Array.prototype.slice.call(arguments, 0);
45904         var ret = false;
45905         for(var i = 0; i < ar.length; i++) {
45906             if (!ar[i]) {
45907                 continue; // skip -- if this happends something invalid got sent, we 
45908                 // should ignore it, as basically that interface element will not show up
45909                 // and that should be pretty obvious!!
45910             }
45911             
45912             if (Roo.form[ar[i].xtype]) {
45913                 ar[i].form = this;
45914                 var fe = Roo.factory(ar[i], Roo.form);
45915                 if (!ret) {
45916                     ret = fe;
45917                 }
45918                 fe.form = this;
45919                 if (fe.store) {
45920                     fe.store.form = this;
45921                 }
45922                 if (fe.isLayout) {  
45923                          
45924                     this.start(fe);
45925                     this.allItems.push(fe);
45926                     if (fe.items && fe.addxtype) {
45927                         fe.addxtype.apply(fe, fe.items);
45928                         delete fe.items;
45929                     }
45930                      this.end();
45931                     continue;
45932                 }
45933                 
45934                 
45935                  
45936                 this.add(fe);
45937               //  console.log('adding ' + ar[i].xtype);
45938             }
45939             if (ar[i].xtype == 'Button') {  
45940                 //console.log('adding button');
45941                 //console.log(ar[i]);
45942                 this.addButton(ar[i]);
45943                 this.allItems.push(fe);
45944                 continue;
45945             }
45946             
45947             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45948                 alert('end is not supported on xtype any more, use items');
45949             //    this.end();
45950             //    //console.log('adding end');
45951             }
45952             
45953         }
45954         return ret;
45955     },
45956     
45957     /**
45958      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45959      * option "monitorValid"
45960      */
45961     startMonitoring : function(){
45962         if(!this.bound){
45963             this.bound = true;
45964             Roo.TaskMgr.start({
45965                 run : this.bindHandler,
45966                 interval : this.monitorPoll || 200,
45967                 scope: this
45968             });
45969         }
45970     },
45971
45972     /**
45973      * Stops monitoring of the valid state of this form
45974      */
45975     stopMonitoring : function(){
45976         this.bound = false;
45977     },
45978
45979     // private
45980     bindHandler : function(){
45981         if(!this.bound){
45982             return false; // stops binding
45983         }
45984         var valid = true;
45985         this.items.each(function(f){
45986             if(!f.isValid(true)){
45987                 valid = false;
45988                 return false;
45989             }
45990         });
45991         for(var i = 0, len = this.buttons.length; i < len; i++){
45992             var btn = this.buttons[i];
45993             if(btn.formBind === true && btn.disabled === valid){
45994                 btn.setDisabled(!valid);
45995             }
45996         }
45997         this.fireEvent('clientvalidation', this, valid);
45998     }
45999     
46000     
46001     
46002     
46003     
46004     
46005     
46006     
46007 });
46008
46009
46010 // back compat
46011 Roo.Form = Roo.form.Form;
46012 /*
46013  * Based on:
46014  * Ext JS Library 1.1.1
46015  * Copyright(c) 2006-2007, Ext JS, LLC.
46016  *
46017  * Originally Released Under LGPL - original licence link has changed is not relivant.
46018  *
46019  * Fork - LGPL
46020  * <script type="text/javascript">
46021  */
46022
46023 // as we use this in bootstrap.
46024 Roo.namespace('Roo.form');
46025  /**
46026  * @class Roo.form.Action
46027  * Internal Class used to handle form actions
46028  * @constructor
46029  * @param {Roo.form.BasicForm} el The form element or its id
46030  * @param {Object} config Configuration options
46031  */
46032
46033  
46034  
46035 // define the action interface
46036 Roo.form.Action = function(form, options){
46037     this.form = form;
46038     this.options = options || {};
46039 };
46040 /**
46041  * Client Validation Failed
46042  * @const 
46043  */
46044 Roo.form.Action.CLIENT_INVALID = 'client';
46045 /**
46046  * Server Validation Failed
46047  * @const 
46048  */
46049 Roo.form.Action.SERVER_INVALID = 'server';
46050  /**
46051  * Connect to Server Failed
46052  * @const 
46053  */
46054 Roo.form.Action.CONNECT_FAILURE = 'connect';
46055 /**
46056  * Reading Data from Server Failed
46057  * @const 
46058  */
46059 Roo.form.Action.LOAD_FAILURE = 'load';
46060
46061 Roo.form.Action.prototype = {
46062     type : 'default',
46063     failureType : undefined,
46064     response : undefined,
46065     result : undefined,
46066
46067     // interface method
46068     run : function(options){
46069
46070     },
46071
46072     // interface method
46073     success : function(response){
46074
46075     },
46076
46077     // interface method
46078     handleResponse : function(response){
46079
46080     },
46081
46082     // default connection failure
46083     failure : function(response){
46084         
46085         this.response = response;
46086         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46087         this.form.afterAction(this, false);
46088     },
46089
46090     processResponse : function(response){
46091         this.response = response;
46092         if(!response.responseText){
46093             return true;
46094         }
46095         this.result = this.handleResponse(response);
46096         return this.result;
46097     },
46098
46099     // utility functions used internally
46100     getUrl : function(appendParams){
46101         var url = this.options.url || this.form.url || this.form.el.dom.action;
46102         if(appendParams){
46103             var p = this.getParams();
46104             if(p){
46105                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46106             }
46107         }
46108         return url;
46109     },
46110
46111     getMethod : function(){
46112         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46113     },
46114
46115     getParams : function(){
46116         var bp = this.form.baseParams;
46117         var p = this.options.params;
46118         if(p){
46119             if(typeof p == "object"){
46120                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46121             }else if(typeof p == 'string' && bp){
46122                 p += '&' + Roo.urlEncode(bp);
46123             }
46124         }else if(bp){
46125             p = Roo.urlEncode(bp);
46126         }
46127         return p;
46128     },
46129
46130     createCallback : function(){
46131         return {
46132             success: this.success,
46133             failure: this.failure,
46134             scope: this,
46135             timeout: (this.form.timeout*1000),
46136             upload: this.form.fileUpload ? this.success : undefined
46137         };
46138     }
46139 };
46140
46141 Roo.form.Action.Submit = function(form, options){
46142     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46143 };
46144
46145 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46146     type : 'submit',
46147
46148     haveProgress : false,
46149     uploadComplete : false,
46150     
46151     // uploadProgress indicator.
46152     uploadProgress : function()
46153     {
46154         if (!this.form.progressUrl) {
46155             return;
46156         }
46157         
46158         if (!this.haveProgress) {
46159             Roo.MessageBox.progress("Uploading", "Uploading");
46160         }
46161         if (this.uploadComplete) {
46162            Roo.MessageBox.hide();
46163            return;
46164         }
46165         
46166         this.haveProgress = true;
46167    
46168         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46169         
46170         var c = new Roo.data.Connection();
46171         c.request({
46172             url : this.form.progressUrl,
46173             params: {
46174                 id : uid
46175             },
46176             method: 'GET',
46177             success : function(req){
46178                //console.log(data);
46179                 var rdata = false;
46180                 var edata;
46181                 try  {
46182                    rdata = Roo.decode(req.responseText)
46183                 } catch (e) {
46184                     Roo.log("Invalid data from server..");
46185                     Roo.log(edata);
46186                     return;
46187                 }
46188                 if (!rdata || !rdata.success) {
46189                     Roo.log(rdata);
46190                     Roo.MessageBox.alert(Roo.encode(rdata));
46191                     return;
46192                 }
46193                 var data = rdata.data;
46194                 
46195                 if (this.uploadComplete) {
46196                    Roo.MessageBox.hide();
46197                    return;
46198                 }
46199                    
46200                 if (data){
46201                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46202                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46203                     );
46204                 }
46205                 this.uploadProgress.defer(2000,this);
46206             },
46207        
46208             failure: function(data) {
46209                 Roo.log('progress url failed ');
46210                 Roo.log(data);
46211             },
46212             scope : this
46213         });
46214            
46215     },
46216     
46217     
46218     run : function()
46219     {
46220         // run get Values on the form, so it syncs any secondary forms.
46221         this.form.getValues();
46222         
46223         var o = this.options;
46224         var method = this.getMethod();
46225         var isPost = method == 'POST';
46226         if(o.clientValidation === false || this.form.isValid()){
46227             
46228             if (this.form.progressUrl) {
46229                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46230                     (new Date() * 1) + '' + Math.random());
46231                     
46232             } 
46233             
46234             
46235             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46236                 form:this.form.el.dom,
46237                 url:this.getUrl(!isPost),
46238                 method: method,
46239                 params:isPost ? this.getParams() : null,
46240                 isUpload: this.form.fileUpload
46241             }));
46242             
46243             this.uploadProgress();
46244
46245         }else if (o.clientValidation !== false){ // client validation failed
46246             this.failureType = Roo.form.Action.CLIENT_INVALID;
46247             this.form.afterAction(this, false);
46248         }
46249     },
46250
46251     success : function(response)
46252     {
46253         this.uploadComplete= true;
46254         if (this.haveProgress) {
46255             Roo.MessageBox.hide();
46256         }
46257         
46258         
46259         var result = this.processResponse(response);
46260         if(result === true || result.success){
46261             this.form.afterAction(this, true);
46262             return;
46263         }
46264         if(result.errors){
46265             this.form.markInvalid(result.errors);
46266             this.failureType = Roo.form.Action.SERVER_INVALID;
46267         }
46268         this.form.afterAction(this, false);
46269     },
46270     failure : function(response)
46271     {
46272         this.uploadComplete= true;
46273         if (this.haveProgress) {
46274             Roo.MessageBox.hide();
46275         }
46276         
46277         this.response = response;
46278         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46279         this.form.afterAction(this, false);
46280     },
46281     
46282     handleResponse : function(response){
46283         if(this.form.errorReader){
46284             var rs = this.form.errorReader.read(response);
46285             var errors = [];
46286             if(rs.records){
46287                 for(var i = 0, len = rs.records.length; i < len; i++) {
46288                     var r = rs.records[i];
46289                     errors[i] = r.data;
46290                 }
46291             }
46292             if(errors.length < 1){
46293                 errors = null;
46294             }
46295             return {
46296                 success : rs.success,
46297                 errors : errors
46298             };
46299         }
46300         var ret = false;
46301         try {
46302             ret = Roo.decode(response.responseText);
46303         } catch (e) {
46304             ret = {
46305                 success: false,
46306                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46307                 errors : []
46308             };
46309         }
46310         return ret;
46311         
46312     }
46313 });
46314
46315
46316 Roo.form.Action.Load = function(form, options){
46317     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46318     this.reader = this.form.reader;
46319 };
46320
46321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46322     type : 'load',
46323
46324     run : function(){
46325         
46326         Roo.Ajax.request(Roo.apply(
46327                 this.createCallback(), {
46328                     method:this.getMethod(),
46329                     url:this.getUrl(false),
46330                     params:this.getParams()
46331         }));
46332     },
46333
46334     success : function(response){
46335         
46336         var result = this.processResponse(response);
46337         if(result === true || !result.success || !result.data){
46338             this.failureType = Roo.form.Action.LOAD_FAILURE;
46339             this.form.afterAction(this, false);
46340             return;
46341         }
46342         this.form.clearInvalid();
46343         this.form.setValues(result.data);
46344         this.form.afterAction(this, true);
46345     },
46346
46347     handleResponse : function(response){
46348         if(this.form.reader){
46349             var rs = this.form.reader.read(response);
46350             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46351             return {
46352                 success : rs.success,
46353                 data : data
46354             };
46355         }
46356         return Roo.decode(response.responseText);
46357     }
46358 });
46359
46360 Roo.form.Action.ACTION_TYPES = {
46361     'load' : Roo.form.Action.Load,
46362     'submit' : Roo.form.Action.Submit
46363 };/*
46364  * Based on:
46365  * Ext JS Library 1.1.1
46366  * Copyright(c) 2006-2007, Ext JS, LLC.
46367  *
46368  * Originally Released Under LGPL - original licence link has changed is not relivant.
46369  *
46370  * Fork - LGPL
46371  * <script type="text/javascript">
46372  */
46373  
46374 /**
46375  * @class Roo.form.Layout
46376  * @extends Roo.Component
46377  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46378  * @constructor
46379  * @param {Object} config Configuration options
46380  */
46381 Roo.form.Layout = function(config){
46382     var xitems = [];
46383     if (config.items) {
46384         xitems = config.items;
46385         delete config.items;
46386     }
46387     Roo.form.Layout.superclass.constructor.call(this, config);
46388     this.stack = [];
46389     Roo.each(xitems, this.addxtype, this);
46390      
46391 };
46392
46393 Roo.extend(Roo.form.Layout, Roo.Component, {
46394     /**
46395      * @cfg {String/Object} autoCreate
46396      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46397      */
46398     /**
46399      * @cfg {String/Object/Function} style
46400      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46401      * a function which returns such a specification.
46402      */
46403     /**
46404      * @cfg {String} labelAlign
46405      * Valid values are "left," "top" and "right" (defaults to "left")
46406      */
46407     /**
46408      * @cfg {Number} labelWidth
46409      * Fixed width in pixels of all field labels (defaults to undefined)
46410      */
46411     /**
46412      * @cfg {Boolean} clear
46413      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46414      */
46415     clear : true,
46416     /**
46417      * @cfg {String} labelSeparator
46418      * The separator to use after field labels (defaults to ':')
46419      */
46420     labelSeparator : ':',
46421     /**
46422      * @cfg {Boolean} hideLabels
46423      * True to suppress the display of field labels in this layout (defaults to false)
46424      */
46425     hideLabels : false,
46426
46427     // private
46428     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46429     
46430     isLayout : true,
46431     
46432     // private
46433     onRender : function(ct, position){
46434         if(this.el){ // from markup
46435             this.el = Roo.get(this.el);
46436         }else {  // generate
46437             var cfg = this.getAutoCreate();
46438             this.el = ct.createChild(cfg, position);
46439         }
46440         if(this.style){
46441             this.el.applyStyles(this.style);
46442         }
46443         if(this.labelAlign){
46444             this.el.addClass('x-form-label-'+this.labelAlign);
46445         }
46446         if(this.hideLabels){
46447             this.labelStyle = "display:none";
46448             this.elementStyle = "padding-left:0;";
46449         }else{
46450             if(typeof this.labelWidth == 'number'){
46451                 this.labelStyle = "width:"+this.labelWidth+"px;";
46452                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46453             }
46454             if(this.labelAlign == 'top'){
46455                 this.labelStyle = "width:auto;";
46456                 this.elementStyle = "padding-left:0;";
46457             }
46458         }
46459         var stack = this.stack;
46460         var slen = stack.length;
46461         if(slen > 0){
46462             if(!this.fieldTpl){
46463                 var t = new Roo.Template(
46464                     '<div class="x-form-item {5}">',
46465                         '<label for="{0}" style="{2}">{1}{4}</label>',
46466                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46467                         '</div>',
46468                     '</div><div class="x-form-clear-left"></div>'
46469                 );
46470                 t.disableFormats = true;
46471                 t.compile();
46472                 Roo.form.Layout.prototype.fieldTpl = t;
46473             }
46474             for(var i = 0; i < slen; i++) {
46475                 if(stack[i].isFormField){
46476                     this.renderField(stack[i]);
46477                 }else{
46478                     this.renderComponent(stack[i]);
46479                 }
46480             }
46481         }
46482         if(this.clear){
46483             this.el.createChild({cls:'x-form-clear'});
46484         }
46485     },
46486
46487     // private
46488     renderField : function(f){
46489         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46490                f.id, //0
46491                f.fieldLabel, //1
46492                f.labelStyle||this.labelStyle||'', //2
46493                this.elementStyle||'', //3
46494                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46495                f.itemCls||this.itemCls||''  //5
46496        ], true).getPrevSibling());
46497     },
46498
46499     // private
46500     renderComponent : function(c){
46501         c.render(c.isLayout ? this.el : this.el.createChild());    
46502     },
46503     /**
46504      * Adds a object form elements (using the xtype property as the factory method.)
46505      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46506      * @param {Object} config 
46507      */
46508     addxtype : function(o)
46509     {
46510         // create the lement.
46511         o.form = this.form;
46512         var fe = Roo.factory(o, Roo.form);
46513         this.form.allItems.push(fe);
46514         this.stack.push(fe);
46515         
46516         if (fe.isFormField) {
46517             this.form.items.add(fe);
46518         }
46519          
46520         return fe;
46521     }
46522 });
46523
46524 /**
46525  * @class Roo.form.Column
46526  * @extends Roo.form.Layout
46527  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46528  * @constructor
46529  * @param {Object} config Configuration options
46530  */
46531 Roo.form.Column = function(config){
46532     Roo.form.Column.superclass.constructor.call(this, config);
46533 };
46534
46535 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46536     /**
46537      * @cfg {Number/String} width
46538      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46539      */
46540     /**
46541      * @cfg {String/Object} autoCreate
46542      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46543      */
46544
46545     // private
46546     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46547
46548     // private
46549     onRender : function(ct, position){
46550         Roo.form.Column.superclass.onRender.call(this, ct, position);
46551         if(this.width){
46552             this.el.setWidth(this.width);
46553         }
46554     }
46555 });
46556
46557
46558 /**
46559  * @class Roo.form.Row
46560  * @extends Roo.form.Layout
46561  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46562  * @constructor
46563  * @param {Object} config Configuration options
46564  */
46565
46566  
46567 Roo.form.Row = function(config){
46568     Roo.form.Row.superclass.constructor.call(this, config);
46569 };
46570  
46571 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46572       /**
46573      * @cfg {Number/String} width
46574      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46575      */
46576     /**
46577      * @cfg {Number/String} height
46578      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46579      */
46580     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46581     
46582     padWidth : 20,
46583     // private
46584     onRender : function(ct, position){
46585         //console.log('row render');
46586         if(!this.rowTpl){
46587             var t = new Roo.Template(
46588                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46589                     '<label for="{0}" style="{2}">{1}{4}</label>',
46590                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46591                     '</div>',
46592                 '</div>'
46593             );
46594             t.disableFormats = true;
46595             t.compile();
46596             Roo.form.Layout.prototype.rowTpl = t;
46597         }
46598         this.fieldTpl = this.rowTpl;
46599         
46600         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46601         var labelWidth = 100;
46602         
46603         if ((this.labelAlign != 'top')) {
46604             if (typeof this.labelWidth == 'number') {
46605                 labelWidth = this.labelWidth
46606             }
46607             this.padWidth =  20 + labelWidth;
46608             
46609         }
46610         
46611         Roo.form.Column.superclass.onRender.call(this, ct, position);
46612         if(this.width){
46613             this.el.setWidth(this.width);
46614         }
46615         if(this.height){
46616             this.el.setHeight(this.height);
46617         }
46618     },
46619     
46620     // private
46621     renderField : function(f){
46622         f.fieldEl = this.fieldTpl.append(this.el, [
46623                f.id, f.fieldLabel,
46624                f.labelStyle||this.labelStyle||'',
46625                this.elementStyle||'',
46626                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46627                f.itemCls||this.itemCls||'',
46628                f.width ? f.width + this.padWidth : 160 + this.padWidth
46629        ],true);
46630     }
46631 });
46632  
46633
46634 /**
46635  * @class Roo.form.FieldSet
46636  * @extends Roo.form.Layout
46637  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46638  * @constructor
46639  * @param {Object} config Configuration options
46640  */
46641 Roo.form.FieldSet = function(config){
46642     Roo.form.FieldSet.superclass.constructor.call(this, config);
46643 };
46644
46645 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46646     /**
46647      * @cfg {String} legend
46648      * The text to display as the legend for the FieldSet (defaults to '')
46649      */
46650     /**
46651      * @cfg {String/Object} autoCreate
46652      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46653      */
46654
46655     // private
46656     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46657
46658     // private
46659     onRender : function(ct, position){
46660         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46661         if(this.legend){
46662             this.setLegend(this.legend);
46663         }
46664     },
46665
46666     // private
46667     setLegend : function(text){
46668         if(this.rendered){
46669             this.el.child('legend').update(text);
46670         }
46671     }
46672 });/*
46673  * Based on:
46674  * Ext JS Library 1.1.1
46675  * Copyright(c) 2006-2007, Ext JS, LLC.
46676  *
46677  * Originally Released Under LGPL - original licence link has changed is not relivant.
46678  *
46679  * Fork - LGPL
46680  * <script type="text/javascript">
46681  */
46682 /**
46683  * @class Roo.form.VTypes
46684  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46685  * @singleton
46686  */
46687 Roo.form.VTypes = function(){
46688     // closure these in so they are only created once.
46689     var alpha = /^[a-zA-Z_]+$/;
46690     var alphanum = /^[a-zA-Z0-9_]+$/;
46691     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46692     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46693
46694     // All these messages and functions are configurable
46695     return {
46696         /**
46697          * The function used to validate email addresses
46698          * @param {String} value The email address
46699          */
46700         'email' : function(v){
46701             return email.test(v);
46702         },
46703         /**
46704          * The error text to display when the email validation function returns false
46705          * @type String
46706          */
46707         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46708         /**
46709          * The keystroke filter mask to be applied on email input
46710          * @type RegExp
46711          */
46712         'emailMask' : /[a-z0-9_\.\-@]/i,
46713
46714         /**
46715          * The function used to validate URLs
46716          * @param {String} value The URL
46717          */
46718         'url' : function(v){
46719             return url.test(v);
46720         },
46721         /**
46722          * The error text to display when the url validation function returns false
46723          * @type String
46724          */
46725         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46726         
46727         /**
46728          * The function used to validate alpha values
46729          * @param {String} value The value
46730          */
46731         'alpha' : function(v){
46732             return alpha.test(v);
46733         },
46734         /**
46735          * The error text to display when the alpha validation function returns false
46736          * @type String
46737          */
46738         'alphaText' : 'This field should only contain letters and _',
46739         /**
46740          * The keystroke filter mask to be applied on alpha input
46741          * @type RegExp
46742          */
46743         'alphaMask' : /[a-z_]/i,
46744
46745         /**
46746          * The function used to validate alphanumeric values
46747          * @param {String} value The value
46748          */
46749         'alphanum' : function(v){
46750             return alphanum.test(v);
46751         },
46752         /**
46753          * The error text to display when the alphanumeric validation function returns false
46754          * @type String
46755          */
46756         'alphanumText' : 'This field should only contain letters, numbers and _',
46757         /**
46758          * The keystroke filter mask to be applied on alphanumeric input
46759          * @type RegExp
46760          */
46761         'alphanumMask' : /[a-z0-9_]/i
46762     };
46763 }();//<script type="text/javascript">
46764
46765 /**
46766  * @class Roo.form.FCKeditor
46767  * @extends Roo.form.TextArea
46768  * Wrapper around the FCKEditor http://www.fckeditor.net
46769  * @constructor
46770  * Creates a new FCKeditor
46771  * @param {Object} config Configuration options
46772  */
46773 Roo.form.FCKeditor = function(config){
46774     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46775     this.addEvents({
46776          /**
46777          * @event editorinit
46778          * Fired when the editor is initialized - you can add extra handlers here..
46779          * @param {FCKeditor} this
46780          * @param {Object} the FCK object.
46781          */
46782         editorinit : true
46783     });
46784     
46785     
46786 };
46787 Roo.form.FCKeditor.editors = { };
46788 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46789 {
46790     //defaultAutoCreate : {
46791     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46792     //},
46793     // private
46794     /**
46795      * @cfg {Object} fck options - see fck manual for details.
46796      */
46797     fckconfig : false,
46798     
46799     /**
46800      * @cfg {Object} fck toolbar set (Basic or Default)
46801      */
46802     toolbarSet : 'Basic',
46803     /**
46804      * @cfg {Object} fck BasePath
46805      */ 
46806     basePath : '/fckeditor/',
46807     
46808     
46809     frame : false,
46810     
46811     value : '',
46812     
46813    
46814     onRender : function(ct, position)
46815     {
46816         if(!this.el){
46817             this.defaultAutoCreate = {
46818                 tag: "textarea",
46819                 style:"width:300px;height:60px;",
46820                 autocomplete: "new-password"
46821             };
46822         }
46823         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46824         /*
46825         if(this.grow){
46826             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46827             if(this.preventScrollbars){
46828                 this.el.setStyle("overflow", "hidden");
46829             }
46830             this.el.setHeight(this.growMin);
46831         }
46832         */
46833         //console.log('onrender' + this.getId() );
46834         Roo.form.FCKeditor.editors[this.getId()] = this;
46835          
46836
46837         this.replaceTextarea() ;
46838         
46839     },
46840     
46841     getEditor : function() {
46842         return this.fckEditor;
46843     },
46844     /**
46845      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46846      * @param {Mixed} value The value to set
46847      */
46848     
46849     
46850     setValue : function(value)
46851     {
46852         //console.log('setValue: ' + value);
46853         
46854         if(typeof(value) == 'undefined') { // not sure why this is happending...
46855             return;
46856         }
46857         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46858         
46859         //if(!this.el || !this.getEditor()) {
46860         //    this.value = value;
46861             //this.setValue.defer(100,this,[value]);    
46862         //    return;
46863         //} 
46864         
46865         if(!this.getEditor()) {
46866             return;
46867         }
46868         
46869         this.getEditor().SetData(value);
46870         
46871         //
46872
46873     },
46874
46875     /**
46876      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46877      * @return {Mixed} value The field value
46878      */
46879     getValue : function()
46880     {
46881         
46882         if (this.frame && this.frame.dom.style.display == 'none') {
46883             return Roo.form.FCKeditor.superclass.getValue.call(this);
46884         }
46885         
46886         if(!this.el || !this.getEditor()) {
46887            
46888            // this.getValue.defer(100,this); 
46889             return this.value;
46890         }
46891        
46892         
46893         var value=this.getEditor().GetData();
46894         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46895         return Roo.form.FCKeditor.superclass.getValue.call(this);
46896         
46897
46898     },
46899
46900     /**
46901      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46902      * @return {Mixed} value The field value
46903      */
46904     getRawValue : function()
46905     {
46906         if (this.frame && this.frame.dom.style.display == 'none') {
46907             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46908         }
46909         
46910         if(!this.el || !this.getEditor()) {
46911             //this.getRawValue.defer(100,this); 
46912             return this.value;
46913             return;
46914         }
46915         
46916         
46917         
46918         var value=this.getEditor().GetData();
46919         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46920         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46921          
46922     },
46923     
46924     setSize : function(w,h) {
46925         
46926         
46927         
46928         //if (this.frame && this.frame.dom.style.display == 'none') {
46929         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46930         //    return;
46931         //}
46932         //if(!this.el || !this.getEditor()) {
46933         //    this.setSize.defer(100,this, [w,h]); 
46934         //    return;
46935         //}
46936         
46937         
46938         
46939         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46940         
46941         this.frame.dom.setAttribute('width', w);
46942         this.frame.dom.setAttribute('height', h);
46943         this.frame.setSize(w,h);
46944         
46945     },
46946     
46947     toggleSourceEdit : function(value) {
46948         
46949       
46950          
46951         this.el.dom.style.display = value ? '' : 'none';
46952         this.frame.dom.style.display = value ?  'none' : '';
46953         
46954     },
46955     
46956     
46957     focus: function(tag)
46958     {
46959         if (this.frame.dom.style.display == 'none') {
46960             return Roo.form.FCKeditor.superclass.focus.call(this);
46961         }
46962         if(!this.el || !this.getEditor()) {
46963             this.focus.defer(100,this, [tag]); 
46964             return;
46965         }
46966         
46967         
46968         
46969         
46970         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46971         this.getEditor().Focus();
46972         if (tgs.length) {
46973             if (!this.getEditor().Selection.GetSelection()) {
46974                 this.focus.defer(100,this, [tag]); 
46975                 return;
46976             }
46977             
46978             
46979             var r = this.getEditor().EditorDocument.createRange();
46980             r.setStart(tgs[0],0);
46981             r.setEnd(tgs[0],0);
46982             this.getEditor().Selection.GetSelection().removeAllRanges();
46983             this.getEditor().Selection.GetSelection().addRange(r);
46984             this.getEditor().Focus();
46985         }
46986         
46987     },
46988     
46989     
46990     
46991     replaceTextarea : function()
46992     {
46993         if ( document.getElementById( this.getId() + '___Frame' ) )
46994             return ;
46995         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46996         //{
46997             // We must check the elements firstly using the Id and then the name.
46998         var oTextarea = document.getElementById( this.getId() );
46999         
47000         var colElementsByName = document.getElementsByName( this.getId() ) ;
47001          
47002         oTextarea.style.display = 'none' ;
47003
47004         if ( oTextarea.tabIndex ) {            
47005             this.TabIndex = oTextarea.tabIndex ;
47006         }
47007         
47008         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47009         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47010         this.frame = Roo.get(this.getId() + '___Frame')
47011     },
47012     
47013     _getConfigHtml : function()
47014     {
47015         var sConfig = '' ;
47016
47017         for ( var o in this.fckconfig ) {
47018             sConfig += sConfig.length > 0  ? '&amp;' : '';
47019             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47020         }
47021
47022         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47023     },
47024     
47025     
47026     _getIFrameHtml : function()
47027     {
47028         var sFile = 'fckeditor.html' ;
47029         /* no idea what this is about..
47030         try
47031         {
47032             if ( (/fcksource=true/i).test( window.top.location.search ) )
47033                 sFile = 'fckeditor.original.html' ;
47034         }
47035         catch (e) { 
47036         */
47037
47038         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47039         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47040         
47041         
47042         var html = '<iframe id="' + this.getId() +
47043             '___Frame" src="' + sLink +
47044             '" width="' + this.width +
47045             '" height="' + this.height + '"' +
47046             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47047             ' frameborder="0" scrolling="no"></iframe>' ;
47048
47049         return html ;
47050     },
47051     
47052     _insertHtmlBefore : function( html, element )
47053     {
47054         if ( element.insertAdjacentHTML )       {
47055             // IE
47056             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47057         } else { // Gecko
47058             var oRange = document.createRange() ;
47059             oRange.setStartBefore( element ) ;
47060             var oFragment = oRange.createContextualFragment( html );
47061             element.parentNode.insertBefore( oFragment, element ) ;
47062         }
47063     }
47064     
47065     
47066   
47067     
47068     
47069     
47070     
47071
47072 });
47073
47074 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47075
47076 function FCKeditor_OnComplete(editorInstance){
47077     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47078     f.fckEditor = editorInstance;
47079     //console.log("loaded");
47080     f.fireEvent('editorinit', f, editorInstance);
47081
47082   
47083
47084  
47085
47086
47087
47088
47089
47090
47091
47092
47093
47094
47095
47096
47097
47098
47099
47100 //<script type="text/javascript">
47101 /**
47102  * @class Roo.form.GridField
47103  * @extends Roo.form.Field
47104  * Embed a grid (or editable grid into a form)
47105  * STATUS ALPHA
47106  * 
47107  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47108  * it needs 
47109  * xgrid.store = Roo.data.Store
47110  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47111  * xgrid.store.reader = Roo.data.JsonReader 
47112  * 
47113  * 
47114  * @constructor
47115  * Creates a new GridField
47116  * @param {Object} config Configuration options
47117  */
47118 Roo.form.GridField = function(config){
47119     Roo.form.GridField.superclass.constructor.call(this, config);
47120      
47121 };
47122
47123 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47124     /**
47125      * @cfg {Number} width  - used to restrict width of grid..
47126      */
47127     width : 100,
47128     /**
47129      * @cfg {Number} height - used to restrict height of grid..
47130      */
47131     height : 50,
47132      /**
47133      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47134          * 
47135          *}
47136      */
47137     xgrid : false, 
47138     /**
47139      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47140      * {tag: "input", type: "checkbox", autocomplete: "off"})
47141      */
47142    // defaultAutoCreate : { tag: 'div' },
47143     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47144     /**
47145      * @cfg {String} addTitle Text to include for adding a title.
47146      */
47147     addTitle : false,
47148     //
47149     onResize : function(){
47150         Roo.form.Field.superclass.onResize.apply(this, arguments);
47151     },
47152
47153     initEvents : function(){
47154         // Roo.form.Checkbox.superclass.initEvents.call(this);
47155         // has no events...
47156        
47157     },
47158
47159
47160     getResizeEl : function(){
47161         return this.wrap;
47162     },
47163
47164     getPositionEl : function(){
47165         return this.wrap;
47166     },
47167
47168     // private
47169     onRender : function(ct, position){
47170         
47171         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47172         var style = this.style;
47173         delete this.style;
47174         
47175         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47176         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47177         this.viewEl = this.wrap.createChild({ tag: 'div' });
47178         if (style) {
47179             this.viewEl.applyStyles(style);
47180         }
47181         if (this.width) {
47182             this.viewEl.setWidth(this.width);
47183         }
47184         if (this.height) {
47185             this.viewEl.setHeight(this.height);
47186         }
47187         //if(this.inputValue !== undefined){
47188         //this.setValue(this.value);
47189         
47190         
47191         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47192         
47193         
47194         this.grid.render();
47195         this.grid.getDataSource().on('remove', this.refreshValue, this);
47196         this.grid.getDataSource().on('update', this.refreshValue, this);
47197         this.grid.on('afteredit', this.refreshValue, this);
47198  
47199     },
47200      
47201     
47202     /**
47203      * Sets the value of the item. 
47204      * @param {String} either an object  or a string..
47205      */
47206     setValue : function(v){
47207         //this.value = v;
47208         v = v || []; // empty set..
47209         // this does not seem smart - it really only affects memoryproxy grids..
47210         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47211             var ds = this.grid.getDataSource();
47212             // assumes a json reader..
47213             var data = {}
47214             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47215             ds.loadData( data);
47216         }
47217         // clear selection so it does not get stale.
47218         if (this.grid.sm) { 
47219             this.grid.sm.clearSelections();
47220         }
47221         
47222         Roo.form.GridField.superclass.setValue.call(this, v);
47223         this.refreshValue();
47224         // should load data in the grid really....
47225     },
47226     
47227     // private
47228     refreshValue: function() {
47229          var val = [];
47230         this.grid.getDataSource().each(function(r) {
47231             val.push(r.data);
47232         });
47233         this.el.dom.value = Roo.encode(val);
47234     }
47235     
47236      
47237     
47238     
47239 });/*
47240  * Based on:
47241  * Ext JS Library 1.1.1
47242  * Copyright(c) 2006-2007, Ext JS, LLC.
47243  *
47244  * Originally Released Under LGPL - original licence link has changed is not relivant.
47245  *
47246  * Fork - LGPL
47247  * <script type="text/javascript">
47248  */
47249 /**
47250  * @class Roo.form.DisplayField
47251  * @extends Roo.form.Field
47252  * A generic Field to display non-editable data.
47253  * @constructor
47254  * Creates a new Display Field item.
47255  * @param {Object} config Configuration options
47256  */
47257 Roo.form.DisplayField = function(config){
47258     Roo.form.DisplayField.superclass.constructor.call(this, config);
47259     
47260 };
47261
47262 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47263     inputType:      'hidden',
47264     allowBlank:     true,
47265     readOnly:         true,
47266     
47267  
47268     /**
47269      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47270      */
47271     focusClass : undefined,
47272     /**
47273      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47274      */
47275     fieldClass: 'x-form-field',
47276     
47277      /**
47278      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47279      */
47280     valueRenderer: undefined,
47281     
47282     width: 100,
47283     /**
47284      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47285      * {tag: "input", type: "checkbox", autocomplete: "off"})
47286      */
47287      
47288  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47289
47290     onResize : function(){
47291         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47292         
47293     },
47294
47295     initEvents : function(){
47296         // Roo.form.Checkbox.superclass.initEvents.call(this);
47297         // has no events...
47298        
47299     },
47300
47301
47302     getResizeEl : function(){
47303         return this.wrap;
47304     },
47305
47306     getPositionEl : function(){
47307         return this.wrap;
47308     },
47309
47310     // private
47311     onRender : function(ct, position){
47312         
47313         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47314         //if(this.inputValue !== undefined){
47315         this.wrap = this.el.wrap();
47316         
47317         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47318         
47319         if (this.bodyStyle) {
47320             this.viewEl.applyStyles(this.bodyStyle);
47321         }
47322         //this.viewEl.setStyle('padding', '2px');
47323         
47324         this.setValue(this.value);
47325         
47326     },
47327 /*
47328     // private
47329     initValue : Roo.emptyFn,
47330
47331   */
47332
47333         // private
47334     onClick : function(){
47335         
47336     },
47337
47338     /**
47339      * Sets the checked state of the checkbox.
47340      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47341      */
47342     setValue : function(v){
47343         this.value = v;
47344         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47345         // this might be called before we have a dom element..
47346         if (!this.viewEl) {
47347             return;
47348         }
47349         this.viewEl.dom.innerHTML = html;
47350         Roo.form.DisplayField.superclass.setValue.call(this, v);
47351
47352     }
47353 });/*
47354  * 
47355  * Licence- LGPL
47356  * 
47357  */
47358
47359 /**
47360  * @class Roo.form.DayPicker
47361  * @extends Roo.form.Field
47362  * A Day picker show [M] [T] [W] ....
47363  * @constructor
47364  * Creates a new Day Picker
47365  * @param {Object} config Configuration options
47366  */
47367 Roo.form.DayPicker= function(config){
47368     Roo.form.DayPicker.superclass.constructor.call(this, config);
47369      
47370 };
47371
47372 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47373     /**
47374      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47375      */
47376     focusClass : undefined,
47377     /**
47378      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47379      */
47380     fieldClass: "x-form-field",
47381    
47382     /**
47383      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47384      * {tag: "input", type: "checkbox", autocomplete: "off"})
47385      */
47386     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47387     
47388    
47389     actionMode : 'viewEl', 
47390     //
47391     // private
47392  
47393     inputType : 'hidden',
47394     
47395      
47396     inputElement: false, // real input element?
47397     basedOn: false, // ????
47398     
47399     isFormField: true, // not sure where this is needed!!!!
47400
47401     onResize : function(){
47402         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47403         if(!this.boxLabel){
47404             this.el.alignTo(this.wrap, 'c-c');
47405         }
47406     },
47407
47408     initEvents : function(){
47409         Roo.form.Checkbox.superclass.initEvents.call(this);
47410         this.el.on("click", this.onClick,  this);
47411         this.el.on("change", this.onClick,  this);
47412     },
47413
47414
47415     getResizeEl : function(){
47416         return this.wrap;
47417     },
47418
47419     getPositionEl : function(){
47420         return this.wrap;
47421     },
47422
47423     
47424     // private
47425     onRender : function(ct, position){
47426         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47427        
47428         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47429         
47430         var r1 = '<table><tr>';
47431         var r2 = '<tr class="x-form-daypick-icons">';
47432         for (var i=0; i < 7; i++) {
47433             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47434             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47435         }
47436         
47437         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47438         viewEl.select('img').on('click', this.onClick, this);
47439         this.viewEl = viewEl;   
47440         
47441         
47442         // this will not work on Chrome!!!
47443         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47444         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47445         
47446         
47447           
47448
47449     },
47450
47451     // private
47452     initValue : Roo.emptyFn,
47453
47454     /**
47455      * Returns the checked state of the checkbox.
47456      * @return {Boolean} True if checked, else false
47457      */
47458     getValue : function(){
47459         return this.el.dom.value;
47460         
47461     },
47462
47463         // private
47464     onClick : function(e){ 
47465         //this.setChecked(!this.checked);
47466         Roo.get(e.target).toggleClass('x-menu-item-checked');
47467         this.refreshValue();
47468         //if(this.el.dom.checked != this.checked){
47469         //    this.setValue(this.el.dom.checked);
47470        // }
47471     },
47472     
47473     // private
47474     refreshValue : function()
47475     {
47476         var val = '';
47477         this.viewEl.select('img',true).each(function(e,i,n)  {
47478             val += e.is(".x-menu-item-checked") ? String(n) : '';
47479         });
47480         this.setValue(val, true);
47481     },
47482
47483     /**
47484      * Sets the checked state of the checkbox.
47485      * On is always based on a string comparison between inputValue and the param.
47486      * @param {Boolean/String} value - the value to set 
47487      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47488      */
47489     setValue : function(v,suppressEvent){
47490         if (!this.el.dom) {
47491             return;
47492         }
47493         var old = this.el.dom.value ;
47494         this.el.dom.value = v;
47495         if (suppressEvent) {
47496             return ;
47497         }
47498          
47499         // update display..
47500         this.viewEl.select('img',true).each(function(e,i,n)  {
47501             
47502             var on = e.is(".x-menu-item-checked");
47503             var newv = v.indexOf(String(n)) > -1;
47504             if (on != newv) {
47505                 e.toggleClass('x-menu-item-checked');
47506             }
47507             
47508         });
47509         
47510         
47511         this.fireEvent('change', this, v, old);
47512         
47513         
47514     },
47515    
47516     // handle setting of hidden value by some other method!!?!?
47517     setFromHidden: function()
47518     {
47519         if(!this.el){
47520             return;
47521         }
47522         //console.log("SET FROM HIDDEN");
47523         //alert('setFrom hidden');
47524         this.setValue(this.el.dom.value);
47525     },
47526     
47527     onDestroy : function()
47528     {
47529         if(this.viewEl){
47530             Roo.get(this.viewEl).remove();
47531         }
47532          
47533         Roo.form.DayPicker.superclass.onDestroy.call(this);
47534     }
47535
47536 });/*
47537  * RooJS Library 1.1.1
47538  * Copyright(c) 2008-2011  Alan Knowles
47539  *
47540  * License - LGPL
47541  */
47542  
47543
47544 /**
47545  * @class Roo.form.ComboCheck
47546  * @extends Roo.form.ComboBox
47547  * A combobox for multiple select items.
47548  *
47549  * FIXME - could do with a reset button..
47550  * 
47551  * @constructor
47552  * Create a new ComboCheck
47553  * @param {Object} config Configuration options
47554  */
47555 Roo.form.ComboCheck = function(config){
47556     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47557     // should verify some data...
47558     // like
47559     // hiddenName = required..
47560     // displayField = required
47561     // valudField == required
47562     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47563     var _t = this;
47564     Roo.each(req, function(e) {
47565         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47566             throw "Roo.form.ComboCheck : missing value for: " + e;
47567         }
47568     });
47569     
47570     
47571 };
47572
47573 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47574      
47575      
47576     editable : false,
47577      
47578     selectedClass: 'x-menu-item-checked', 
47579     
47580     // private
47581     onRender : function(ct, position){
47582         var _t = this;
47583         
47584         
47585         
47586         if(!this.tpl){
47587             var cls = 'x-combo-list';
47588
47589             
47590             this.tpl =  new Roo.Template({
47591                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47592                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47593                    '<span>{' + this.displayField + '}</span>' +
47594                     '</div>' 
47595                 
47596             });
47597         }
47598  
47599         
47600         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47601         this.view.singleSelect = false;
47602         this.view.multiSelect = true;
47603         this.view.toggleSelect = true;
47604         this.pageTb.add(new Roo.Toolbar.Fill(), {
47605             
47606             text: 'Done',
47607             handler: function()
47608             {
47609                 _t.collapse();
47610             }
47611         });
47612     },
47613     
47614     onViewOver : function(e, t){
47615         // do nothing...
47616         return;
47617         
47618     },
47619     
47620     onViewClick : function(doFocus,index){
47621         return;
47622         
47623     },
47624     select: function () {
47625         //Roo.log("SELECT CALLED");
47626     },
47627      
47628     selectByValue : function(xv, scrollIntoView){
47629         var ar = this.getValueArray();
47630         var sels = [];
47631         
47632         Roo.each(ar, function(v) {
47633             if(v === undefined || v === null){
47634                 return;
47635             }
47636             var r = this.findRecord(this.valueField, v);
47637             if(r){
47638                 sels.push(this.store.indexOf(r))
47639                 
47640             }
47641         },this);
47642         this.view.select(sels);
47643         return false;
47644     },
47645     
47646     
47647     
47648     onSelect : function(record, index){
47649        // Roo.log("onselect Called");
47650        // this is only called by the clear button now..
47651         this.view.clearSelections();
47652         this.setValue('[]');
47653         if (this.value != this.valueBefore) {
47654             this.fireEvent('change', this, this.value, this.valueBefore);
47655             this.valueBefore = this.value;
47656         }
47657     },
47658     getValueArray : function()
47659     {
47660         var ar = [] ;
47661         
47662         try {
47663             //Roo.log(this.value);
47664             if (typeof(this.value) == 'undefined') {
47665                 return [];
47666             }
47667             var ar = Roo.decode(this.value);
47668             return  ar instanceof Array ? ar : []; //?? valid?
47669             
47670         } catch(e) {
47671             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47672             return [];
47673         }
47674          
47675     },
47676     expand : function ()
47677     {
47678         
47679         Roo.form.ComboCheck.superclass.expand.call(this);
47680         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47681         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47682         
47683
47684     },
47685     
47686     collapse : function(){
47687         Roo.form.ComboCheck.superclass.collapse.call(this);
47688         var sl = this.view.getSelectedIndexes();
47689         var st = this.store;
47690         var nv = [];
47691         var tv = [];
47692         var r;
47693         Roo.each(sl, function(i) {
47694             r = st.getAt(i);
47695             nv.push(r.get(this.valueField));
47696         },this);
47697         this.setValue(Roo.encode(nv));
47698         if (this.value != this.valueBefore) {
47699
47700             this.fireEvent('change', this, this.value, this.valueBefore);
47701             this.valueBefore = this.value;
47702         }
47703         
47704     },
47705     
47706     setValue : function(v){
47707         // Roo.log(v);
47708         this.value = v;
47709         
47710         var vals = this.getValueArray();
47711         var tv = [];
47712         Roo.each(vals, function(k) {
47713             var r = this.findRecord(this.valueField, k);
47714             if(r){
47715                 tv.push(r.data[this.displayField]);
47716             }else if(this.valueNotFoundText !== undefined){
47717                 tv.push( this.valueNotFoundText );
47718             }
47719         },this);
47720        // Roo.log(tv);
47721         
47722         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47723         this.hiddenField.value = v;
47724         this.value = v;
47725     }
47726     
47727 });/*
47728  * Based on:
47729  * Ext JS Library 1.1.1
47730  * Copyright(c) 2006-2007, Ext JS, LLC.
47731  *
47732  * Originally Released Under LGPL - original licence link has changed is not relivant.
47733  *
47734  * Fork - LGPL
47735  * <script type="text/javascript">
47736  */
47737  
47738 /**
47739  * @class Roo.form.Signature
47740  * @extends Roo.form.Field
47741  * Signature field.  
47742  * @constructor
47743  * 
47744  * @param {Object} config Configuration options
47745  */
47746
47747 Roo.form.Signature = function(config){
47748     Roo.form.Signature.superclass.constructor.call(this, config);
47749     
47750     this.addEvents({// not in used??
47751          /**
47752          * @event confirm
47753          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47754              * @param {Roo.form.Signature} combo This combo box
47755              */
47756         'confirm' : true,
47757         /**
47758          * @event reset
47759          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47760              * @param {Roo.form.ComboBox} combo This combo box
47761              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47762              */
47763         'reset' : true
47764     });
47765 };
47766
47767 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47768     /**
47769      * @cfg {Object} labels Label to use when rendering a form.
47770      * defaults to 
47771      * labels : { 
47772      *      clear : "Clear",
47773      *      confirm : "Confirm"
47774      *  }
47775      */
47776     labels : { 
47777         clear : "Clear",
47778         confirm : "Confirm"
47779     },
47780     /**
47781      * @cfg {Number} width The signature panel width (defaults to 300)
47782      */
47783     width: 300,
47784     /**
47785      * @cfg {Number} height The signature panel height (defaults to 100)
47786      */
47787     height : 100,
47788     /**
47789      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47790      */
47791     allowBlank : false,
47792     
47793     //private
47794     // {Object} signPanel The signature SVG panel element (defaults to {})
47795     signPanel : {},
47796     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47797     isMouseDown : false,
47798     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47799     isConfirmed : false,
47800     // {String} signatureTmp SVG mapping string (defaults to empty string)
47801     signatureTmp : '',
47802     
47803     
47804     defaultAutoCreate : { // modified by initCompnoent..
47805         tag: "input",
47806         type:"hidden"
47807     },
47808
47809     // private
47810     onRender : function(ct, position){
47811         
47812         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47813         
47814         this.wrap = this.el.wrap({
47815             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47816         });
47817         
47818         this.createToolbar(this);
47819         this.signPanel = this.wrap.createChild({
47820                 tag: 'div',
47821                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47822             }, this.el
47823         );
47824             
47825         this.svgID = Roo.id();
47826         this.svgEl = this.signPanel.createChild({
47827               xmlns : 'http://www.w3.org/2000/svg',
47828               tag : 'svg',
47829               id : this.svgID + "-svg",
47830               width: this.width,
47831               height: this.height,
47832               viewBox: '0 0 '+this.width+' '+this.height,
47833               cn : [
47834                 {
47835                     tag: "rect",
47836                     id: this.svgID + "-svg-r",
47837                     width: this.width,
47838                     height: this.height,
47839                     fill: "#ffa"
47840                 },
47841                 {
47842                     tag: "line",
47843                     id: this.svgID + "-svg-l",
47844                     x1: "0", // start
47845                     y1: (this.height*0.8), // start set the line in 80% of height
47846                     x2: this.width, // end
47847                     y2: (this.height*0.8), // end set the line in 80% of height
47848                     'stroke': "#666",
47849                     'stroke-width': "1",
47850                     'stroke-dasharray': "3",
47851                     'shape-rendering': "crispEdges",
47852                     'pointer-events': "none"
47853                 },
47854                 {
47855                     tag: "path",
47856                     id: this.svgID + "-svg-p",
47857                     'stroke': "navy",
47858                     'stroke-width': "3",
47859                     'fill': "none",
47860                     'pointer-events': 'none'
47861                 }
47862               ]
47863         });
47864         this.createSVG();
47865         this.svgBox = this.svgEl.dom.getScreenCTM();
47866     },
47867     createSVG : function(){ 
47868         var svg = this.signPanel;
47869         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47870         var t = this;
47871
47872         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47873         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47874         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47875         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47876         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47877         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47878         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47879         
47880     },
47881     isTouchEvent : function(e){
47882         return e.type.match(/^touch/);
47883     },
47884     getCoords : function (e) {
47885         var pt    = this.svgEl.dom.createSVGPoint();
47886         pt.x = e.clientX; 
47887         pt.y = e.clientY;
47888         if (this.isTouchEvent(e)) {
47889             pt.x =  e.targetTouches[0].clientX 
47890             pt.y = e.targetTouches[0].clientY;
47891         }
47892         var a = this.svgEl.dom.getScreenCTM();
47893         var b = a.inverse();
47894         var mx = pt.matrixTransform(b);
47895         return mx.x + ',' + mx.y;
47896     },
47897     //mouse event headler 
47898     down : function (e) {
47899         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47900         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47901         
47902         this.isMouseDown = true;
47903         
47904         e.preventDefault();
47905     },
47906     move : function (e) {
47907         if (this.isMouseDown) {
47908             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47909             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47910         }
47911         
47912         e.preventDefault();
47913     },
47914     up : function (e) {
47915         this.isMouseDown = false;
47916         var sp = this.signatureTmp.split(' ');
47917         
47918         if(sp.length > 1){
47919             if(!sp[sp.length-2].match(/^L/)){
47920                 sp.pop();
47921                 sp.pop();
47922                 sp.push("");
47923                 this.signatureTmp = sp.join(" ");
47924             }
47925         }
47926         if(this.getValue() != this.signatureTmp){
47927             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47928             this.isConfirmed = false;
47929         }
47930         e.preventDefault();
47931     },
47932     
47933     /**
47934      * Protected method that will not generally be called directly. It
47935      * is called when the editor creates its toolbar. Override this method if you need to
47936      * add custom toolbar buttons.
47937      * @param {HtmlEditor} editor
47938      */
47939     createToolbar : function(editor){
47940          function btn(id, toggle, handler){
47941             var xid = fid + '-'+ id ;
47942             return {
47943                 id : xid,
47944                 cmd : id,
47945                 cls : 'x-btn-icon x-edit-'+id,
47946                 enableToggle:toggle !== false,
47947                 scope: editor, // was editor...
47948                 handler:handler||editor.relayBtnCmd,
47949                 clickEvent:'mousedown',
47950                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47951                 tabIndex:-1
47952             };
47953         }
47954         
47955         
47956         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47957         this.tb = tb;
47958         this.tb.add(
47959            {
47960                 cls : ' x-signature-btn x-signature-'+id,
47961                 scope: editor, // was editor...
47962                 handler: this.reset,
47963                 clickEvent:'mousedown',
47964                 text: this.labels.clear
47965             },
47966             {
47967                  xtype : 'Fill',
47968                  xns: Roo.Toolbar
47969             }, 
47970             {
47971                 cls : '  x-signature-btn x-signature-'+id,
47972                 scope: editor, // was editor...
47973                 handler: this.confirmHandler,
47974                 clickEvent:'mousedown',
47975                 text: this.labels.confirm
47976             }
47977         );
47978     
47979     },
47980     //public
47981     /**
47982      * when user is clicked confirm then show this image.....
47983      * 
47984      * @return {String} Image Data URI
47985      */
47986     getImageDataURI : function(){
47987         var svg = this.svgEl.dom.parentNode.innerHTML;
47988         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47989         return src; 
47990     },
47991     /**
47992      * 
47993      * @return {Boolean} this.isConfirmed
47994      */
47995     getConfirmed : function(){
47996         return this.isConfirmed;
47997     },
47998     /**
47999      * 
48000      * @return {Number} this.width
48001      */
48002     getWidth : function(){
48003         return this.width;
48004     },
48005     /**
48006      * 
48007      * @return {Number} this.height
48008      */
48009     getHeight : function(){
48010         return this.height;
48011     },
48012     // private
48013     getSignature : function(){
48014         return this.signatureTmp;
48015     },
48016     // private
48017     reset : function(){
48018         this.signatureTmp = '';
48019         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48020         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48021         this.isConfirmed = false;
48022         Roo.form.Signature.superclass.reset.call(this);
48023     },
48024     setSignature : function(s){
48025         this.signatureTmp = s;
48026         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48027         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48028         this.setValue(s);
48029         this.isConfirmed = false;
48030         Roo.form.Signature.superclass.reset.call(this);
48031     }, 
48032     test : function(){
48033 //        Roo.log(this.signPanel.dom.contentWindow.up())
48034     },
48035     //private
48036     setConfirmed : function(){
48037         
48038         
48039         
48040 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48041     },
48042     // private
48043     confirmHandler : function(){
48044         if(!this.getSignature()){
48045             return;
48046         }
48047         
48048         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48049         this.setValue(this.getSignature());
48050         this.isConfirmed = true;
48051         
48052         this.fireEvent('confirm', this);
48053     },
48054     // private
48055     // Subclasses should provide the validation implementation by overriding this
48056     validateValue : function(value){
48057         if(this.allowBlank){
48058             return true;
48059         }
48060         
48061         if(this.isConfirmed){
48062             return true;
48063         }
48064         return false;
48065     }
48066 });/*
48067  * Based on:
48068  * Ext JS Library 1.1.1
48069  * Copyright(c) 2006-2007, Ext JS, LLC.
48070  *
48071  * Originally Released Under LGPL - original licence link has changed is not relivant.
48072  *
48073  * Fork - LGPL
48074  * <script type="text/javascript">
48075  */
48076  
48077
48078 /**
48079  * @class Roo.form.ComboBox
48080  * @extends Roo.form.TriggerField
48081  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48082  * @constructor
48083  * Create a new ComboBox.
48084  * @param {Object} config Configuration options
48085  */
48086 Roo.form.Select = function(config){
48087     Roo.form.Select.superclass.constructor.call(this, config);
48088      
48089 };
48090
48091 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48092     /**
48093      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48094      */
48095     /**
48096      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48097      * rendering into an Roo.Editor, defaults to false)
48098      */
48099     /**
48100      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48101      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48102      */
48103     /**
48104      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48105      */
48106     /**
48107      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48108      * the dropdown list (defaults to undefined, with no header element)
48109      */
48110
48111      /**
48112      * @cfg {String/Roo.Template} tpl The template to use to render the output
48113      */
48114      
48115     // private
48116     defaultAutoCreate : {tag: "select"  },
48117     /**
48118      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48119      */
48120     listWidth: undefined,
48121     /**
48122      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48123      * mode = 'remote' or 'text' if mode = 'local')
48124      */
48125     displayField: undefined,
48126     /**
48127      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48128      * mode = 'remote' or 'value' if mode = 'local'). 
48129      * Note: use of a valueField requires the user make a selection
48130      * in order for a value to be mapped.
48131      */
48132     valueField: undefined,
48133     
48134     
48135     /**
48136      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48137      * field's data value (defaults to the underlying DOM element's name)
48138      */
48139     hiddenName: undefined,
48140     /**
48141      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48142      */
48143     listClass: '',
48144     /**
48145      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48146      */
48147     selectedClass: 'x-combo-selected',
48148     /**
48149      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48150      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48151      * which displays a downward arrow icon).
48152      */
48153     triggerClass : 'x-form-arrow-trigger',
48154     /**
48155      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48156      */
48157     shadow:'sides',
48158     /**
48159      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48160      * anchor positions (defaults to 'tl-bl')
48161      */
48162     listAlign: 'tl-bl?',
48163     /**
48164      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48165      */
48166     maxHeight: 300,
48167     /**
48168      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48169      * query specified by the allQuery config option (defaults to 'query')
48170      */
48171     triggerAction: 'query',
48172     /**
48173      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48174      * (defaults to 4, does not apply if editable = false)
48175      */
48176     minChars : 4,
48177     /**
48178      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48179      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48180      */
48181     typeAhead: false,
48182     /**
48183      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48184      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48185      */
48186     queryDelay: 500,
48187     /**
48188      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48189      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48190      */
48191     pageSize: 0,
48192     /**
48193      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48194      * when editable = true (defaults to false)
48195      */
48196     selectOnFocus:false,
48197     /**
48198      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48199      */
48200     queryParam: 'query',
48201     /**
48202      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48203      * when mode = 'remote' (defaults to 'Loading...')
48204      */
48205     loadingText: 'Loading...',
48206     /**
48207      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48208      */
48209     resizable: false,
48210     /**
48211      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48212      */
48213     handleHeight : 8,
48214     /**
48215      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48216      * traditional select (defaults to true)
48217      */
48218     editable: true,
48219     /**
48220      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48221      */
48222     allQuery: '',
48223     /**
48224      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48225      */
48226     mode: 'remote',
48227     /**
48228      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48229      * listWidth has a higher value)
48230      */
48231     minListWidth : 70,
48232     /**
48233      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48234      * allow the user to set arbitrary text into the field (defaults to false)
48235      */
48236     forceSelection:false,
48237     /**
48238      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48239      * if typeAhead = true (defaults to 250)
48240      */
48241     typeAheadDelay : 250,
48242     /**
48243      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48244      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48245      */
48246     valueNotFoundText : undefined,
48247     
48248     /**
48249      * @cfg {String} defaultValue The value displayed after loading the store.
48250      */
48251     defaultValue: '',
48252     
48253     /**
48254      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48255      */
48256     blockFocus : false,
48257     
48258     /**
48259      * @cfg {Boolean} disableClear Disable showing of clear button.
48260      */
48261     disableClear : false,
48262     /**
48263      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48264      */
48265     alwaysQuery : false,
48266     
48267     //private
48268     addicon : false,
48269     editicon: false,
48270     
48271     // element that contains real text value.. (when hidden is used..)
48272      
48273     // private
48274     onRender : function(ct, position){
48275         Roo.form.Field.prototype.onRender.call(this, ct, position);
48276         
48277         if(this.store){
48278             this.store.on('beforeload', this.onBeforeLoad, this);
48279             this.store.on('load', this.onLoad, this);
48280             this.store.on('loadexception', this.onLoadException, this);
48281             this.store.load({});
48282         }
48283         
48284         
48285         
48286     },
48287
48288     // private
48289     initEvents : function(){
48290         //Roo.form.ComboBox.superclass.initEvents.call(this);
48291  
48292     },
48293
48294     onDestroy : function(){
48295        
48296         if(this.store){
48297             this.store.un('beforeload', this.onBeforeLoad, this);
48298             this.store.un('load', this.onLoad, this);
48299             this.store.un('loadexception', this.onLoadException, this);
48300         }
48301         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48302     },
48303
48304     // private
48305     fireKey : function(e){
48306         if(e.isNavKeyPress() && !this.list.isVisible()){
48307             this.fireEvent("specialkey", this, e);
48308         }
48309     },
48310
48311     // private
48312     onResize: function(w, h){
48313         
48314         return; 
48315     
48316         
48317     },
48318
48319     /**
48320      * Allow or prevent the user from directly editing the field text.  If false is passed,
48321      * the user will only be able to select from the items defined in the dropdown list.  This method
48322      * is the runtime equivalent of setting the 'editable' config option at config time.
48323      * @param {Boolean} value True to allow the user to directly edit the field text
48324      */
48325     setEditable : function(value){
48326          
48327     },
48328
48329     // private
48330     onBeforeLoad : function(){
48331         
48332         Roo.log("Select before load");
48333         return;
48334     
48335         this.innerList.update(this.loadingText ?
48336                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48337         //this.restrictHeight();
48338         this.selectedIndex = -1;
48339     },
48340
48341     // private
48342     onLoad : function(){
48343
48344     
48345         var dom = this.el.dom;
48346         dom.innerHTML = '';
48347          var od = dom.ownerDocument;
48348          
48349         if (this.emptyText) {
48350             var op = od.createElement('option');
48351             op.setAttribute('value', '');
48352             op.innerHTML = String.format('{0}', this.emptyText);
48353             dom.appendChild(op);
48354         }
48355         if(this.store.getCount() > 0){
48356            
48357             var vf = this.valueField;
48358             var df = this.displayField;
48359             this.store.data.each(function(r) {
48360                 // which colmsn to use... testing - cdoe / title..
48361                 var op = od.createElement('option');
48362                 op.setAttribute('value', r.data[vf]);
48363                 op.innerHTML = String.format('{0}', r.data[df]);
48364                 dom.appendChild(op);
48365             });
48366             if (typeof(this.defaultValue != 'undefined')) {
48367                 this.setValue(this.defaultValue);
48368             }
48369             
48370              
48371         }else{
48372             //this.onEmptyResults();
48373         }
48374         //this.el.focus();
48375     },
48376     // private
48377     onLoadException : function()
48378     {
48379         dom.innerHTML = '';
48380             
48381         Roo.log("Select on load exception");
48382         return;
48383     
48384         this.collapse();
48385         Roo.log(this.store.reader.jsonData);
48386         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48387             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48388         }
48389         
48390         
48391     },
48392     // private
48393     onTypeAhead : function(){
48394          
48395     },
48396
48397     // private
48398     onSelect : function(record, index){
48399         Roo.log('on select?');
48400         return;
48401         if(this.fireEvent('beforeselect', this, record, index) !== false){
48402             this.setFromData(index > -1 ? record.data : false);
48403             this.collapse();
48404             this.fireEvent('select', this, record, index);
48405         }
48406     },
48407
48408     /**
48409      * Returns the currently selected field value or empty string if no value is set.
48410      * @return {String} value The selected value
48411      */
48412     getValue : function(){
48413         var dom = this.el.dom;
48414         this.value = dom.options[dom.selectedIndex].value;
48415         return this.value;
48416         
48417     },
48418
48419     /**
48420      * Clears any text/value currently set in the field
48421      */
48422     clearValue : function(){
48423         this.value = '';
48424         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48425         
48426     },
48427
48428     /**
48429      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48430      * will be displayed in the field.  If the value does not match the data value of an existing item,
48431      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48432      * Otherwise the field will be blank (although the value will still be set).
48433      * @param {String} value The value to match
48434      */
48435     setValue : function(v){
48436         var d = this.el.dom;
48437         for (var i =0; i < d.options.length;i++) {
48438             if (v == d.options[i].value) {
48439                 d.selectedIndex = i;
48440                 this.value = v;
48441                 return;
48442             }
48443         }
48444         this.clearValue();
48445     },
48446     /**
48447      * @property {Object} the last set data for the element
48448      */
48449     
48450     lastData : false,
48451     /**
48452      * Sets the value of the field based on a object which is related to the record format for the store.
48453      * @param {Object} value the value to set as. or false on reset?
48454      */
48455     setFromData : function(o){
48456         Roo.log('setfrom data?');
48457          
48458         
48459         
48460     },
48461     // private
48462     reset : function(){
48463         this.clearValue();
48464     },
48465     // private
48466     findRecord : function(prop, value){
48467         
48468         return false;
48469     
48470         var record;
48471         if(this.store.getCount() > 0){
48472             this.store.each(function(r){
48473                 if(r.data[prop] == value){
48474                     record = r;
48475                     return false;
48476                 }
48477                 return true;
48478             });
48479         }
48480         return record;
48481     },
48482     
48483     getName: function()
48484     {
48485         // returns hidden if it's set..
48486         if (!this.rendered) {return ''};
48487         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48488         
48489     },
48490      
48491
48492     
48493
48494     // private
48495     onEmptyResults : function(){
48496         Roo.log('empty results');
48497         //this.collapse();
48498     },
48499
48500     /**
48501      * Returns true if the dropdown list is expanded, else false.
48502      */
48503     isExpanded : function(){
48504         return false;
48505     },
48506
48507     /**
48508      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48509      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48510      * @param {String} value The data value of the item to select
48511      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48512      * selected item if it is not currently in view (defaults to true)
48513      * @return {Boolean} True if the value matched an item in the list, else false
48514      */
48515     selectByValue : function(v, scrollIntoView){
48516         Roo.log('select By Value');
48517         return false;
48518     
48519         if(v !== undefined && v !== null){
48520             var r = this.findRecord(this.valueField || this.displayField, v);
48521             if(r){
48522                 this.select(this.store.indexOf(r), scrollIntoView);
48523                 return true;
48524             }
48525         }
48526         return false;
48527     },
48528
48529     /**
48530      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48531      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48532      * @param {Number} index The zero-based index of the list item to select
48533      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48534      * selected item if it is not currently in view (defaults to true)
48535      */
48536     select : function(index, scrollIntoView){
48537         Roo.log('select ');
48538         return  ;
48539         
48540         this.selectedIndex = index;
48541         this.view.select(index);
48542         if(scrollIntoView !== false){
48543             var el = this.view.getNode(index);
48544             if(el){
48545                 this.innerList.scrollChildIntoView(el, false);
48546             }
48547         }
48548     },
48549
48550       
48551
48552     // private
48553     validateBlur : function(){
48554         
48555         return;
48556         
48557     },
48558
48559     // private
48560     initQuery : function(){
48561         this.doQuery(this.getRawValue());
48562     },
48563
48564     // private
48565     doForce : function(){
48566         if(this.el.dom.value.length > 0){
48567             this.el.dom.value =
48568                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48569              
48570         }
48571     },
48572
48573     /**
48574      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48575      * query allowing the query action to be canceled if needed.
48576      * @param {String} query The SQL query to execute
48577      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48578      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48579      * saved in the current store (defaults to false)
48580      */
48581     doQuery : function(q, forceAll){
48582         
48583         Roo.log('doQuery?');
48584         if(q === undefined || q === null){
48585             q = '';
48586         }
48587         var qe = {
48588             query: q,
48589             forceAll: forceAll,
48590             combo: this,
48591             cancel:false
48592         };
48593         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48594             return false;
48595         }
48596         q = qe.query;
48597         forceAll = qe.forceAll;
48598         if(forceAll === true || (q.length >= this.minChars)){
48599             if(this.lastQuery != q || this.alwaysQuery){
48600                 this.lastQuery = q;
48601                 if(this.mode == 'local'){
48602                     this.selectedIndex = -1;
48603                     if(forceAll){
48604                         this.store.clearFilter();
48605                     }else{
48606                         this.store.filter(this.displayField, q);
48607                     }
48608                     this.onLoad();
48609                 }else{
48610                     this.store.baseParams[this.queryParam] = q;
48611                     this.store.load({
48612                         params: this.getParams(q)
48613                     });
48614                     this.expand();
48615                 }
48616             }else{
48617                 this.selectedIndex = -1;
48618                 this.onLoad();   
48619             }
48620         }
48621     },
48622
48623     // private
48624     getParams : function(q){
48625         var p = {};
48626         //p[this.queryParam] = q;
48627         if(this.pageSize){
48628             p.start = 0;
48629             p.limit = this.pageSize;
48630         }
48631         return p;
48632     },
48633
48634     /**
48635      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48636      */
48637     collapse : function(){
48638         
48639     },
48640
48641     // private
48642     collapseIf : function(e){
48643         
48644     },
48645
48646     /**
48647      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48648      */
48649     expand : function(){
48650         
48651     } ,
48652
48653     // private
48654      
48655
48656     /** 
48657     * @cfg {Boolean} grow 
48658     * @hide 
48659     */
48660     /** 
48661     * @cfg {Number} growMin 
48662     * @hide 
48663     */
48664     /** 
48665     * @cfg {Number} growMax 
48666     * @hide 
48667     */
48668     /**
48669      * @hide
48670      * @method autoSize
48671      */
48672     
48673     setWidth : function()
48674     {
48675         
48676     },
48677     getResizeEl : function(){
48678         return this.el;
48679     }
48680 });//<script type="text/javasscript">
48681  
48682
48683 /**
48684  * @class Roo.DDView
48685  * A DnD enabled version of Roo.View.
48686  * @param {Element/String} container The Element in which to create the View.
48687  * @param {String} tpl The template string used to create the markup for each element of the View
48688  * @param {Object} config The configuration properties. These include all the config options of
48689  * {@link Roo.View} plus some specific to this class.<br>
48690  * <p>
48691  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48692  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48693  * <p>
48694  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48695 .x-view-drag-insert-above {
48696         border-top:1px dotted #3366cc;
48697 }
48698 .x-view-drag-insert-below {
48699         border-bottom:1px dotted #3366cc;
48700 }
48701 </code></pre>
48702  * 
48703  */
48704  
48705 Roo.DDView = function(container, tpl, config) {
48706     Roo.DDView.superclass.constructor.apply(this, arguments);
48707     this.getEl().setStyle("outline", "0px none");
48708     this.getEl().unselectable();
48709     if (this.dragGroup) {
48710                 this.setDraggable(this.dragGroup.split(","));
48711     }
48712     if (this.dropGroup) {
48713                 this.setDroppable(this.dropGroup.split(","));
48714     }
48715     if (this.deletable) {
48716         this.setDeletable();
48717     }
48718     this.isDirtyFlag = false;
48719         this.addEvents({
48720                 "drop" : true
48721         });
48722 };
48723
48724 Roo.extend(Roo.DDView, Roo.View, {
48725 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48726 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48727 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48728 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48729
48730         isFormField: true,
48731
48732         reset: Roo.emptyFn,
48733         
48734         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48735
48736         validate: function() {
48737                 return true;
48738         },
48739         
48740         destroy: function() {
48741                 this.purgeListeners();
48742                 this.getEl.removeAllListeners();
48743                 this.getEl().remove();
48744                 if (this.dragZone) {
48745                         if (this.dragZone.destroy) {
48746                                 this.dragZone.destroy();
48747                         }
48748                 }
48749                 if (this.dropZone) {
48750                         if (this.dropZone.destroy) {
48751                                 this.dropZone.destroy();
48752                         }
48753                 }
48754         },
48755
48756 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48757         getName: function() {
48758                 return this.name;
48759         },
48760
48761 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48762         setValue: function(v) {
48763                 if (!this.store) {
48764                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48765                 }
48766                 var data = {};
48767                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48768                 this.store.proxy = new Roo.data.MemoryProxy(data);
48769                 this.store.load();
48770         },
48771
48772 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48773         getValue: function() {
48774                 var result = '(';
48775                 this.store.each(function(rec) {
48776                         result += rec.id + ',';
48777                 });
48778                 return result.substr(0, result.length - 1) + ')';
48779         },
48780         
48781         getIds: function() {
48782                 var i = 0, result = new Array(this.store.getCount());
48783                 this.store.each(function(rec) {
48784                         result[i++] = rec.id;
48785                 });
48786                 return result;
48787         },
48788         
48789         isDirty: function() {
48790                 return this.isDirtyFlag;
48791         },
48792
48793 /**
48794  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48795  *      whole Element becomes the target, and this causes the drop gesture to append.
48796  */
48797     getTargetFromEvent : function(e) {
48798                 var target = e.getTarget();
48799                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48800                 target = target.parentNode;
48801                 }
48802                 if (!target) {
48803                         target = this.el.dom.lastChild || this.el.dom;
48804                 }
48805                 return target;
48806     },
48807
48808 /**
48809  *      Create the drag data which consists of an object which has the property "ddel" as
48810  *      the drag proxy element. 
48811  */
48812     getDragData : function(e) {
48813         var target = this.findItemFromChild(e.getTarget());
48814                 if(target) {
48815                         this.handleSelection(e);
48816                         var selNodes = this.getSelectedNodes();
48817             var dragData = {
48818                 source: this,
48819                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48820                 nodes: selNodes,
48821                 records: []
48822                         };
48823                         var selectedIndices = this.getSelectedIndexes();
48824                         for (var i = 0; i < selectedIndices.length; i++) {
48825                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48826                         }
48827                         if (selNodes.length == 1) {
48828                                 dragData.ddel = target.cloneNode(true); // the div element
48829                         } else {
48830                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48831                                 div.className = 'multi-proxy';
48832                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48833                                         div.appendChild(selNodes[i].cloneNode(true));
48834                                 }
48835                                 dragData.ddel = div;
48836                         }
48837             //console.log(dragData)
48838             //console.log(dragData.ddel.innerHTML)
48839                         return dragData;
48840                 }
48841         //console.log('nodragData')
48842                 return false;
48843     },
48844     
48845 /**     Specify to which ddGroup items in this DDView may be dragged. */
48846     setDraggable: function(ddGroup) {
48847         if (ddGroup instanceof Array) {
48848                 Roo.each(ddGroup, this.setDraggable, this);
48849                 return;
48850         }
48851         if (this.dragZone) {
48852                 this.dragZone.addToGroup(ddGroup);
48853         } else {
48854                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48855                                 containerScroll: true,
48856                                 ddGroup: ddGroup 
48857
48858                         });
48859 //                      Draggability implies selection. DragZone's mousedown selects the element.
48860                         if (!this.multiSelect) { this.singleSelect = true; }
48861
48862 //                      Wire the DragZone's handlers up to methods in *this*
48863                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48864                 }
48865     },
48866
48867 /**     Specify from which ddGroup this DDView accepts drops. */
48868     setDroppable: function(ddGroup) {
48869         if (ddGroup instanceof Array) {
48870                 Roo.each(ddGroup, this.setDroppable, this);
48871                 return;
48872         }
48873         if (this.dropZone) {
48874                 this.dropZone.addToGroup(ddGroup);
48875         } else {
48876                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48877                                 containerScroll: true,
48878                                 ddGroup: ddGroup
48879                         });
48880
48881 //                      Wire the DropZone's handlers up to methods in *this*
48882                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48883                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48884                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48885                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48886                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48887                 }
48888     },
48889
48890 /**     Decide whether to drop above or below a View node. */
48891     getDropPoint : function(e, n, dd){
48892         if (n == this.el.dom) { return "above"; }
48893                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48894                 var c = t + (b - t) / 2;
48895                 var y = Roo.lib.Event.getPageY(e);
48896                 if(y <= c) {
48897                         return "above";
48898                 }else{
48899                         return "below";
48900                 }
48901     },
48902
48903     onNodeEnter : function(n, dd, e, data){
48904                 return false;
48905     },
48906     
48907     onNodeOver : function(n, dd, e, data){
48908                 var pt = this.getDropPoint(e, n, dd);
48909                 // set the insert point style on the target node
48910                 var dragElClass = this.dropNotAllowed;
48911                 if (pt) {
48912                         var targetElClass;
48913                         if (pt == "above"){
48914                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48915                                 targetElClass = "x-view-drag-insert-above";
48916                         } else {
48917                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48918                                 targetElClass = "x-view-drag-insert-below";
48919                         }
48920                         if (this.lastInsertClass != targetElClass){
48921                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48922                                 this.lastInsertClass = targetElClass;
48923                         }
48924                 }
48925                 return dragElClass;
48926         },
48927
48928     onNodeOut : function(n, dd, e, data){
48929                 this.removeDropIndicators(n);
48930     },
48931
48932     onNodeDrop : function(n, dd, e, data){
48933         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48934                 return false;
48935         }
48936         var pt = this.getDropPoint(e, n, dd);
48937                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48938                 if (pt == "below") { insertAt++; }
48939                 for (var i = 0; i < data.records.length; i++) {
48940                         var r = data.records[i];
48941                         var dup = this.store.getById(r.id);
48942                         if (dup && (dd != this.dragZone)) {
48943                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48944                         } else {
48945                                 if (data.copy) {
48946                                         this.store.insert(insertAt++, r.copy());
48947                                 } else {
48948                                         data.source.isDirtyFlag = true;
48949                                         r.store.remove(r);
48950                                         this.store.insert(insertAt++, r);
48951                                 }
48952                                 this.isDirtyFlag = true;
48953                         }
48954                 }
48955                 this.dragZone.cachedTarget = null;
48956                 return true;
48957     },
48958
48959     removeDropIndicators : function(n){
48960                 if(n){
48961                         Roo.fly(n).removeClass([
48962                                 "x-view-drag-insert-above",
48963                                 "x-view-drag-insert-below"]);
48964                         this.lastInsertClass = "_noclass";
48965                 }
48966     },
48967
48968 /**
48969  *      Utility method. Add a delete option to the DDView's context menu.
48970  *      @param {String} imageUrl The URL of the "delete" icon image.
48971  */
48972         setDeletable: function(imageUrl) {
48973                 if (!this.singleSelect && !this.multiSelect) {
48974                         this.singleSelect = true;
48975                 }
48976                 var c = this.getContextMenu();
48977                 this.contextMenu.on("itemclick", function(item) {
48978                         switch (item.id) {
48979                                 case "delete":
48980                                         this.remove(this.getSelectedIndexes());
48981                                         break;
48982                         }
48983                 }, this);
48984                 this.contextMenu.add({
48985                         icon: imageUrl,
48986                         id: "delete",
48987                         text: 'Delete'
48988                 });
48989         },
48990         
48991 /**     Return the context menu for this DDView. */
48992         getContextMenu: function() {
48993                 if (!this.contextMenu) {
48994 //                      Create the View's context menu
48995                         this.contextMenu = new Roo.menu.Menu({
48996                                 id: this.id + "-contextmenu"
48997                         });
48998                         this.el.on("contextmenu", this.showContextMenu, this);
48999                 }
49000                 return this.contextMenu;
49001         },
49002         
49003         disableContextMenu: function() {
49004                 if (this.contextMenu) {
49005                         this.el.un("contextmenu", this.showContextMenu, this);
49006                 }
49007         },
49008
49009         showContextMenu: function(e, item) {
49010         item = this.findItemFromChild(e.getTarget());
49011                 if (item) {
49012                         e.stopEvent();
49013                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49014                         this.contextMenu.showAt(e.getXY());
49015             }
49016     },
49017
49018 /**
49019  *      Remove {@link Roo.data.Record}s at the specified indices.
49020  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49021  */
49022     remove: function(selectedIndices) {
49023                 selectedIndices = [].concat(selectedIndices);
49024                 for (var i = 0; i < selectedIndices.length; i++) {
49025                         var rec = this.store.getAt(selectedIndices[i]);
49026                         this.store.remove(rec);
49027                 }
49028     },
49029
49030 /**
49031  *      Double click fires the event, but also, if this is draggable, and there is only one other
49032  *      related DropZone, it transfers the selected node.
49033  */
49034     onDblClick : function(e){
49035         var item = this.findItemFromChild(e.getTarget());
49036         if(item){
49037             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49038                 return false;
49039             }
49040             if (this.dragGroup) {
49041                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49042                     while (targets.indexOf(this.dropZone) > -1) {
49043                             targets.remove(this.dropZone);
49044                                 }
49045                     if (targets.length == 1) {
49046                                         this.dragZone.cachedTarget = null;
49047                         var el = Roo.get(targets[0].getEl());
49048                         var box = el.getBox(true);
49049                         targets[0].onNodeDrop(el.dom, {
49050                                 target: el.dom,
49051                                 xy: [box.x, box.y + box.height - 1]
49052                         }, null, this.getDragData(e));
49053                     }
49054                 }
49055         }
49056     },
49057     
49058     handleSelection: function(e) {
49059                 this.dragZone.cachedTarget = null;
49060         var item = this.findItemFromChild(e.getTarget());
49061         if (!item) {
49062                 this.clearSelections(true);
49063                 return;
49064         }
49065                 if (item && (this.multiSelect || this.singleSelect)){
49066                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49067                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49068                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49069                                 this.unselect(item);
49070                         } else {
49071                                 this.select(item, this.multiSelect && e.ctrlKey);
49072                                 this.lastSelection = item;
49073                         }
49074                 }
49075     },
49076
49077     onItemClick : function(item, index, e){
49078                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49079                         return false;
49080                 }
49081                 return true;
49082     },
49083
49084     unselect : function(nodeInfo, suppressEvent){
49085                 var node = this.getNode(nodeInfo);
49086                 if(node && this.isSelected(node)){
49087                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49088                                 Roo.fly(node).removeClass(this.selectedClass);
49089                                 this.selections.remove(node);
49090                                 if(!suppressEvent){
49091                                         this.fireEvent("selectionchange", this, this.selections);
49092                                 }
49093                         }
49094                 }
49095     }
49096 });
49097 /*
49098  * Based on:
49099  * Ext JS Library 1.1.1
49100  * Copyright(c) 2006-2007, Ext JS, LLC.
49101  *
49102  * Originally Released Under LGPL - original licence link has changed is not relivant.
49103  *
49104  * Fork - LGPL
49105  * <script type="text/javascript">
49106  */
49107  
49108 /**
49109  * @class Roo.LayoutManager
49110  * @extends Roo.util.Observable
49111  * Base class for layout managers.
49112  */
49113 Roo.LayoutManager = function(container, config){
49114     Roo.LayoutManager.superclass.constructor.call(this);
49115     this.el = Roo.get(container);
49116     // ie scrollbar fix
49117     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49118         document.body.scroll = "no";
49119     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49120         this.el.position('relative');
49121     }
49122     this.id = this.el.id;
49123     this.el.addClass("x-layout-container");
49124     /** false to disable window resize monitoring @type Boolean */
49125     this.monitorWindowResize = true;
49126     this.regions = {};
49127     this.addEvents({
49128         /**
49129          * @event layout
49130          * Fires when a layout is performed. 
49131          * @param {Roo.LayoutManager} this
49132          */
49133         "layout" : true,
49134         /**
49135          * @event regionresized
49136          * Fires when the user resizes a region. 
49137          * @param {Roo.LayoutRegion} region The resized region
49138          * @param {Number} newSize The new size (width for east/west, height for north/south)
49139          */
49140         "regionresized" : true,
49141         /**
49142          * @event regioncollapsed
49143          * Fires when a region is collapsed. 
49144          * @param {Roo.LayoutRegion} region The collapsed region
49145          */
49146         "regioncollapsed" : true,
49147         /**
49148          * @event regionexpanded
49149          * Fires when a region is expanded.  
49150          * @param {Roo.LayoutRegion} region The expanded region
49151          */
49152         "regionexpanded" : true
49153     });
49154     this.updating = false;
49155     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49156 };
49157
49158 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49159     /**
49160      * Returns true if this layout is currently being updated
49161      * @return {Boolean}
49162      */
49163     isUpdating : function(){
49164         return this.updating; 
49165     },
49166     
49167     /**
49168      * Suspend the LayoutManager from doing auto-layouts while
49169      * making multiple add or remove calls
49170      */
49171     beginUpdate : function(){
49172         this.updating = true;    
49173     },
49174     
49175     /**
49176      * Restore auto-layouts and optionally disable the manager from performing a layout
49177      * @param {Boolean} noLayout true to disable a layout update 
49178      */
49179     endUpdate : function(noLayout){
49180         this.updating = false;
49181         if(!noLayout){
49182             this.layout();
49183         }    
49184     },
49185     
49186     layout: function(){
49187         
49188     },
49189     
49190     onRegionResized : function(region, newSize){
49191         this.fireEvent("regionresized", region, newSize);
49192         this.layout();
49193     },
49194     
49195     onRegionCollapsed : function(region){
49196         this.fireEvent("regioncollapsed", region);
49197     },
49198     
49199     onRegionExpanded : function(region){
49200         this.fireEvent("regionexpanded", region);
49201     },
49202         
49203     /**
49204      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49205      * performs box-model adjustments.
49206      * @return {Object} The size as an object {width: (the width), height: (the height)}
49207      */
49208     getViewSize : function(){
49209         var size;
49210         if(this.el.dom != document.body){
49211             size = this.el.getSize();
49212         }else{
49213             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49214         }
49215         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49216         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49217         return size;
49218     },
49219     
49220     /**
49221      * Returns the Element this layout is bound to.
49222      * @return {Roo.Element}
49223      */
49224     getEl : function(){
49225         return this.el;
49226     },
49227     
49228     /**
49229      * Returns the specified region.
49230      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49231      * @return {Roo.LayoutRegion}
49232      */
49233     getRegion : function(target){
49234         return this.regions[target.toLowerCase()];
49235     },
49236     
49237     onWindowResize : function(){
49238         if(this.monitorWindowResize){
49239             this.layout();
49240         }
49241     }
49242 });/*
49243  * Based on:
49244  * Ext JS Library 1.1.1
49245  * Copyright(c) 2006-2007, Ext JS, LLC.
49246  *
49247  * Originally Released Under LGPL - original licence link has changed is not relivant.
49248  *
49249  * Fork - LGPL
49250  * <script type="text/javascript">
49251  */
49252 /**
49253  * @class Roo.BorderLayout
49254  * @extends Roo.LayoutManager
49255  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49256  * please see: <br><br>
49257  * <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>
49258  * <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>
49259  * Example:
49260  <pre><code>
49261  var layout = new Roo.BorderLayout(document.body, {
49262     north: {
49263         initialSize: 25,
49264         titlebar: false
49265     },
49266     west: {
49267         split:true,
49268         initialSize: 200,
49269         minSize: 175,
49270         maxSize: 400,
49271         titlebar: true,
49272         collapsible: true
49273     },
49274     east: {
49275         split:true,
49276         initialSize: 202,
49277         minSize: 175,
49278         maxSize: 400,
49279         titlebar: true,
49280         collapsible: true
49281     },
49282     south: {
49283         split:true,
49284         initialSize: 100,
49285         minSize: 100,
49286         maxSize: 200,
49287         titlebar: true,
49288         collapsible: true
49289     },
49290     center: {
49291         titlebar: true,
49292         autoScroll:true,
49293         resizeTabs: true,
49294         minTabWidth: 50,
49295         preferredTabWidth: 150
49296     }
49297 });
49298
49299 // shorthand
49300 var CP = Roo.ContentPanel;
49301
49302 layout.beginUpdate();
49303 layout.add("north", new CP("north", "North"));
49304 layout.add("south", new CP("south", {title: "South", closable: true}));
49305 layout.add("west", new CP("west", {title: "West"}));
49306 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49307 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49308 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49309 layout.getRegion("center").showPanel("center1");
49310 layout.endUpdate();
49311 </code></pre>
49312
49313 <b>The container the layout is rendered into can be either the body element or any other element.
49314 If it is not the body element, the container needs to either be an absolute positioned element,
49315 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49316 the container size if it is not the body element.</b>
49317
49318 * @constructor
49319 * Create a new BorderLayout
49320 * @param {String/HTMLElement/Element} container The container this layout is bound to
49321 * @param {Object} config Configuration options
49322  */
49323 Roo.BorderLayout = function(container, config){
49324     config = config || {};
49325     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49326     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49327     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49328         var target = this.factory.validRegions[i];
49329         if(config[target]){
49330             this.addRegion(target, config[target]);
49331         }
49332     }
49333 };
49334
49335 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49336     /**
49337      * Creates and adds a new region if it doesn't already exist.
49338      * @param {String} target The target region key (north, south, east, west or center).
49339      * @param {Object} config The regions config object
49340      * @return {BorderLayoutRegion} The new region
49341      */
49342     addRegion : function(target, config){
49343         if(!this.regions[target]){
49344             var r = this.factory.create(target, this, config);
49345             this.bindRegion(target, r);
49346         }
49347         return this.regions[target];
49348     },
49349
49350     // private (kinda)
49351     bindRegion : function(name, r){
49352         this.regions[name] = r;
49353         r.on("visibilitychange", this.layout, this);
49354         r.on("paneladded", this.layout, this);
49355         r.on("panelremoved", this.layout, this);
49356         r.on("invalidated", this.layout, this);
49357         r.on("resized", this.onRegionResized, this);
49358         r.on("collapsed", this.onRegionCollapsed, this);
49359         r.on("expanded", this.onRegionExpanded, this);
49360     },
49361
49362     /**
49363      * Performs a layout update.
49364      */
49365     layout : function(){
49366         if(this.updating) return;
49367         var size = this.getViewSize();
49368         var w = size.width;
49369         var h = size.height;
49370         var centerW = w;
49371         var centerH = h;
49372         var centerY = 0;
49373         var centerX = 0;
49374         //var x = 0, y = 0;
49375
49376         var rs = this.regions;
49377         var north = rs["north"];
49378         var south = rs["south"]; 
49379         var west = rs["west"];
49380         var east = rs["east"];
49381         var center = rs["center"];
49382         //if(this.hideOnLayout){ // not supported anymore
49383             //c.el.setStyle("display", "none");
49384         //}
49385         if(north && north.isVisible()){
49386             var b = north.getBox();
49387             var m = north.getMargins();
49388             b.width = w - (m.left+m.right);
49389             b.x = m.left;
49390             b.y = m.top;
49391             centerY = b.height + b.y + m.bottom;
49392             centerH -= centerY;
49393             north.updateBox(this.safeBox(b));
49394         }
49395         if(south && south.isVisible()){
49396             var b = south.getBox();
49397             var m = south.getMargins();
49398             b.width = w - (m.left+m.right);
49399             b.x = m.left;
49400             var totalHeight = (b.height + m.top + m.bottom);
49401             b.y = h - totalHeight + m.top;
49402             centerH -= totalHeight;
49403             south.updateBox(this.safeBox(b));
49404         }
49405         if(west && west.isVisible()){
49406             var b = west.getBox();
49407             var m = west.getMargins();
49408             b.height = centerH - (m.top+m.bottom);
49409             b.x = m.left;
49410             b.y = centerY + m.top;
49411             var totalWidth = (b.width + m.left + m.right);
49412             centerX += totalWidth;
49413             centerW -= totalWidth;
49414             west.updateBox(this.safeBox(b));
49415         }
49416         if(east && east.isVisible()){
49417             var b = east.getBox();
49418             var m = east.getMargins();
49419             b.height = centerH - (m.top+m.bottom);
49420             var totalWidth = (b.width + m.left + m.right);
49421             b.x = w - totalWidth + m.left;
49422             b.y = centerY + m.top;
49423             centerW -= totalWidth;
49424             east.updateBox(this.safeBox(b));
49425         }
49426         if(center){
49427             var m = center.getMargins();
49428             var centerBox = {
49429                 x: centerX + m.left,
49430                 y: centerY + m.top,
49431                 width: centerW - (m.left+m.right),
49432                 height: centerH - (m.top+m.bottom)
49433             };
49434             //if(this.hideOnLayout){
49435                 //center.el.setStyle("display", "block");
49436             //}
49437             center.updateBox(this.safeBox(centerBox));
49438         }
49439         this.el.repaint();
49440         this.fireEvent("layout", this);
49441     },
49442
49443     // private
49444     safeBox : function(box){
49445         box.width = Math.max(0, box.width);
49446         box.height = Math.max(0, box.height);
49447         return box;
49448     },
49449
49450     /**
49451      * Adds a ContentPanel (or subclass) to this layout.
49452      * @param {String} target The target region key (north, south, east, west or center).
49453      * @param {Roo.ContentPanel} panel The panel to add
49454      * @return {Roo.ContentPanel} The added panel
49455      */
49456     add : function(target, panel){
49457          
49458         target = target.toLowerCase();
49459         return this.regions[target].add(panel);
49460     },
49461
49462     /**
49463      * Remove a ContentPanel (or subclass) to this layout.
49464      * @param {String} target The target region key (north, south, east, west or center).
49465      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49466      * @return {Roo.ContentPanel} The removed panel
49467      */
49468     remove : function(target, panel){
49469         target = target.toLowerCase();
49470         return this.regions[target].remove(panel);
49471     },
49472
49473     /**
49474      * Searches all regions for a panel with the specified id
49475      * @param {String} panelId
49476      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49477      */
49478     findPanel : function(panelId){
49479         var rs = this.regions;
49480         for(var target in rs){
49481             if(typeof rs[target] != "function"){
49482                 var p = rs[target].getPanel(panelId);
49483                 if(p){
49484                     return p;
49485                 }
49486             }
49487         }
49488         return null;
49489     },
49490
49491     /**
49492      * Searches all regions for a panel with the specified id and activates (shows) it.
49493      * @param {String/ContentPanel} panelId The panels id or the panel itself
49494      * @return {Roo.ContentPanel} The shown panel or null
49495      */
49496     showPanel : function(panelId) {
49497       var rs = this.regions;
49498       for(var target in rs){
49499          var r = rs[target];
49500          if(typeof r != "function"){
49501             if(r.hasPanel(panelId)){
49502                return r.showPanel(panelId);
49503             }
49504          }
49505       }
49506       return null;
49507    },
49508
49509    /**
49510      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49511      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49512      */
49513     restoreState : function(provider){
49514         if(!provider){
49515             provider = Roo.state.Manager;
49516         }
49517         var sm = new Roo.LayoutStateManager();
49518         sm.init(this, provider);
49519     },
49520
49521     /**
49522      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49523      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49524      * a valid ContentPanel config object.  Example:
49525      * <pre><code>
49526 // Create the main layout
49527 var layout = new Roo.BorderLayout('main-ct', {
49528     west: {
49529         split:true,
49530         minSize: 175,
49531         titlebar: true
49532     },
49533     center: {
49534         title:'Components'
49535     }
49536 }, 'main-ct');
49537
49538 // Create and add multiple ContentPanels at once via configs
49539 layout.batchAdd({
49540    west: {
49541        id: 'source-files',
49542        autoCreate:true,
49543        title:'Ext Source Files',
49544        autoScroll:true,
49545        fitToFrame:true
49546    },
49547    center : {
49548        el: cview,
49549        autoScroll:true,
49550        fitToFrame:true,
49551        toolbar: tb,
49552        resizeEl:'cbody'
49553    }
49554 });
49555 </code></pre>
49556      * @param {Object} regions An object containing ContentPanel configs by region name
49557      */
49558     batchAdd : function(regions){
49559         this.beginUpdate();
49560         for(var rname in regions){
49561             var lr = this.regions[rname];
49562             if(lr){
49563                 this.addTypedPanels(lr, regions[rname]);
49564             }
49565         }
49566         this.endUpdate();
49567     },
49568
49569     // private
49570     addTypedPanels : function(lr, ps){
49571         if(typeof ps == 'string'){
49572             lr.add(new Roo.ContentPanel(ps));
49573         }
49574         else if(ps instanceof Array){
49575             for(var i =0, len = ps.length; i < len; i++){
49576                 this.addTypedPanels(lr, ps[i]);
49577             }
49578         }
49579         else if(!ps.events){ // raw config?
49580             var el = ps.el;
49581             delete ps.el; // prevent conflict
49582             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49583         }
49584         else {  // panel object assumed!
49585             lr.add(ps);
49586         }
49587     },
49588     /**
49589      * Adds a xtype elements to the layout.
49590      * <pre><code>
49591
49592 layout.addxtype({
49593        xtype : 'ContentPanel',
49594        region: 'west',
49595        items: [ .... ]
49596    }
49597 );
49598
49599 layout.addxtype({
49600         xtype : 'NestedLayoutPanel',
49601         region: 'west',
49602         layout: {
49603            center: { },
49604            west: { }   
49605         },
49606         items : [ ... list of content panels or nested layout panels.. ]
49607    }
49608 );
49609 </code></pre>
49610      * @param {Object} cfg Xtype definition of item to add.
49611      */
49612     addxtype : function(cfg)
49613     {
49614         // basically accepts a pannel...
49615         // can accept a layout region..!?!?
49616         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49617         
49618         if (!cfg.xtype.match(/Panel$/)) {
49619             return false;
49620         }
49621         var ret = false;
49622         
49623         if (typeof(cfg.region) == 'undefined') {
49624             Roo.log("Failed to add Panel, region was not set");
49625             Roo.log(cfg);
49626             return false;
49627         }
49628         var region = cfg.region;
49629         delete cfg.region;
49630         
49631           
49632         var xitems = [];
49633         if (cfg.items) {
49634             xitems = cfg.items;
49635             delete cfg.items;
49636         }
49637         var nb = false;
49638         
49639         switch(cfg.xtype) 
49640         {
49641             case 'ContentPanel':  // ContentPanel (el, cfg)
49642             case 'ScrollPanel':  // ContentPanel (el, cfg)
49643             case 'ViewPanel': 
49644                 if(cfg.autoCreate) {
49645                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49646                 } else {
49647                     var el = this.el.createChild();
49648                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49649                 }
49650                 
49651                 this.add(region, ret);
49652                 break;
49653             
49654             
49655             case 'TreePanel': // our new panel!
49656                 cfg.el = this.el.createChild();
49657                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49658                 this.add(region, ret);
49659                 break;
49660             
49661             case 'NestedLayoutPanel': 
49662                 // create a new Layout (which is  a Border Layout...
49663                 var el = this.el.createChild();
49664                 var clayout = cfg.layout;
49665                 delete cfg.layout;
49666                 clayout.items   = clayout.items  || [];
49667                 // replace this exitems with the clayout ones..
49668                 xitems = clayout.items;
49669                  
49670                 
49671                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49672                     cfg.background = false;
49673                 }
49674                 var layout = new Roo.BorderLayout(el, clayout);
49675                 
49676                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49677                 //console.log('adding nested layout panel '  + cfg.toSource());
49678                 this.add(region, ret);
49679                 nb = {}; /// find first...
49680                 break;
49681                 
49682             case 'GridPanel': 
49683             
49684                 // needs grid and region
49685                 
49686                 //var el = this.getRegion(region).el.createChild();
49687                 var el = this.el.createChild();
49688                 // create the grid first...
49689                 
49690                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49691                 delete cfg.grid;
49692                 if (region == 'center' && this.active ) {
49693                     cfg.background = false;
49694                 }
49695                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49696                 
49697                 this.add(region, ret);
49698                 if (cfg.background) {
49699                     ret.on('activate', function(gp) {
49700                         if (!gp.grid.rendered) {
49701                             gp.grid.render();
49702                         }
49703                     });
49704                 } else {
49705                     grid.render();
49706                 }
49707                 break;
49708            
49709            
49710            
49711                 
49712                 
49713                 
49714             default:
49715                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49716                     
49717                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49718                     this.add(region, ret);
49719                 } else {
49720                 
49721                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49722                     return null;
49723                 }
49724                 
49725              // GridPanel (grid, cfg)
49726             
49727         }
49728         this.beginUpdate();
49729         // add children..
49730         var region = '';
49731         var abn = {};
49732         Roo.each(xitems, function(i)  {
49733             region = nb && i.region ? i.region : false;
49734             
49735             var add = ret.addxtype(i);
49736            
49737             if (region) {
49738                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49739                 if (!i.background) {
49740                     abn[region] = nb[region] ;
49741                 }
49742             }
49743             
49744         });
49745         this.endUpdate();
49746
49747         // make the last non-background panel active..
49748         //if (nb) { Roo.log(abn); }
49749         if (nb) {
49750             
49751             for(var r in abn) {
49752                 region = this.getRegion(r);
49753                 if (region) {
49754                     // tried using nb[r], but it does not work..
49755                      
49756                     region.showPanel(abn[r]);
49757                    
49758                 }
49759             }
49760         }
49761         return ret;
49762         
49763     }
49764 });
49765
49766 /**
49767  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49768  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49769  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49770  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49771  * <pre><code>
49772 // shorthand
49773 var CP = Roo.ContentPanel;
49774
49775 var layout = Roo.BorderLayout.create({
49776     north: {
49777         initialSize: 25,
49778         titlebar: false,
49779         panels: [new CP("north", "North")]
49780     },
49781     west: {
49782         split:true,
49783         initialSize: 200,
49784         minSize: 175,
49785         maxSize: 400,
49786         titlebar: true,
49787         collapsible: true,
49788         panels: [new CP("west", {title: "West"})]
49789     },
49790     east: {
49791         split:true,
49792         initialSize: 202,
49793         minSize: 175,
49794         maxSize: 400,
49795         titlebar: true,
49796         collapsible: true,
49797         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49798     },
49799     south: {
49800         split:true,
49801         initialSize: 100,
49802         minSize: 100,
49803         maxSize: 200,
49804         titlebar: true,
49805         collapsible: true,
49806         panels: [new CP("south", {title: "South", closable: true})]
49807     },
49808     center: {
49809         titlebar: true,
49810         autoScroll:true,
49811         resizeTabs: true,
49812         minTabWidth: 50,
49813         preferredTabWidth: 150,
49814         panels: [
49815             new CP("center1", {title: "Close Me", closable: true}),
49816             new CP("center2", {title: "Center Panel", closable: false})
49817         ]
49818     }
49819 }, document.body);
49820
49821 layout.getRegion("center").showPanel("center1");
49822 </code></pre>
49823  * @param config
49824  * @param targetEl
49825  */
49826 Roo.BorderLayout.create = function(config, targetEl){
49827     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49828     layout.beginUpdate();
49829     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49830     for(var j = 0, jlen = regions.length; j < jlen; j++){
49831         var lr = regions[j];
49832         if(layout.regions[lr] && config[lr].panels){
49833             var r = layout.regions[lr];
49834             var ps = config[lr].panels;
49835             layout.addTypedPanels(r, ps);
49836         }
49837     }
49838     layout.endUpdate();
49839     return layout;
49840 };
49841
49842 // private
49843 Roo.BorderLayout.RegionFactory = {
49844     // private
49845     validRegions : ["north","south","east","west","center"],
49846
49847     // private
49848     create : function(target, mgr, config){
49849         target = target.toLowerCase();
49850         if(config.lightweight || config.basic){
49851             return new Roo.BasicLayoutRegion(mgr, config, target);
49852         }
49853         switch(target){
49854             case "north":
49855                 return new Roo.NorthLayoutRegion(mgr, config);
49856             case "south":
49857                 return new Roo.SouthLayoutRegion(mgr, config);
49858             case "east":
49859                 return new Roo.EastLayoutRegion(mgr, config);
49860             case "west":
49861                 return new Roo.WestLayoutRegion(mgr, config);
49862             case "center":
49863                 return new Roo.CenterLayoutRegion(mgr, config);
49864         }
49865         throw 'Layout region "'+target+'" not supported.';
49866     }
49867 };/*
49868  * Based on:
49869  * Ext JS Library 1.1.1
49870  * Copyright(c) 2006-2007, Ext JS, LLC.
49871  *
49872  * Originally Released Under LGPL - original licence link has changed is not relivant.
49873  *
49874  * Fork - LGPL
49875  * <script type="text/javascript">
49876  */
49877  
49878 /**
49879  * @class Roo.BasicLayoutRegion
49880  * @extends Roo.util.Observable
49881  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49882  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49883  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49884  */
49885 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49886     this.mgr = mgr;
49887     this.position  = pos;
49888     this.events = {
49889         /**
49890          * @scope Roo.BasicLayoutRegion
49891          */
49892         
49893         /**
49894          * @event beforeremove
49895          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49896          * @param {Roo.LayoutRegion} this
49897          * @param {Roo.ContentPanel} panel The panel
49898          * @param {Object} e The cancel event object
49899          */
49900         "beforeremove" : true,
49901         /**
49902          * @event invalidated
49903          * Fires when the layout for this region is changed.
49904          * @param {Roo.LayoutRegion} this
49905          */
49906         "invalidated" : true,
49907         /**
49908          * @event visibilitychange
49909          * Fires when this region is shown or hidden 
49910          * @param {Roo.LayoutRegion} this
49911          * @param {Boolean} visibility true or false
49912          */
49913         "visibilitychange" : true,
49914         /**
49915          * @event paneladded
49916          * Fires when a panel is added. 
49917          * @param {Roo.LayoutRegion} this
49918          * @param {Roo.ContentPanel} panel The panel
49919          */
49920         "paneladded" : true,
49921         /**
49922          * @event panelremoved
49923          * Fires when a panel is removed. 
49924          * @param {Roo.LayoutRegion} this
49925          * @param {Roo.ContentPanel} panel The panel
49926          */
49927         "panelremoved" : true,
49928         /**
49929          * @event collapsed
49930          * Fires when this region is collapsed.
49931          * @param {Roo.LayoutRegion} this
49932          */
49933         "collapsed" : true,
49934         /**
49935          * @event expanded
49936          * Fires when this region is expanded.
49937          * @param {Roo.LayoutRegion} this
49938          */
49939         "expanded" : true,
49940         /**
49941          * @event slideshow
49942          * Fires when this region is slid into view.
49943          * @param {Roo.LayoutRegion} this
49944          */
49945         "slideshow" : true,
49946         /**
49947          * @event slidehide
49948          * Fires when this region slides out of view. 
49949          * @param {Roo.LayoutRegion} this
49950          */
49951         "slidehide" : true,
49952         /**
49953          * @event panelactivated
49954          * Fires when a panel is activated. 
49955          * @param {Roo.LayoutRegion} this
49956          * @param {Roo.ContentPanel} panel The activated panel
49957          */
49958         "panelactivated" : true,
49959         /**
49960          * @event resized
49961          * Fires when the user resizes this region. 
49962          * @param {Roo.LayoutRegion} this
49963          * @param {Number} newSize The new size (width for east/west, height for north/south)
49964          */
49965         "resized" : true
49966     };
49967     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49968     this.panels = new Roo.util.MixedCollection();
49969     this.panels.getKey = this.getPanelId.createDelegate(this);
49970     this.box = null;
49971     this.activePanel = null;
49972     // ensure listeners are added...
49973     
49974     if (config.listeners || config.events) {
49975         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49976             listeners : config.listeners || {},
49977             events : config.events || {}
49978         });
49979     }
49980     
49981     if(skipConfig !== true){
49982         this.applyConfig(config);
49983     }
49984 };
49985
49986 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49987     getPanelId : function(p){
49988         return p.getId();
49989     },
49990     
49991     applyConfig : function(config){
49992         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49993         this.config = config;
49994         
49995     },
49996     
49997     /**
49998      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49999      * the width, for horizontal (north, south) the height.
50000      * @param {Number} newSize The new width or height
50001      */
50002     resizeTo : function(newSize){
50003         var el = this.el ? this.el :
50004                  (this.activePanel ? this.activePanel.getEl() : null);
50005         if(el){
50006             switch(this.position){
50007                 case "east":
50008                 case "west":
50009                     el.setWidth(newSize);
50010                     this.fireEvent("resized", this, newSize);
50011                 break;
50012                 case "north":
50013                 case "south":
50014                     el.setHeight(newSize);
50015                     this.fireEvent("resized", this, newSize);
50016                 break;                
50017             }
50018         }
50019     },
50020     
50021     getBox : function(){
50022         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50023     },
50024     
50025     getMargins : function(){
50026         return this.margins;
50027     },
50028     
50029     updateBox : function(box){
50030         this.box = box;
50031         var el = this.activePanel.getEl();
50032         el.dom.style.left = box.x + "px";
50033         el.dom.style.top = box.y + "px";
50034         this.activePanel.setSize(box.width, box.height);
50035     },
50036     
50037     /**
50038      * Returns the container element for this region.
50039      * @return {Roo.Element}
50040      */
50041     getEl : function(){
50042         return this.activePanel;
50043     },
50044     
50045     /**
50046      * Returns true if this region is currently visible.
50047      * @return {Boolean}
50048      */
50049     isVisible : function(){
50050         return this.activePanel ? true : false;
50051     },
50052     
50053     setActivePanel : function(panel){
50054         panel = this.getPanel(panel);
50055         if(this.activePanel && this.activePanel != panel){
50056             this.activePanel.setActiveState(false);
50057             this.activePanel.getEl().setLeftTop(-10000,-10000);
50058         }
50059         this.activePanel = panel;
50060         panel.setActiveState(true);
50061         if(this.box){
50062             panel.setSize(this.box.width, this.box.height);
50063         }
50064         this.fireEvent("panelactivated", this, panel);
50065         this.fireEvent("invalidated");
50066     },
50067     
50068     /**
50069      * Show the specified panel.
50070      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50071      * @return {Roo.ContentPanel} The shown panel or null
50072      */
50073     showPanel : function(panel){
50074         if(panel = this.getPanel(panel)){
50075             this.setActivePanel(panel);
50076         }
50077         return panel;
50078     },
50079     
50080     /**
50081      * Get the active panel for this region.
50082      * @return {Roo.ContentPanel} The active panel or null
50083      */
50084     getActivePanel : function(){
50085         return this.activePanel;
50086     },
50087     
50088     /**
50089      * Add the passed ContentPanel(s)
50090      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50091      * @return {Roo.ContentPanel} The panel added (if only one was added)
50092      */
50093     add : function(panel){
50094         if(arguments.length > 1){
50095             for(var i = 0, len = arguments.length; i < len; i++) {
50096                 this.add(arguments[i]);
50097             }
50098             return null;
50099         }
50100         if(this.hasPanel(panel)){
50101             this.showPanel(panel);
50102             return panel;
50103         }
50104         var el = panel.getEl();
50105         if(el.dom.parentNode != this.mgr.el.dom){
50106             this.mgr.el.dom.appendChild(el.dom);
50107         }
50108         if(panel.setRegion){
50109             panel.setRegion(this);
50110         }
50111         this.panels.add(panel);
50112         el.setStyle("position", "absolute");
50113         if(!panel.background){
50114             this.setActivePanel(panel);
50115             if(this.config.initialSize && this.panels.getCount()==1){
50116                 this.resizeTo(this.config.initialSize);
50117             }
50118         }
50119         this.fireEvent("paneladded", this, panel);
50120         return panel;
50121     },
50122     
50123     /**
50124      * Returns true if the panel is in this region.
50125      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50126      * @return {Boolean}
50127      */
50128     hasPanel : function(panel){
50129         if(typeof panel == "object"){ // must be panel obj
50130             panel = panel.getId();
50131         }
50132         return this.getPanel(panel) ? true : false;
50133     },
50134     
50135     /**
50136      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50137      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50138      * @param {Boolean} preservePanel Overrides the config preservePanel option
50139      * @return {Roo.ContentPanel} The panel that was removed
50140      */
50141     remove : function(panel, preservePanel){
50142         panel = this.getPanel(panel);
50143         if(!panel){
50144             return null;
50145         }
50146         var e = {};
50147         this.fireEvent("beforeremove", this, panel, e);
50148         if(e.cancel === true){
50149             return null;
50150         }
50151         var panelId = panel.getId();
50152         this.panels.removeKey(panelId);
50153         return panel;
50154     },
50155     
50156     /**
50157      * Returns the panel specified or null if it's not in this region.
50158      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50159      * @return {Roo.ContentPanel}
50160      */
50161     getPanel : function(id){
50162         if(typeof id == "object"){ // must be panel obj
50163             return id;
50164         }
50165         return this.panels.get(id);
50166     },
50167     
50168     /**
50169      * Returns this regions position (north/south/east/west/center).
50170      * @return {String} 
50171      */
50172     getPosition: function(){
50173         return this.position;    
50174     }
50175 });/*
50176  * Based on:
50177  * Ext JS Library 1.1.1
50178  * Copyright(c) 2006-2007, Ext JS, LLC.
50179  *
50180  * Originally Released Under LGPL - original licence link has changed is not relivant.
50181  *
50182  * Fork - LGPL
50183  * <script type="text/javascript">
50184  */
50185  
50186 /**
50187  * @class Roo.LayoutRegion
50188  * @extends Roo.BasicLayoutRegion
50189  * This class represents a region in a layout manager.
50190  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50191  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50192  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50193  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50194  * @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})
50195  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50196  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50197  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50198  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50199  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50200  * @cfg {String}    title           The title for the region (overrides panel titles)
50201  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50202  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50203  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50204  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50205  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50206  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50207  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50208  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50209  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50210  * @cfg {Boolean}   showPin         True to show a pin button
50211  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50212  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50213  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50214  * @cfg {Number}    width           For East/West panels
50215  * @cfg {Number}    height          For North/South panels
50216  * @cfg {Boolean}   split           To show the splitter
50217  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50218  */
50219 Roo.LayoutRegion = function(mgr, config, pos){
50220     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50221     var dh = Roo.DomHelper;
50222     /** This region's container element 
50223     * @type Roo.Element */
50224     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50225     /** This region's title element 
50226     * @type Roo.Element */
50227
50228     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50229         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50230         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50231     ]}, true);
50232     this.titleEl.enableDisplayMode();
50233     /** This region's title text element 
50234     * @type HTMLElement */
50235     this.titleTextEl = this.titleEl.dom.firstChild;
50236     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50237     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50238     this.closeBtn.enableDisplayMode();
50239     this.closeBtn.on("click", this.closeClicked, this);
50240     this.closeBtn.hide();
50241
50242     this.createBody(config);
50243     this.visible = true;
50244     this.collapsed = false;
50245
50246     if(config.hideWhenEmpty){
50247         this.hide();
50248         this.on("paneladded", this.validateVisibility, this);
50249         this.on("panelremoved", this.validateVisibility, this);
50250     }
50251     this.applyConfig(config);
50252 };
50253
50254 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50255
50256     createBody : function(){
50257         /** This region's body element 
50258         * @type Roo.Element */
50259         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50260     },
50261
50262     applyConfig : function(c){
50263         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50264             var dh = Roo.DomHelper;
50265             if(c.titlebar !== false){
50266                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50267                 this.collapseBtn.on("click", this.collapse, this);
50268                 this.collapseBtn.enableDisplayMode();
50269
50270                 if(c.showPin === true || this.showPin){
50271                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50272                     this.stickBtn.enableDisplayMode();
50273                     this.stickBtn.on("click", this.expand, this);
50274                     this.stickBtn.hide();
50275                 }
50276             }
50277             /** This region's collapsed element
50278             * @type Roo.Element */
50279             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50280                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50281             ]}, true);
50282             if(c.floatable !== false){
50283                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50284                this.collapsedEl.on("click", this.collapseClick, this);
50285             }
50286
50287             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50288                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50289                    id: "message", unselectable: "on", style:{"float":"left"}});
50290                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50291              }
50292             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50293             this.expandBtn.on("click", this.expand, this);
50294         }
50295         if(this.collapseBtn){
50296             this.collapseBtn.setVisible(c.collapsible == true);
50297         }
50298         this.cmargins = c.cmargins || this.cmargins ||
50299                          (this.position == "west" || this.position == "east" ?
50300                              {top: 0, left: 2, right:2, bottom: 0} :
50301                              {top: 2, left: 0, right:0, bottom: 2});
50302         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50303         this.bottomTabs = c.tabPosition != "top";
50304         this.autoScroll = c.autoScroll || false;
50305         if(this.autoScroll){
50306             this.bodyEl.setStyle("overflow", "auto");
50307         }else{
50308             this.bodyEl.setStyle("overflow", "hidden");
50309         }
50310         //if(c.titlebar !== false){
50311             if((!c.titlebar && !c.title) || c.titlebar === false){
50312                 this.titleEl.hide();
50313             }else{
50314                 this.titleEl.show();
50315                 if(c.title){
50316                     this.titleTextEl.innerHTML = c.title;
50317                 }
50318             }
50319         //}
50320         this.duration = c.duration || .30;
50321         this.slideDuration = c.slideDuration || .45;
50322         this.config = c;
50323         if(c.collapsed){
50324             this.collapse(true);
50325         }
50326         if(c.hidden){
50327             this.hide();
50328         }
50329     },
50330     /**
50331      * Returns true if this region is currently visible.
50332      * @return {Boolean}
50333      */
50334     isVisible : function(){
50335         return this.visible;
50336     },
50337
50338     /**
50339      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50340      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50341      */
50342     setCollapsedTitle : function(title){
50343         title = title || "&#160;";
50344         if(this.collapsedTitleTextEl){
50345             this.collapsedTitleTextEl.innerHTML = title;
50346         }
50347     },
50348
50349     getBox : function(){
50350         var b;
50351         if(!this.collapsed){
50352             b = this.el.getBox(false, true);
50353         }else{
50354             b = this.collapsedEl.getBox(false, true);
50355         }
50356         return b;
50357     },
50358
50359     getMargins : function(){
50360         return this.collapsed ? this.cmargins : this.margins;
50361     },
50362
50363     highlight : function(){
50364         this.el.addClass("x-layout-panel-dragover");
50365     },
50366
50367     unhighlight : function(){
50368         this.el.removeClass("x-layout-panel-dragover");
50369     },
50370
50371     updateBox : function(box){
50372         this.box = box;
50373         if(!this.collapsed){
50374             this.el.dom.style.left = box.x + "px";
50375             this.el.dom.style.top = box.y + "px";
50376             this.updateBody(box.width, box.height);
50377         }else{
50378             this.collapsedEl.dom.style.left = box.x + "px";
50379             this.collapsedEl.dom.style.top = box.y + "px";
50380             this.collapsedEl.setSize(box.width, box.height);
50381         }
50382         if(this.tabs){
50383             this.tabs.autoSizeTabs();
50384         }
50385     },
50386
50387     updateBody : function(w, h){
50388         if(w !== null){
50389             this.el.setWidth(w);
50390             w -= this.el.getBorderWidth("rl");
50391             if(this.config.adjustments){
50392                 w += this.config.adjustments[0];
50393             }
50394         }
50395         if(h !== null){
50396             this.el.setHeight(h);
50397             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50398             h -= this.el.getBorderWidth("tb");
50399             if(this.config.adjustments){
50400                 h += this.config.adjustments[1];
50401             }
50402             this.bodyEl.setHeight(h);
50403             if(this.tabs){
50404                 h = this.tabs.syncHeight(h);
50405             }
50406         }
50407         if(this.panelSize){
50408             w = w !== null ? w : this.panelSize.width;
50409             h = h !== null ? h : this.panelSize.height;
50410         }
50411         if(this.activePanel){
50412             var el = this.activePanel.getEl();
50413             w = w !== null ? w : el.getWidth();
50414             h = h !== null ? h : el.getHeight();
50415             this.panelSize = {width: w, height: h};
50416             this.activePanel.setSize(w, h);
50417         }
50418         if(Roo.isIE && this.tabs){
50419             this.tabs.el.repaint();
50420         }
50421     },
50422
50423     /**
50424      * Returns the container element for this region.
50425      * @return {Roo.Element}
50426      */
50427     getEl : function(){
50428         return this.el;
50429     },
50430
50431     /**
50432      * Hides this region.
50433      */
50434     hide : function(){
50435         if(!this.collapsed){
50436             this.el.dom.style.left = "-2000px";
50437             this.el.hide();
50438         }else{
50439             this.collapsedEl.dom.style.left = "-2000px";
50440             this.collapsedEl.hide();
50441         }
50442         this.visible = false;
50443         this.fireEvent("visibilitychange", this, false);
50444     },
50445
50446     /**
50447      * Shows this region if it was previously hidden.
50448      */
50449     show : function(){
50450         if(!this.collapsed){
50451             this.el.show();
50452         }else{
50453             this.collapsedEl.show();
50454         }
50455         this.visible = true;
50456         this.fireEvent("visibilitychange", this, true);
50457     },
50458
50459     closeClicked : function(){
50460         if(this.activePanel){
50461             this.remove(this.activePanel);
50462         }
50463     },
50464
50465     collapseClick : function(e){
50466         if(this.isSlid){
50467            e.stopPropagation();
50468            this.slideIn();
50469         }else{
50470            e.stopPropagation();
50471            this.slideOut();
50472         }
50473     },
50474
50475     /**
50476      * Collapses this region.
50477      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50478      */
50479     collapse : function(skipAnim){
50480         if(this.collapsed) return;
50481         this.collapsed = true;
50482         if(this.split){
50483             this.split.el.hide();
50484         }
50485         if(this.config.animate && skipAnim !== true){
50486             this.fireEvent("invalidated", this);
50487             this.animateCollapse();
50488         }else{
50489             this.el.setLocation(-20000,-20000);
50490             this.el.hide();
50491             this.collapsedEl.show();
50492             this.fireEvent("collapsed", this);
50493             this.fireEvent("invalidated", this);
50494         }
50495     },
50496
50497     animateCollapse : function(){
50498         // overridden
50499     },
50500
50501     /**
50502      * Expands this region if it was previously collapsed.
50503      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50504      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50505      */
50506     expand : function(e, skipAnim){
50507         if(e) e.stopPropagation();
50508         if(!this.collapsed || this.el.hasActiveFx()) return;
50509         if(this.isSlid){
50510             this.afterSlideIn();
50511             skipAnim = true;
50512         }
50513         this.collapsed = false;
50514         if(this.config.animate && skipAnim !== true){
50515             this.animateExpand();
50516         }else{
50517             this.el.show();
50518             if(this.split){
50519                 this.split.el.show();
50520             }
50521             this.collapsedEl.setLocation(-2000,-2000);
50522             this.collapsedEl.hide();
50523             this.fireEvent("invalidated", this);
50524             this.fireEvent("expanded", this);
50525         }
50526     },
50527
50528     animateExpand : function(){
50529         // overridden
50530     },
50531
50532     initTabs : function()
50533     {
50534         this.bodyEl.setStyle("overflow", "hidden");
50535         var ts = new Roo.TabPanel(
50536                 this.bodyEl.dom,
50537                 {
50538                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50539                     disableTooltips: this.config.disableTabTips,
50540                     toolbar : this.config.toolbar
50541                 }
50542         );
50543         if(this.config.hideTabs){
50544             ts.stripWrap.setDisplayed(false);
50545         }
50546         this.tabs = ts;
50547         ts.resizeTabs = this.config.resizeTabs === true;
50548         ts.minTabWidth = this.config.minTabWidth || 40;
50549         ts.maxTabWidth = this.config.maxTabWidth || 250;
50550         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50551         ts.monitorResize = false;
50552         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50553         ts.bodyEl.addClass('x-layout-tabs-body');
50554         this.panels.each(this.initPanelAsTab, this);
50555     },
50556
50557     initPanelAsTab : function(panel){
50558         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50559                     this.config.closeOnTab && panel.isClosable());
50560         if(panel.tabTip !== undefined){
50561             ti.setTooltip(panel.tabTip);
50562         }
50563         ti.on("activate", function(){
50564               this.setActivePanel(panel);
50565         }, this);
50566         if(this.config.closeOnTab){
50567             ti.on("beforeclose", function(t, e){
50568                 e.cancel = true;
50569                 this.remove(panel);
50570             }, this);
50571         }
50572         return ti;
50573     },
50574
50575     updatePanelTitle : function(panel, title){
50576         if(this.activePanel == panel){
50577             this.updateTitle(title);
50578         }
50579         if(this.tabs){
50580             var ti = this.tabs.getTab(panel.getEl().id);
50581             ti.setText(title);
50582             if(panel.tabTip !== undefined){
50583                 ti.setTooltip(panel.tabTip);
50584             }
50585         }
50586     },
50587
50588     updateTitle : function(title){
50589         if(this.titleTextEl && !this.config.title){
50590             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50591         }
50592     },
50593
50594     setActivePanel : function(panel){
50595         panel = this.getPanel(panel);
50596         if(this.activePanel && this.activePanel != panel){
50597             this.activePanel.setActiveState(false);
50598         }
50599         this.activePanel = panel;
50600         panel.setActiveState(true);
50601         if(this.panelSize){
50602             panel.setSize(this.panelSize.width, this.panelSize.height);
50603         }
50604         if(this.closeBtn){
50605             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50606         }
50607         this.updateTitle(panel.getTitle());
50608         if(this.tabs){
50609             this.fireEvent("invalidated", this);
50610         }
50611         this.fireEvent("panelactivated", this, panel);
50612     },
50613
50614     /**
50615      * Shows the specified panel.
50616      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50617      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50618      */
50619     showPanel : function(panel)
50620     {
50621         panel = this.getPanel(panel);
50622         if(panel){
50623             if(this.tabs){
50624                 var tab = this.tabs.getTab(panel.getEl().id);
50625                 if(tab.isHidden()){
50626                     this.tabs.unhideTab(tab.id);
50627                 }
50628                 tab.activate();
50629             }else{
50630                 this.setActivePanel(panel);
50631             }
50632         }
50633         return panel;
50634     },
50635
50636     /**
50637      * Get the active panel for this region.
50638      * @return {Roo.ContentPanel} The active panel or null
50639      */
50640     getActivePanel : function(){
50641         return this.activePanel;
50642     },
50643
50644     validateVisibility : function(){
50645         if(this.panels.getCount() < 1){
50646             this.updateTitle("&#160;");
50647             this.closeBtn.hide();
50648             this.hide();
50649         }else{
50650             if(!this.isVisible()){
50651                 this.show();
50652             }
50653         }
50654     },
50655
50656     /**
50657      * Adds the passed ContentPanel(s) to this region.
50658      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50659      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50660      */
50661     add : function(panel){
50662         if(arguments.length > 1){
50663             for(var i = 0, len = arguments.length; i < len; i++) {
50664                 this.add(arguments[i]);
50665             }
50666             return null;
50667         }
50668         if(this.hasPanel(panel)){
50669             this.showPanel(panel);
50670             return panel;
50671         }
50672         panel.setRegion(this);
50673         this.panels.add(panel);
50674         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50675             this.bodyEl.dom.appendChild(panel.getEl().dom);
50676             if(panel.background !== true){
50677                 this.setActivePanel(panel);
50678             }
50679             this.fireEvent("paneladded", this, panel);
50680             return panel;
50681         }
50682         if(!this.tabs){
50683             this.initTabs();
50684         }else{
50685             this.initPanelAsTab(panel);
50686         }
50687         if(panel.background !== true){
50688             this.tabs.activate(panel.getEl().id);
50689         }
50690         this.fireEvent("paneladded", this, panel);
50691         return panel;
50692     },
50693
50694     /**
50695      * Hides the tab for the specified panel.
50696      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50697      */
50698     hidePanel : function(panel){
50699         if(this.tabs && (panel = this.getPanel(panel))){
50700             this.tabs.hideTab(panel.getEl().id);
50701         }
50702     },
50703
50704     /**
50705      * Unhides the tab for a previously hidden panel.
50706      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50707      */
50708     unhidePanel : function(panel){
50709         if(this.tabs && (panel = this.getPanel(panel))){
50710             this.tabs.unhideTab(panel.getEl().id);
50711         }
50712     },
50713
50714     clearPanels : function(){
50715         while(this.panels.getCount() > 0){
50716              this.remove(this.panels.first());
50717         }
50718     },
50719
50720     /**
50721      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50722      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50723      * @param {Boolean} preservePanel Overrides the config preservePanel option
50724      * @return {Roo.ContentPanel} The panel that was removed
50725      */
50726     remove : function(panel, preservePanel){
50727         panel = this.getPanel(panel);
50728         if(!panel){
50729             return null;
50730         }
50731         var e = {};
50732         this.fireEvent("beforeremove", this, panel, e);
50733         if(e.cancel === true){
50734             return null;
50735         }
50736         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50737         var panelId = panel.getId();
50738         this.panels.removeKey(panelId);
50739         if(preservePanel){
50740             document.body.appendChild(panel.getEl().dom);
50741         }
50742         if(this.tabs){
50743             this.tabs.removeTab(panel.getEl().id);
50744         }else if (!preservePanel){
50745             this.bodyEl.dom.removeChild(panel.getEl().dom);
50746         }
50747         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50748             var p = this.panels.first();
50749             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50750             tempEl.appendChild(p.getEl().dom);
50751             this.bodyEl.update("");
50752             this.bodyEl.dom.appendChild(p.getEl().dom);
50753             tempEl = null;
50754             this.updateTitle(p.getTitle());
50755             this.tabs = null;
50756             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50757             this.setActivePanel(p);
50758         }
50759         panel.setRegion(null);
50760         if(this.activePanel == panel){
50761             this.activePanel = null;
50762         }
50763         if(this.config.autoDestroy !== false && preservePanel !== true){
50764             try{panel.destroy();}catch(e){}
50765         }
50766         this.fireEvent("panelremoved", this, panel);
50767         return panel;
50768     },
50769
50770     /**
50771      * Returns the TabPanel component used by this region
50772      * @return {Roo.TabPanel}
50773      */
50774     getTabs : function(){
50775         return this.tabs;
50776     },
50777
50778     createTool : function(parentEl, className){
50779         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50780             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50781         btn.addClassOnOver("x-layout-tools-button-over");
50782         return btn;
50783     }
50784 });/*
50785  * Based on:
50786  * Ext JS Library 1.1.1
50787  * Copyright(c) 2006-2007, Ext JS, LLC.
50788  *
50789  * Originally Released Under LGPL - original licence link has changed is not relivant.
50790  *
50791  * Fork - LGPL
50792  * <script type="text/javascript">
50793  */
50794  
50795
50796
50797 /**
50798  * @class Roo.SplitLayoutRegion
50799  * @extends Roo.LayoutRegion
50800  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50801  */
50802 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50803     this.cursor = cursor;
50804     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50805 };
50806
50807 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50808     splitTip : "Drag to resize.",
50809     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50810     useSplitTips : false,
50811
50812     applyConfig : function(config){
50813         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50814         if(config.split){
50815             if(!this.split){
50816                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50817                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50818                 /** The SplitBar for this region 
50819                 * @type Roo.SplitBar */
50820                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50821                 this.split.on("moved", this.onSplitMove, this);
50822                 this.split.useShim = config.useShim === true;
50823                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50824                 if(this.useSplitTips){
50825                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50826                 }
50827                 if(config.collapsible){
50828                     this.split.el.on("dblclick", this.collapse,  this);
50829                 }
50830             }
50831             if(typeof config.minSize != "undefined"){
50832                 this.split.minSize = config.minSize;
50833             }
50834             if(typeof config.maxSize != "undefined"){
50835                 this.split.maxSize = config.maxSize;
50836             }
50837             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50838                 this.hideSplitter();
50839             }
50840         }
50841     },
50842
50843     getHMaxSize : function(){
50844          var cmax = this.config.maxSize || 10000;
50845          var center = this.mgr.getRegion("center");
50846          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50847     },
50848
50849     getVMaxSize : function(){
50850          var cmax = this.config.maxSize || 10000;
50851          var center = this.mgr.getRegion("center");
50852          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50853     },
50854
50855     onSplitMove : function(split, newSize){
50856         this.fireEvent("resized", this, newSize);
50857     },
50858     
50859     /** 
50860      * Returns the {@link Roo.SplitBar} for this region.
50861      * @return {Roo.SplitBar}
50862      */
50863     getSplitBar : function(){
50864         return this.split;
50865     },
50866     
50867     hide : function(){
50868         this.hideSplitter();
50869         Roo.SplitLayoutRegion.superclass.hide.call(this);
50870     },
50871
50872     hideSplitter : function(){
50873         if(this.split){
50874             this.split.el.setLocation(-2000,-2000);
50875             this.split.el.hide();
50876         }
50877     },
50878
50879     show : function(){
50880         if(this.split){
50881             this.split.el.show();
50882         }
50883         Roo.SplitLayoutRegion.superclass.show.call(this);
50884     },
50885     
50886     beforeSlide: function(){
50887         if(Roo.isGecko){// firefox overflow auto bug workaround
50888             this.bodyEl.clip();
50889             if(this.tabs) this.tabs.bodyEl.clip();
50890             if(this.activePanel){
50891                 this.activePanel.getEl().clip();
50892                 
50893                 if(this.activePanel.beforeSlide){
50894                     this.activePanel.beforeSlide();
50895                 }
50896             }
50897         }
50898     },
50899     
50900     afterSlide : function(){
50901         if(Roo.isGecko){// firefox overflow auto bug workaround
50902             this.bodyEl.unclip();
50903             if(this.tabs) this.tabs.bodyEl.unclip();
50904             if(this.activePanel){
50905                 this.activePanel.getEl().unclip();
50906                 if(this.activePanel.afterSlide){
50907                     this.activePanel.afterSlide();
50908                 }
50909             }
50910         }
50911     },
50912
50913     initAutoHide : function(){
50914         if(this.autoHide !== false){
50915             if(!this.autoHideHd){
50916                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50917                 this.autoHideHd = {
50918                     "mouseout": function(e){
50919                         if(!e.within(this.el, true)){
50920                             st.delay(500);
50921                         }
50922                     },
50923                     "mouseover" : function(e){
50924                         st.cancel();
50925                     },
50926                     scope : this
50927                 };
50928             }
50929             this.el.on(this.autoHideHd);
50930         }
50931     },
50932
50933     clearAutoHide : function(){
50934         if(this.autoHide !== false){
50935             this.el.un("mouseout", this.autoHideHd.mouseout);
50936             this.el.un("mouseover", this.autoHideHd.mouseover);
50937         }
50938     },
50939
50940     clearMonitor : function(){
50941         Roo.get(document).un("click", this.slideInIf, this);
50942     },
50943
50944     // these names are backwards but not changed for compat
50945     slideOut : function(){
50946         if(this.isSlid || this.el.hasActiveFx()){
50947             return;
50948         }
50949         this.isSlid = true;
50950         if(this.collapseBtn){
50951             this.collapseBtn.hide();
50952         }
50953         this.closeBtnState = this.closeBtn.getStyle('display');
50954         this.closeBtn.hide();
50955         if(this.stickBtn){
50956             this.stickBtn.show();
50957         }
50958         this.el.show();
50959         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50960         this.beforeSlide();
50961         this.el.setStyle("z-index", 10001);
50962         this.el.slideIn(this.getSlideAnchor(), {
50963             callback: function(){
50964                 this.afterSlide();
50965                 this.initAutoHide();
50966                 Roo.get(document).on("click", this.slideInIf, this);
50967                 this.fireEvent("slideshow", this);
50968             },
50969             scope: this,
50970             block: true
50971         });
50972     },
50973
50974     afterSlideIn : function(){
50975         this.clearAutoHide();
50976         this.isSlid = false;
50977         this.clearMonitor();
50978         this.el.setStyle("z-index", "");
50979         if(this.collapseBtn){
50980             this.collapseBtn.show();
50981         }
50982         this.closeBtn.setStyle('display', this.closeBtnState);
50983         if(this.stickBtn){
50984             this.stickBtn.hide();
50985         }
50986         this.fireEvent("slidehide", this);
50987     },
50988
50989     slideIn : function(cb){
50990         if(!this.isSlid || this.el.hasActiveFx()){
50991             Roo.callback(cb);
50992             return;
50993         }
50994         this.isSlid = false;
50995         this.beforeSlide();
50996         this.el.slideOut(this.getSlideAnchor(), {
50997             callback: function(){
50998                 this.el.setLeftTop(-10000, -10000);
50999                 this.afterSlide();
51000                 this.afterSlideIn();
51001                 Roo.callback(cb);
51002             },
51003             scope: this,
51004             block: true
51005         });
51006     },
51007     
51008     slideInIf : function(e){
51009         if(!e.within(this.el)){
51010             this.slideIn();
51011         }
51012     },
51013
51014     animateCollapse : function(){
51015         this.beforeSlide();
51016         this.el.setStyle("z-index", 20000);
51017         var anchor = this.getSlideAnchor();
51018         this.el.slideOut(anchor, {
51019             callback : function(){
51020                 this.el.setStyle("z-index", "");
51021                 this.collapsedEl.slideIn(anchor, {duration:.3});
51022                 this.afterSlide();
51023                 this.el.setLocation(-10000,-10000);
51024                 this.el.hide();
51025                 this.fireEvent("collapsed", this);
51026             },
51027             scope: this,
51028             block: true
51029         });
51030     },
51031
51032     animateExpand : function(){
51033         this.beforeSlide();
51034         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51035         this.el.setStyle("z-index", 20000);
51036         this.collapsedEl.hide({
51037             duration:.1
51038         });
51039         this.el.slideIn(this.getSlideAnchor(), {
51040             callback : function(){
51041                 this.el.setStyle("z-index", "");
51042                 this.afterSlide();
51043                 if(this.split){
51044                     this.split.el.show();
51045                 }
51046                 this.fireEvent("invalidated", this);
51047                 this.fireEvent("expanded", this);
51048             },
51049             scope: this,
51050             block: true
51051         });
51052     },
51053
51054     anchors : {
51055         "west" : "left",
51056         "east" : "right",
51057         "north" : "top",
51058         "south" : "bottom"
51059     },
51060
51061     sanchors : {
51062         "west" : "l",
51063         "east" : "r",
51064         "north" : "t",
51065         "south" : "b"
51066     },
51067
51068     canchors : {
51069         "west" : "tl-tr",
51070         "east" : "tr-tl",
51071         "north" : "tl-bl",
51072         "south" : "bl-tl"
51073     },
51074
51075     getAnchor : function(){
51076         return this.anchors[this.position];
51077     },
51078
51079     getCollapseAnchor : function(){
51080         return this.canchors[this.position];
51081     },
51082
51083     getSlideAnchor : function(){
51084         return this.sanchors[this.position];
51085     },
51086
51087     getAlignAdj : function(){
51088         var cm = this.cmargins;
51089         switch(this.position){
51090             case "west":
51091                 return [0, 0];
51092             break;
51093             case "east":
51094                 return [0, 0];
51095             break;
51096             case "north":
51097                 return [0, 0];
51098             break;
51099             case "south":
51100                 return [0, 0];
51101             break;
51102         }
51103     },
51104
51105     getExpandAdj : function(){
51106         var c = this.collapsedEl, cm = this.cmargins;
51107         switch(this.position){
51108             case "west":
51109                 return [-(cm.right+c.getWidth()+cm.left), 0];
51110             break;
51111             case "east":
51112                 return [cm.right+c.getWidth()+cm.left, 0];
51113             break;
51114             case "north":
51115                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51116             break;
51117             case "south":
51118                 return [0, cm.top+cm.bottom+c.getHeight()];
51119             break;
51120         }
51121     }
51122 });/*
51123  * Based on:
51124  * Ext JS Library 1.1.1
51125  * Copyright(c) 2006-2007, Ext JS, LLC.
51126  *
51127  * Originally Released Under LGPL - original licence link has changed is not relivant.
51128  *
51129  * Fork - LGPL
51130  * <script type="text/javascript">
51131  */
51132 /*
51133  * These classes are private internal classes
51134  */
51135 Roo.CenterLayoutRegion = function(mgr, config){
51136     Roo.LayoutRegion.call(this, mgr, config, "center");
51137     this.visible = true;
51138     this.minWidth = config.minWidth || 20;
51139     this.minHeight = config.minHeight || 20;
51140 };
51141
51142 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51143     hide : function(){
51144         // center panel can't be hidden
51145     },
51146     
51147     show : function(){
51148         // center panel can't be hidden
51149     },
51150     
51151     getMinWidth: function(){
51152         return this.minWidth;
51153     },
51154     
51155     getMinHeight: function(){
51156         return this.minHeight;
51157     }
51158 });
51159
51160
51161 Roo.NorthLayoutRegion = function(mgr, config){
51162     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51163     if(this.split){
51164         this.split.placement = Roo.SplitBar.TOP;
51165         this.split.orientation = Roo.SplitBar.VERTICAL;
51166         this.split.el.addClass("x-layout-split-v");
51167     }
51168     var size = config.initialSize || config.height;
51169     if(typeof size != "undefined"){
51170         this.el.setHeight(size);
51171     }
51172 };
51173 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51174     orientation: Roo.SplitBar.VERTICAL,
51175     getBox : function(){
51176         if(this.collapsed){
51177             return this.collapsedEl.getBox();
51178         }
51179         var box = this.el.getBox();
51180         if(this.split){
51181             box.height += this.split.el.getHeight();
51182         }
51183         return box;
51184     },
51185     
51186     updateBox : function(box){
51187         if(this.split && !this.collapsed){
51188             box.height -= this.split.el.getHeight();
51189             this.split.el.setLeft(box.x);
51190             this.split.el.setTop(box.y+box.height);
51191             this.split.el.setWidth(box.width);
51192         }
51193         if(this.collapsed){
51194             this.updateBody(box.width, null);
51195         }
51196         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51197     }
51198 });
51199
51200 Roo.SouthLayoutRegion = function(mgr, config){
51201     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51202     if(this.split){
51203         this.split.placement = Roo.SplitBar.BOTTOM;
51204         this.split.orientation = Roo.SplitBar.VERTICAL;
51205         this.split.el.addClass("x-layout-split-v");
51206     }
51207     var size = config.initialSize || config.height;
51208     if(typeof size != "undefined"){
51209         this.el.setHeight(size);
51210     }
51211 };
51212 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51213     orientation: Roo.SplitBar.VERTICAL,
51214     getBox : function(){
51215         if(this.collapsed){
51216             return this.collapsedEl.getBox();
51217         }
51218         var box = this.el.getBox();
51219         if(this.split){
51220             var sh = this.split.el.getHeight();
51221             box.height += sh;
51222             box.y -= sh;
51223         }
51224         return box;
51225     },
51226     
51227     updateBox : function(box){
51228         if(this.split && !this.collapsed){
51229             var sh = this.split.el.getHeight();
51230             box.height -= sh;
51231             box.y += sh;
51232             this.split.el.setLeft(box.x);
51233             this.split.el.setTop(box.y-sh);
51234             this.split.el.setWidth(box.width);
51235         }
51236         if(this.collapsed){
51237             this.updateBody(box.width, null);
51238         }
51239         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51240     }
51241 });
51242
51243 Roo.EastLayoutRegion = function(mgr, config){
51244     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51245     if(this.split){
51246         this.split.placement = Roo.SplitBar.RIGHT;
51247         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51248         this.split.el.addClass("x-layout-split-h");
51249     }
51250     var size = config.initialSize || config.width;
51251     if(typeof size != "undefined"){
51252         this.el.setWidth(size);
51253     }
51254 };
51255 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51256     orientation: Roo.SplitBar.HORIZONTAL,
51257     getBox : function(){
51258         if(this.collapsed){
51259             return this.collapsedEl.getBox();
51260         }
51261         var box = this.el.getBox();
51262         if(this.split){
51263             var sw = this.split.el.getWidth();
51264             box.width += sw;
51265             box.x -= sw;
51266         }
51267         return box;
51268     },
51269
51270     updateBox : function(box){
51271         if(this.split && !this.collapsed){
51272             var sw = this.split.el.getWidth();
51273             box.width -= sw;
51274             this.split.el.setLeft(box.x);
51275             this.split.el.setTop(box.y);
51276             this.split.el.setHeight(box.height);
51277             box.x += sw;
51278         }
51279         if(this.collapsed){
51280             this.updateBody(null, box.height);
51281         }
51282         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51283     }
51284 });
51285
51286 Roo.WestLayoutRegion = function(mgr, config){
51287     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51288     if(this.split){
51289         this.split.placement = Roo.SplitBar.LEFT;
51290         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51291         this.split.el.addClass("x-layout-split-h");
51292     }
51293     var size = config.initialSize || config.width;
51294     if(typeof size != "undefined"){
51295         this.el.setWidth(size);
51296     }
51297 };
51298 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51299     orientation: Roo.SplitBar.HORIZONTAL,
51300     getBox : function(){
51301         if(this.collapsed){
51302             return this.collapsedEl.getBox();
51303         }
51304         var box = this.el.getBox();
51305         if(this.split){
51306             box.width += this.split.el.getWidth();
51307         }
51308         return box;
51309     },
51310     
51311     updateBox : function(box){
51312         if(this.split && !this.collapsed){
51313             var sw = this.split.el.getWidth();
51314             box.width -= sw;
51315             this.split.el.setLeft(box.x+box.width);
51316             this.split.el.setTop(box.y);
51317             this.split.el.setHeight(box.height);
51318         }
51319         if(this.collapsed){
51320             this.updateBody(null, box.height);
51321         }
51322         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51323     }
51324 });
51325 /*
51326  * Based on:
51327  * Ext JS Library 1.1.1
51328  * Copyright(c) 2006-2007, Ext JS, LLC.
51329  *
51330  * Originally Released Under LGPL - original licence link has changed is not relivant.
51331  *
51332  * Fork - LGPL
51333  * <script type="text/javascript">
51334  */
51335  
51336  
51337 /*
51338  * Private internal class for reading and applying state
51339  */
51340 Roo.LayoutStateManager = function(layout){
51341      // default empty state
51342      this.state = {
51343         north: {},
51344         south: {},
51345         east: {},
51346         west: {}       
51347     };
51348 };
51349
51350 Roo.LayoutStateManager.prototype = {
51351     init : function(layout, provider){
51352         this.provider = provider;
51353         var state = provider.get(layout.id+"-layout-state");
51354         if(state){
51355             var wasUpdating = layout.isUpdating();
51356             if(!wasUpdating){
51357                 layout.beginUpdate();
51358             }
51359             for(var key in state){
51360                 if(typeof state[key] != "function"){
51361                     var rstate = state[key];
51362                     var r = layout.getRegion(key);
51363                     if(r && rstate){
51364                         if(rstate.size){
51365                             r.resizeTo(rstate.size);
51366                         }
51367                         if(rstate.collapsed == true){
51368                             r.collapse(true);
51369                         }else{
51370                             r.expand(null, true);
51371                         }
51372                     }
51373                 }
51374             }
51375             if(!wasUpdating){
51376                 layout.endUpdate();
51377             }
51378             this.state = state; 
51379         }
51380         this.layout = layout;
51381         layout.on("regionresized", this.onRegionResized, this);
51382         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51383         layout.on("regionexpanded", this.onRegionExpanded, this);
51384     },
51385     
51386     storeState : function(){
51387         this.provider.set(this.layout.id+"-layout-state", this.state);
51388     },
51389     
51390     onRegionResized : function(region, newSize){
51391         this.state[region.getPosition()].size = newSize;
51392         this.storeState();
51393     },
51394     
51395     onRegionCollapsed : function(region){
51396         this.state[region.getPosition()].collapsed = true;
51397         this.storeState();
51398     },
51399     
51400     onRegionExpanded : function(region){
51401         this.state[region.getPosition()].collapsed = false;
51402         this.storeState();
51403     }
51404 };/*
51405  * Based on:
51406  * Ext JS Library 1.1.1
51407  * Copyright(c) 2006-2007, Ext JS, LLC.
51408  *
51409  * Originally Released Under LGPL - original licence link has changed is not relivant.
51410  *
51411  * Fork - LGPL
51412  * <script type="text/javascript">
51413  */
51414 /**
51415  * @class Roo.ContentPanel
51416  * @extends Roo.util.Observable
51417  * A basic ContentPanel element.
51418  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51419  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51420  * @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
51421  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51422  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51423  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51424  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51425  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51426  * @cfg {String} title          The title for this panel
51427  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51428  * @cfg {String} url            Calls {@link #setUrl} with this value
51429  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51430  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51431  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51432  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51433
51434  * @constructor
51435  * Create a new ContentPanel.
51436  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51437  * @param {String/Object} config A string to set only the title or a config object
51438  * @param {String} content (optional) Set the HTML content for this panel
51439  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51440  */
51441 Roo.ContentPanel = function(el, config, content){
51442     
51443      
51444     /*
51445     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51446         config = el;
51447         el = Roo.id();
51448     }
51449     if (config && config.parentLayout) { 
51450         el = config.parentLayout.el.createChild(); 
51451     }
51452     */
51453     if(el.autoCreate){ // xtype is available if this is called from factory
51454         config = el;
51455         el = Roo.id();
51456     }
51457     this.el = Roo.get(el);
51458     if(!this.el && config && config.autoCreate){
51459         if(typeof config.autoCreate == "object"){
51460             if(!config.autoCreate.id){
51461                 config.autoCreate.id = config.id||el;
51462             }
51463             this.el = Roo.DomHelper.append(document.body,
51464                         config.autoCreate, true);
51465         }else{
51466             this.el = Roo.DomHelper.append(document.body,
51467                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51468         }
51469     }
51470     this.closable = false;
51471     this.loaded = false;
51472     this.active = false;
51473     if(typeof config == "string"){
51474         this.title = config;
51475     }else{
51476         Roo.apply(this, config);
51477     }
51478     
51479     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51480         this.wrapEl = this.el.wrap();
51481         this.toolbar.container = this.el.insertSibling(false, 'before');
51482         this.toolbar = new Roo.Toolbar(this.toolbar);
51483     }
51484     
51485     // xtype created footer. - not sure if will work as we normally have to render first..
51486     if (this.footer && !this.footer.el && this.footer.xtype) {
51487         if (!this.wrapEl) {
51488             this.wrapEl = this.el.wrap();
51489         }
51490     
51491         this.footer.container = this.wrapEl.createChild();
51492          
51493         this.footer = Roo.factory(this.footer, Roo);
51494         
51495     }
51496     
51497     if(this.resizeEl){
51498         this.resizeEl = Roo.get(this.resizeEl, true);
51499     }else{
51500         this.resizeEl = this.el;
51501     }
51502     // handle view.xtype
51503     
51504  
51505     
51506     
51507     this.addEvents({
51508         /**
51509          * @event activate
51510          * Fires when this panel is activated. 
51511          * @param {Roo.ContentPanel} this
51512          */
51513         "activate" : true,
51514         /**
51515          * @event deactivate
51516          * Fires when this panel is activated. 
51517          * @param {Roo.ContentPanel} this
51518          */
51519         "deactivate" : true,
51520
51521         /**
51522          * @event resize
51523          * Fires when this panel is resized if fitToFrame is true.
51524          * @param {Roo.ContentPanel} this
51525          * @param {Number} width The width after any component adjustments
51526          * @param {Number} height The height after any component adjustments
51527          */
51528         "resize" : true,
51529         
51530          /**
51531          * @event render
51532          * Fires when this tab is created
51533          * @param {Roo.ContentPanel} this
51534          */
51535         "render" : true
51536         
51537         
51538         
51539     });
51540     
51541
51542     
51543     
51544     if(this.autoScroll){
51545         this.resizeEl.setStyle("overflow", "auto");
51546     } else {
51547         // fix randome scrolling
51548         this.el.on('scroll', function() {
51549             Roo.log('fix random scolling');
51550             this.scrollTo('top',0); 
51551         });
51552     }
51553     content = content || this.content;
51554     if(content){
51555         this.setContent(content);
51556     }
51557     if(config && config.url){
51558         this.setUrl(this.url, this.params, this.loadOnce);
51559     }
51560     
51561     
51562     
51563     Roo.ContentPanel.superclass.constructor.call(this);
51564     
51565     if (this.view && typeof(this.view.xtype) != 'undefined') {
51566         this.view.el = this.el.appendChild(document.createElement("div"));
51567         this.view = Roo.factory(this.view); 
51568         this.view.render  &&  this.view.render(false, '');  
51569     }
51570     
51571     
51572     this.fireEvent('render', this);
51573 };
51574
51575 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51576     tabTip:'',
51577     setRegion : function(region){
51578         this.region = region;
51579         if(region){
51580            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51581         }else{
51582            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51583         } 
51584     },
51585     
51586     /**
51587      * Returns the toolbar for this Panel if one was configured. 
51588      * @return {Roo.Toolbar} 
51589      */
51590     getToolbar : function(){
51591         return this.toolbar;
51592     },
51593     
51594     setActiveState : function(active){
51595         this.active = active;
51596         if(!active){
51597             this.fireEvent("deactivate", this);
51598         }else{
51599             this.fireEvent("activate", this);
51600         }
51601     },
51602     /**
51603      * Updates this panel's element
51604      * @param {String} content The new content
51605      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51606     */
51607     setContent : function(content, loadScripts){
51608         this.el.update(content, loadScripts);
51609     },
51610
51611     ignoreResize : function(w, h){
51612         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51613             return true;
51614         }else{
51615             this.lastSize = {width: w, height: h};
51616             return false;
51617         }
51618     },
51619     /**
51620      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51621      * @return {Roo.UpdateManager} The UpdateManager
51622      */
51623     getUpdateManager : function(){
51624         return this.el.getUpdateManager();
51625     },
51626      /**
51627      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51628      * @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:
51629 <pre><code>
51630 panel.load({
51631     url: "your-url.php",
51632     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51633     callback: yourFunction,
51634     scope: yourObject, //(optional scope)
51635     discardUrl: false,
51636     nocache: false,
51637     text: "Loading...",
51638     timeout: 30,
51639     scripts: false
51640 });
51641 </code></pre>
51642      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51643      * 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.
51644      * @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}
51645      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51646      * @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.
51647      * @return {Roo.ContentPanel} this
51648      */
51649     load : function(){
51650         var um = this.el.getUpdateManager();
51651         um.update.apply(um, arguments);
51652         return this;
51653     },
51654
51655
51656     /**
51657      * 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.
51658      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51659      * @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)
51660      * @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)
51661      * @return {Roo.UpdateManager} The UpdateManager
51662      */
51663     setUrl : function(url, params, loadOnce){
51664         if(this.refreshDelegate){
51665             this.removeListener("activate", this.refreshDelegate);
51666         }
51667         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51668         this.on("activate", this.refreshDelegate);
51669         return this.el.getUpdateManager();
51670     },
51671     
51672     _handleRefresh : function(url, params, loadOnce){
51673         if(!loadOnce || !this.loaded){
51674             var updater = this.el.getUpdateManager();
51675             updater.update(url, params, this._setLoaded.createDelegate(this));
51676         }
51677     },
51678     
51679     _setLoaded : function(){
51680         this.loaded = true;
51681     }, 
51682     
51683     /**
51684      * Returns this panel's id
51685      * @return {String} 
51686      */
51687     getId : function(){
51688         return this.el.id;
51689     },
51690     
51691     /** 
51692      * Returns this panel's element - used by regiosn to add.
51693      * @return {Roo.Element} 
51694      */
51695     getEl : function(){
51696         return this.wrapEl || this.el;
51697     },
51698     
51699     adjustForComponents : function(width, height)
51700     {
51701         //Roo.log('adjustForComponents ');
51702         if(this.resizeEl != this.el){
51703             width -= this.el.getFrameWidth('lr');
51704             height -= this.el.getFrameWidth('tb');
51705         }
51706         if(this.toolbar){
51707             var te = this.toolbar.getEl();
51708             height -= te.getHeight();
51709             te.setWidth(width);
51710         }
51711         if(this.footer){
51712             var te = this.footer.getEl();
51713             Roo.log("footer:" + te.getHeight());
51714             
51715             height -= te.getHeight();
51716             te.setWidth(width);
51717         }
51718         
51719         
51720         if(this.adjustments){
51721             width += this.adjustments[0];
51722             height += this.adjustments[1];
51723         }
51724         return {"width": width, "height": height};
51725     },
51726     
51727     setSize : function(width, height){
51728         if(this.fitToFrame && !this.ignoreResize(width, height)){
51729             if(this.fitContainer && this.resizeEl != this.el){
51730                 this.el.setSize(width, height);
51731             }
51732             var size = this.adjustForComponents(width, height);
51733             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51734             this.fireEvent('resize', this, size.width, size.height);
51735         }
51736     },
51737     
51738     /**
51739      * Returns this panel's title
51740      * @return {String} 
51741      */
51742     getTitle : function(){
51743         return this.title;
51744     },
51745     
51746     /**
51747      * Set this panel's title
51748      * @param {String} title
51749      */
51750     setTitle : function(title){
51751         this.title = title;
51752         if(this.region){
51753             this.region.updatePanelTitle(this, title);
51754         }
51755     },
51756     
51757     /**
51758      * Returns true is this panel was configured to be closable
51759      * @return {Boolean} 
51760      */
51761     isClosable : function(){
51762         return this.closable;
51763     },
51764     
51765     beforeSlide : function(){
51766         this.el.clip();
51767         this.resizeEl.clip();
51768     },
51769     
51770     afterSlide : function(){
51771         this.el.unclip();
51772         this.resizeEl.unclip();
51773     },
51774     
51775     /**
51776      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51777      *   Will fail silently if the {@link #setUrl} method has not been called.
51778      *   This does not activate the panel, just updates its content.
51779      */
51780     refresh : function(){
51781         if(this.refreshDelegate){
51782            this.loaded = false;
51783            this.refreshDelegate();
51784         }
51785     },
51786     
51787     /**
51788      * Destroys this panel
51789      */
51790     destroy : function(){
51791         this.el.removeAllListeners();
51792         var tempEl = document.createElement("span");
51793         tempEl.appendChild(this.el.dom);
51794         tempEl.innerHTML = "";
51795         this.el.remove();
51796         this.el = null;
51797     },
51798     
51799     /**
51800      * form - if the content panel contains a form - this is a reference to it.
51801      * @type {Roo.form.Form}
51802      */
51803     form : false,
51804     /**
51805      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51806      *    This contains a reference to it.
51807      * @type {Roo.View}
51808      */
51809     view : false,
51810     
51811       /**
51812      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51813      * <pre><code>
51814
51815 layout.addxtype({
51816        xtype : 'Form',
51817        items: [ .... ]
51818    }
51819 );
51820
51821 </code></pre>
51822      * @param {Object} cfg Xtype definition of item to add.
51823      */
51824     
51825     addxtype : function(cfg) {
51826         // add form..
51827         if (cfg.xtype.match(/^Form$/)) {
51828             
51829             var el;
51830             //if (this.footer) {
51831             //    el = this.footer.container.insertSibling(false, 'before');
51832             //} else {
51833                 el = this.el.createChild();
51834             //}
51835
51836             this.form = new  Roo.form.Form(cfg);
51837             
51838             
51839             if ( this.form.allItems.length) this.form.render(el.dom);
51840             return this.form;
51841         }
51842         // should only have one of theses..
51843         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51844             // views.. should not be just added - used named prop 'view''
51845             
51846             cfg.el = this.el.appendChild(document.createElement("div"));
51847             // factory?
51848             
51849             var ret = new Roo.factory(cfg);
51850              
51851              ret.render && ret.render(false, ''); // render blank..
51852             this.view = ret;
51853             return ret;
51854         }
51855         return false;
51856     }
51857 });
51858
51859 /**
51860  * @class Roo.GridPanel
51861  * @extends Roo.ContentPanel
51862  * @constructor
51863  * Create a new GridPanel.
51864  * @param {Roo.grid.Grid} grid The grid for this panel
51865  * @param {String/Object} config A string to set only the panel's title, or a config object
51866  */
51867 Roo.GridPanel = function(grid, config){
51868     
51869   
51870     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51871         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51872         
51873     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51874     
51875     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51876     
51877     if(this.toolbar){
51878         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51879     }
51880     // xtype created footer. - not sure if will work as we normally have to render first..
51881     if (this.footer && !this.footer.el && this.footer.xtype) {
51882         
51883         this.footer.container = this.grid.getView().getFooterPanel(true);
51884         this.footer.dataSource = this.grid.dataSource;
51885         this.footer = Roo.factory(this.footer, Roo);
51886         
51887     }
51888     
51889     grid.monitorWindowResize = false; // turn off autosizing
51890     grid.autoHeight = false;
51891     grid.autoWidth = false;
51892     this.grid = grid;
51893     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51894 };
51895
51896 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51897     getId : function(){
51898         return this.grid.id;
51899     },
51900     
51901     /**
51902      * Returns the grid for this panel
51903      * @return {Roo.grid.Grid} 
51904      */
51905     getGrid : function(){
51906         return this.grid;    
51907     },
51908     
51909     setSize : function(width, height){
51910         if(!this.ignoreResize(width, height)){
51911             var grid = this.grid;
51912             var size = this.adjustForComponents(width, height);
51913             grid.getGridEl().setSize(size.width, size.height);
51914             grid.autoSize();
51915         }
51916     },
51917     
51918     beforeSlide : function(){
51919         this.grid.getView().scroller.clip();
51920     },
51921     
51922     afterSlide : function(){
51923         this.grid.getView().scroller.unclip();
51924     },
51925     
51926     destroy : function(){
51927         this.grid.destroy();
51928         delete this.grid;
51929         Roo.GridPanel.superclass.destroy.call(this); 
51930     }
51931 });
51932
51933
51934 /**
51935  * @class Roo.NestedLayoutPanel
51936  * @extends Roo.ContentPanel
51937  * @constructor
51938  * Create a new NestedLayoutPanel.
51939  * 
51940  * 
51941  * @param {Roo.BorderLayout} layout The layout for this panel
51942  * @param {String/Object} config A string to set only the title or a config object
51943  */
51944 Roo.NestedLayoutPanel = function(layout, config)
51945 {
51946     // construct with only one argument..
51947     /* FIXME - implement nicer consturctors
51948     if (layout.layout) {
51949         config = layout;
51950         layout = config.layout;
51951         delete config.layout;
51952     }
51953     if (layout.xtype && !layout.getEl) {
51954         // then layout needs constructing..
51955         layout = Roo.factory(layout, Roo);
51956     }
51957     */
51958     
51959     
51960     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51961     
51962     layout.monitorWindowResize = false; // turn off autosizing
51963     this.layout = layout;
51964     this.layout.getEl().addClass("x-layout-nested-layout");
51965     
51966     
51967     
51968     
51969 };
51970
51971 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51972
51973     setSize : function(width, height){
51974         if(!this.ignoreResize(width, height)){
51975             var size = this.adjustForComponents(width, height);
51976             var el = this.layout.getEl();
51977             el.setSize(size.width, size.height);
51978             var touch = el.dom.offsetWidth;
51979             this.layout.layout();
51980             // ie requires a double layout on the first pass
51981             if(Roo.isIE && !this.initialized){
51982                 this.initialized = true;
51983                 this.layout.layout();
51984             }
51985         }
51986     },
51987     
51988     // activate all subpanels if not currently active..
51989     
51990     setActiveState : function(active){
51991         this.active = active;
51992         if(!active){
51993             this.fireEvent("deactivate", this);
51994             return;
51995         }
51996         
51997         this.fireEvent("activate", this);
51998         // not sure if this should happen before or after..
51999         if (!this.layout) {
52000             return; // should not happen..
52001         }
52002         var reg = false;
52003         for (var r in this.layout.regions) {
52004             reg = this.layout.getRegion(r);
52005             if (reg.getActivePanel()) {
52006                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52007                 reg.setActivePanel(reg.getActivePanel());
52008                 continue;
52009             }
52010             if (!reg.panels.length) {
52011                 continue;
52012             }
52013             reg.showPanel(reg.getPanel(0));
52014         }
52015         
52016         
52017         
52018         
52019     },
52020     
52021     /**
52022      * Returns the nested BorderLayout for this panel
52023      * @return {Roo.BorderLayout} 
52024      */
52025     getLayout : function(){
52026         return this.layout;
52027     },
52028     
52029      /**
52030      * Adds a xtype elements to the layout of the nested panel
52031      * <pre><code>
52032
52033 panel.addxtype({
52034        xtype : 'ContentPanel',
52035        region: 'west',
52036        items: [ .... ]
52037    }
52038 );
52039
52040 panel.addxtype({
52041         xtype : 'NestedLayoutPanel',
52042         region: 'west',
52043         layout: {
52044            center: { },
52045            west: { }   
52046         },
52047         items : [ ... list of content panels or nested layout panels.. ]
52048    }
52049 );
52050 </code></pre>
52051      * @param {Object} cfg Xtype definition of item to add.
52052      */
52053     addxtype : function(cfg) {
52054         return this.layout.addxtype(cfg);
52055     
52056     }
52057 });
52058
52059 Roo.ScrollPanel = function(el, config, content){
52060     config = config || {};
52061     config.fitToFrame = true;
52062     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52063     
52064     this.el.dom.style.overflow = "hidden";
52065     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52066     this.el.removeClass("x-layout-inactive-content");
52067     this.el.on("mousewheel", this.onWheel, this);
52068
52069     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52070     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52071     up.unselectable(); down.unselectable();
52072     up.on("click", this.scrollUp, this);
52073     down.on("click", this.scrollDown, this);
52074     up.addClassOnOver("x-scroller-btn-over");
52075     down.addClassOnOver("x-scroller-btn-over");
52076     up.addClassOnClick("x-scroller-btn-click");
52077     down.addClassOnClick("x-scroller-btn-click");
52078     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52079
52080     this.resizeEl = this.el;
52081     this.el = wrap; this.up = up; this.down = down;
52082 };
52083
52084 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52085     increment : 100,
52086     wheelIncrement : 5,
52087     scrollUp : function(){
52088         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52089     },
52090
52091     scrollDown : function(){
52092         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52093     },
52094
52095     afterScroll : function(){
52096         var el = this.resizeEl;
52097         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52098         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52099         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52100     },
52101
52102     setSize : function(){
52103         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52104         this.afterScroll();
52105     },
52106
52107     onWheel : function(e){
52108         var d = e.getWheelDelta();
52109         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52110         this.afterScroll();
52111         e.stopEvent();
52112     },
52113
52114     setContent : function(content, loadScripts){
52115         this.resizeEl.update(content, loadScripts);
52116     }
52117
52118 });
52119
52120
52121
52122
52123
52124
52125
52126
52127
52128 /**
52129  * @class Roo.TreePanel
52130  * @extends Roo.ContentPanel
52131  * @constructor
52132  * Create a new TreePanel. - defaults to fit/scoll contents.
52133  * @param {String/Object} config A string to set only the panel's title, or a config object
52134  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52135  */
52136 Roo.TreePanel = function(config){
52137     var el = config.el;
52138     var tree = config.tree;
52139     delete config.tree; 
52140     delete config.el; // hopefull!
52141     
52142     // wrapper for IE7 strict & safari scroll issue
52143     
52144     var treeEl = el.createChild();
52145     config.resizeEl = treeEl;
52146     
52147     
52148     
52149     Roo.TreePanel.superclass.constructor.call(this, el, config);
52150  
52151  
52152     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52153     //console.log(tree);
52154     this.on('activate', function()
52155     {
52156         if (this.tree.rendered) {
52157             return;
52158         }
52159         //console.log('render tree');
52160         this.tree.render();
52161     });
52162     // this should not be needed.. - it's actually the 'el' that resizes?
52163     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52164     
52165     //this.on('resize',  function (cp, w, h) {
52166     //        this.tree.innerCt.setWidth(w);
52167     //        this.tree.innerCt.setHeight(h);
52168     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52169     //});
52170
52171         
52172     
52173 };
52174
52175 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52176     fitToFrame : true,
52177     autoScroll : true
52178 });
52179
52180
52181
52182
52183
52184
52185
52186
52187
52188
52189
52190 /*
52191  * Based on:
52192  * Ext JS Library 1.1.1
52193  * Copyright(c) 2006-2007, Ext JS, LLC.
52194  *
52195  * Originally Released Under LGPL - original licence link has changed is not relivant.
52196  *
52197  * Fork - LGPL
52198  * <script type="text/javascript">
52199  */
52200  
52201
52202 /**
52203  * @class Roo.ReaderLayout
52204  * @extends Roo.BorderLayout
52205  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52206  * center region containing two nested regions (a top one for a list view and one for item preview below),
52207  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52208  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52209  * expedites the setup of the overall layout and regions for this common application style.
52210  * Example:
52211  <pre><code>
52212 var reader = new Roo.ReaderLayout();
52213 var CP = Roo.ContentPanel;  // shortcut for adding
52214
52215 reader.beginUpdate();
52216 reader.add("north", new CP("north", "North"));
52217 reader.add("west", new CP("west", {title: "West"}));
52218 reader.add("east", new CP("east", {title: "East"}));
52219
52220 reader.regions.listView.add(new CP("listView", "List"));
52221 reader.regions.preview.add(new CP("preview", "Preview"));
52222 reader.endUpdate();
52223 </code></pre>
52224 * @constructor
52225 * Create a new ReaderLayout
52226 * @param {Object} config Configuration options
52227 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52228 * document.body if omitted)
52229 */
52230 Roo.ReaderLayout = function(config, renderTo){
52231     var c = config || {size:{}};
52232     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52233         north: c.north !== false ? Roo.apply({
52234             split:false,
52235             initialSize: 32,
52236             titlebar: false
52237         }, c.north) : false,
52238         west: c.west !== false ? Roo.apply({
52239             split:true,
52240             initialSize: 200,
52241             minSize: 175,
52242             maxSize: 400,
52243             titlebar: true,
52244             collapsible: true,
52245             animate: true,
52246             margins:{left:5,right:0,bottom:5,top:5},
52247             cmargins:{left:5,right:5,bottom:5,top:5}
52248         }, c.west) : false,
52249         east: c.east !== false ? Roo.apply({
52250             split:true,
52251             initialSize: 200,
52252             minSize: 175,
52253             maxSize: 400,
52254             titlebar: true,
52255             collapsible: true,
52256             animate: true,
52257             margins:{left:0,right:5,bottom:5,top:5},
52258             cmargins:{left:5,right:5,bottom:5,top:5}
52259         }, c.east) : false,
52260         center: Roo.apply({
52261             tabPosition: 'top',
52262             autoScroll:false,
52263             closeOnTab: true,
52264             titlebar:false,
52265             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52266         }, c.center)
52267     });
52268
52269     this.el.addClass('x-reader');
52270
52271     this.beginUpdate();
52272
52273     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52274         south: c.preview !== false ? Roo.apply({
52275             split:true,
52276             initialSize: 200,
52277             minSize: 100,
52278             autoScroll:true,
52279             collapsible:true,
52280             titlebar: true,
52281             cmargins:{top:5,left:0, right:0, bottom:0}
52282         }, c.preview) : false,
52283         center: Roo.apply({
52284             autoScroll:false,
52285             titlebar:false,
52286             minHeight:200
52287         }, c.listView)
52288     });
52289     this.add('center', new Roo.NestedLayoutPanel(inner,
52290             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52291
52292     this.endUpdate();
52293
52294     this.regions.preview = inner.getRegion('south');
52295     this.regions.listView = inner.getRegion('center');
52296 };
52297
52298 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52299  * Based on:
52300  * Ext JS Library 1.1.1
52301  * Copyright(c) 2006-2007, Ext JS, LLC.
52302  *
52303  * Originally Released Under LGPL - original licence link has changed is not relivant.
52304  *
52305  * Fork - LGPL
52306  * <script type="text/javascript">
52307  */
52308  
52309 /**
52310  * @class Roo.grid.Grid
52311  * @extends Roo.util.Observable
52312  * This class represents the primary interface of a component based grid control.
52313  * <br><br>Usage:<pre><code>
52314  var grid = new Roo.grid.Grid("my-container-id", {
52315      ds: myDataStore,
52316      cm: myColModel,
52317      selModel: mySelectionModel,
52318      autoSizeColumns: true,
52319      monitorWindowResize: false,
52320      trackMouseOver: true
52321  });
52322  // set any options
52323  grid.render();
52324  * </code></pre>
52325  * <b>Common Problems:</b><br/>
52326  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52327  * element will correct this<br/>
52328  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52329  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52330  * are unpredictable.<br/>
52331  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52332  * grid to calculate dimensions/offsets.<br/>
52333   * @constructor
52334  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52335  * The container MUST have some type of size defined for the grid to fill. The container will be
52336  * automatically set to position relative if it isn't already.
52337  * @param {Object} config A config object that sets properties on this grid.
52338  */
52339 Roo.grid.Grid = function(container, config){
52340         // initialize the container
52341         this.container = Roo.get(container);
52342         this.container.update("");
52343         this.container.setStyle("overflow", "hidden");
52344     this.container.addClass('x-grid-container');
52345
52346     this.id = this.container.id;
52347
52348     Roo.apply(this, config);
52349     // check and correct shorthanded configs
52350     if(this.ds){
52351         this.dataSource = this.ds;
52352         delete this.ds;
52353     }
52354     if(this.cm){
52355         this.colModel = this.cm;
52356         delete this.cm;
52357     }
52358     if(this.sm){
52359         this.selModel = this.sm;
52360         delete this.sm;
52361     }
52362
52363     if (this.selModel) {
52364         this.selModel = Roo.factory(this.selModel, Roo.grid);
52365         this.sm = this.selModel;
52366         this.sm.xmodule = this.xmodule || false;
52367     }
52368     if (typeof(this.colModel.config) == 'undefined') {
52369         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52370         this.cm = this.colModel;
52371         this.cm.xmodule = this.xmodule || false;
52372     }
52373     if (this.dataSource) {
52374         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52375         this.ds = this.dataSource;
52376         this.ds.xmodule = this.xmodule || false;
52377          
52378     }
52379     
52380     
52381     
52382     if(this.width){
52383         this.container.setWidth(this.width);
52384     }
52385
52386     if(this.height){
52387         this.container.setHeight(this.height);
52388     }
52389     /** @private */
52390         this.addEvents({
52391         // raw events
52392         /**
52393          * @event click
52394          * The raw click event for the entire grid.
52395          * @param {Roo.EventObject} e
52396          */
52397         "click" : true,
52398         /**
52399          * @event dblclick
52400          * The raw dblclick event for the entire grid.
52401          * @param {Roo.EventObject} e
52402          */
52403         "dblclick" : true,
52404         /**
52405          * @event contextmenu
52406          * The raw contextmenu event for the entire grid.
52407          * @param {Roo.EventObject} e
52408          */
52409         "contextmenu" : true,
52410         /**
52411          * @event mousedown
52412          * The raw mousedown event for the entire grid.
52413          * @param {Roo.EventObject} e
52414          */
52415         "mousedown" : true,
52416         /**
52417          * @event mouseup
52418          * The raw mouseup event for the entire grid.
52419          * @param {Roo.EventObject} e
52420          */
52421         "mouseup" : true,
52422         /**
52423          * @event mouseover
52424          * The raw mouseover event for the entire grid.
52425          * @param {Roo.EventObject} e
52426          */
52427         "mouseover" : true,
52428         /**
52429          * @event mouseout
52430          * The raw mouseout event for the entire grid.
52431          * @param {Roo.EventObject} e
52432          */
52433         "mouseout" : true,
52434         /**
52435          * @event keypress
52436          * The raw keypress event for the entire grid.
52437          * @param {Roo.EventObject} e
52438          */
52439         "keypress" : true,
52440         /**
52441          * @event keydown
52442          * The raw keydown event for the entire grid.
52443          * @param {Roo.EventObject} e
52444          */
52445         "keydown" : true,
52446
52447         // custom events
52448
52449         /**
52450          * @event cellclick
52451          * Fires when a cell is clicked
52452          * @param {Grid} this
52453          * @param {Number} rowIndex
52454          * @param {Number} columnIndex
52455          * @param {Roo.EventObject} e
52456          */
52457         "cellclick" : true,
52458         /**
52459          * @event celldblclick
52460          * Fires when a cell is double clicked
52461          * @param {Grid} this
52462          * @param {Number} rowIndex
52463          * @param {Number} columnIndex
52464          * @param {Roo.EventObject} e
52465          */
52466         "celldblclick" : true,
52467         /**
52468          * @event rowclick
52469          * Fires when a row is clicked
52470          * @param {Grid} this
52471          * @param {Number} rowIndex
52472          * @param {Roo.EventObject} e
52473          */
52474         "rowclick" : true,
52475         /**
52476          * @event rowdblclick
52477          * Fires when a row is double clicked
52478          * @param {Grid} this
52479          * @param {Number} rowIndex
52480          * @param {Roo.EventObject} e
52481          */
52482         "rowdblclick" : true,
52483         /**
52484          * @event headerclick
52485          * Fires when a header is clicked
52486          * @param {Grid} this
52487          * @param {Number} columnIndex
52488          * @param {Roo.EventObject} e
52489          */
52490         "headerclick" : true,
52491         /**
52492          * @event headerdblclick
52493          * Fires when a header cell is double clicked
52494          * @param {Grid} this
52495          * @param {Number} columnIndex
52496          * @param {Roo.EventObject} e
52497          */
52498         "headerdblclick" : true,
52499         /**
52500          * @event rowcontextmenu
52501          * Fires when a row is right clicked
52502          * @param {Grid} this
52503          * @param {Number} rowIndex
52504          * @param {Roo.EventObject} e
52505          */
52506         "rowcontextmenu" : true,
52507         /**
52508          * @event cellcontextmenu
52509          * Fires when a cell is right clicked
52510          * @param {Grid} this
52511          * @param {Number} rowIndex
52512          * @param {Number} cellIndex
52513          * @param {Roo.EventObject} e
52514          */
52515          "cellcontextmenu" : true,
52516         /**
52517          * @event headercontextmenu
52518          * Fires when a header is right clicked
52519          * @param {Grid} this
52520          * @param {Number} columnIndex
52521          * @param {Roo.EventObject} e
52522          */
52523         "headercontextmenu" : true,
52524         /**
52525          * @event bodyscroll
52526          * Fires when the body element is scrolled
52527          * @param {Number} scrollLeft
52528          * @param {Number} scrollTop
52529          */
52530         "bodyscroll" : true,
52531         /**
52532          * @event columnresize
52533          * Fires when the user resizes a column
52534          * @param {Number} columnIndex
52535          * @param {Number} newSize
52536          */
52537         "columnresize" : true,
52538         /**
52539          * @event columnmove
52540          * Fires when the user moves a column
52541          * @param {Number} oldIndex
52542          * @param {Number} newIndex
52543          */
52544         "columnmove" : true,
52545         /**
52546          * @event startdrag
52547          * Fires when row(s) start being dragged
52548          * @param {Grid} this
52549          * @param {Roo.GridDD} dd The drag drop object
52550          * @param {event} e The raw browser event
52551          */
52552         "startdrag" : true,
52553         /**
52554          * @event enddrag
52555          * Fires when a drag operation is complete
52556          * @param {Grid} this
52557          * @param {Roo.GridDD} dd The drag drop object
52558          * @param {event} e The raw browser event
52559          */
52560         "enddrag" : true,
52561         /**
52562          * @event dragdrop
52563          * Fires when dragged row(s) are dropped on a valid DD target
52564          * @param {Grid} this
52565          * @param {Roo.GridDD} dd The drag drop object
52566          * @param {String} targetId The target drag drop object
52567          * @param {event} e The raw browser event
52568          */
52569         "dragdrop" : true,
52570         /**
52571          * @event dragover
52572          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52573          * @param {Grid} this
52574          * @param {Roo.GridDD} dd The drag drop object
52575          * @param {String} targetId The target drag drop object
52576          * @param {event} e The raw browser event
52577          */
52578         "dragover" : true,
52579         /**
52580          * @event dragenter
52581          *  Fires when the dragged row(s) first cross another DD target while being dragged
52582          * @param {Grid} this
52583          * @param {Roo.GridDD} dd The drag drop object
52584          * @param {String} targetId The target drag drop object
52585          * @param {event} e The raw browser event
52586          */
52587         "dragenter" : true,
52588         /**
52589          * @event dragout
52590          * Fires when the dragged row(s) leave another DD target while being dragged
52591          * @param {Grid} this
52592          * @param {Roo.GridDD} dd The drag drop object
52593          * @param {String} targetId The target drag drop object
52594          * @param {event} e The raw browser event
52595          */
52596         "dragout" : true,
52597         /**
52598          * @event rowclass
52599          * Fires when a row is rendered, so you can change add a style to it.
52600          * @param {GridView} gridview   The grid view
52601          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52602          */
52603         'rowclass' : true,
52604
52605         /**
52606          * @event render
52607          * Fires when the grid is rendered
52608          * @param {Grid} grid
52609          */
52610         'render' : true
52611     });
52612
52613     Roo.grid.Grid.superclass.constructor.call(this);
52614 };
52615 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52616     
52617     /**
52618      * @cfg {String} ddGroup - drag drop group.
52619      */
52620
52621     /**
52622      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52623      */
52624     minColumnWidth : 25,
52625
52626     /**
52627      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52628      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52629      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52630      */
52631     autoSizeColumns : false,
52632
52633     /**
52634      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52635      */
52636     autoSizeHeaders : true,
52637
52638     /**
52639      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52640      */
52641     monitorWindowResize : true,
52642
52643     /**
52644      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52645      * rows measured to get a columns size. Default is 0 (all rows).
52646      */
52647     maxRowsToMeasure : 0,
52648
52649     /**
52650      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52651      */
52652     trackMouseOver : true,
52653
52654     /**
52655     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52656     */
52657     
52658     /**
52659     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52660     */
52661     enableDragDrop : false,
52662     
52663     /**
52664     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52665     */
52666     enableColumnMove : true,
52667     
52668     /**
52669     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52670     */
52671     enableColumnHide : true,
52672     
52673     /**
52674     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52675     */
52676     enableRowHeightSync : false,
52677     
52678     /**
52679     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52680     */
52681     stripeRows : true,
52682     
52683     /**
52684     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52685     */
52686     autoHeight : false,
52687
52688     /**
52689      * @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.
52690      */
52691     autoExpandColumn : false,
52692
52693     /**
52694     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52695     * Default is 50.
52696     */
52697     autoExpandMin : 50,
52698
52699     /**
52700     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52701     */
52702     autoExpandMax : 1000,
52703
52704     /**
52705     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52706     */
52707     view : null,
52708
52709     /**
52710     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52711     */
52712     loadMask : false,
52713     /**
52714     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52715     */
52716     dropTarget: false,
52717     
52718    
52719     
52720     // private
52721     rendered : false,
52722
52723     /**
52724     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52725     * of a fixed width. Default is false.
52726     */
52727     /**
52728     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52729     */
52730     /**
52731      * Called once after all setup has been completed and the grid is ready to be rendered.
52732      * @return {Roo.grid.Grid} this
52733      */
52734     render : function()
52735     {
52736         var c = this.container;
52737         // try to detect autoHeight/width mode
52738         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52739             this.autoHeight = true;
52740         }
52741         var view = this.getView();
52742         view.init(this);
52743
52744         c.on("click", this.onClick, this);
52745         c.on("dblclick", this.onDblClick, this);
52746         c.on("contextmenu", this.onContextMenu, this);
52747         c.on("keydown", this.onKeyDown, this);
52748         if (Roo.isTouch) {
52749             c.on("touchstart", this.onTouchStart, this);
52750         }
52751
52752         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52753
52754         this.getSelectionModel().init(this);
52755
52756         view.render();
52757
52758         if(this.loadMask){
52759             this.loadMask = new Roo.LoadMask(this.container,
52760                     Roo.apply({store:this.dataSource}, this.loadMask));
52761         }
52762         
52763         
52764         if (this.toolbar && this.toolbar.xtype) {
52765             this.toolbar.container = this.getView().getHeaderPanel(true);
52766             this.toolbar = new Roo.Toolbar(this.toolbar);
52767         }
52768         if (this.footer && this.footer.xtype) {
52769             this.footer.dataSource = this.getDataSource();
52770             this.footer.container = this.getView().getFooterPanel(true);
52771             this.footer = Roo.factory(this.footer, Roo);
52772         }
52773         if (this.dropTarget && this.dropTarget.xtype) {
52774             delete this.dropTarget.xtype;
52775             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52776         }
52777         
52778         
52779         this.rendered = true;
52780         this.fireEvent('render', this);
52781         return this;
52782     },
52783
52784         /**
52785          * Reconfigures the grid to use a different Store and Column Model.
52786          * The View will be bound to the new objects and refreshed.
52787          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52788          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52789          */
52790     reconfigure : function(dataSource, colModel){
52791         if(this.loadMask){
52792             this.loadMask.destroy();
52793             this.loadMask = new Roo.LoadMask(this.container,
52794                     Roo.apply({store:dataSource}, this.loadMask));
52795         }
52796         this.view.bind(dataSource, colModel);
52797         this.dataSource = dataSource;
52798         this.colModel = colModel;
52799         this.view.refresh(true);
52800     },
52801
52802     // private
52803     onKeyDown : function(e){
52804         this.fireEvent("keydown", e);
52805     },
52806
52807     /**
52808      * Destroy this grid.
52809      * @param {Boolean} removeEl True to remove the element
52810      */
52811     destroy : function(removeEl, keepListeners){
52812         if(this.loadMask){
52813             this.loadMask.destroy();
52814         }
52815         var c = this.container;
52816         c.removeAllListeners();
52817         this.view.destroy();
52818         this.colModel.purgeListeners();
52819         if(!keepListeners){
52820             this.purgeListeners();
52821         }
52822         c.update("");
52823         if(removeEl === true){
52824             c.remove();
52825         }
52826     },
52827
52828     // private
52829     processEvent : function(name, e){
52830         // does this fire select???
52831         //Roo.log('grid:processEvent '  + name);
52832         
52833         if (name != 'touchstart' ) {
52834             this.fireEvent(name, e);    
52835         }
52836         
52837         var t = e.getTarget();
52838         var v = this.view;
52839         var header = v.findHeaderIndex(t);
52840         if(header !== false){
52841             var ename = name == 'touchstart' ? 'click' : name;
52842              
52843             this.fireEvent("header" + ename, this, header, e);
52844         }else{
52845             var row = v.findRowIndex(t);
52846             var cell = v.findCellIndex(t);
52847             if (name == 'touchstart') {
52848                 // first touch is always a click.
52849                 // hopefull this happens after selection is updated.?
52850                 name = false;
52851                 
52852                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52853                     var cs = this.selModel.getSelectedCell();
52854                     if (row == cs[0] && cell == cs[1]){
52855                         name = 'dblclick';
52856                     }
52857                 }
52858                 if (typeof(this.selModel.getSelections) != 'undefined') {
52859                     var cs = this.selModel.getSelections();
52860                     var ds = this.dataSource;
52861                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52862                         name = 'dblclick';
52863                     }
52864                 }
52865                 if (!name) {
52866                     return;
52867                 }
52868             }
52869             
52870             
52871             if(row !== false){
52872                 this.fireEvent("row" + name, this, row, e);
52873                 if(cell !== false){
52874                     this.fireEvent("cell" + name, this, row, cell, e);
52875                 }
52876             }
52877         }
52878     },
52879
52880     // private
52881     onClick : function(e){
52882         this.processEvent("click", e);
52883     },
52884    // private
52885     onTouchStart : function(e){
52886         this.processEvent("touchstart", e);
52887     },
52888
52889     // private
52890     onContextMenu : function(e, t){
52891         this.processEvent("contextmenu", e);
52892     },
52893
52894     // private
52895     onDblClick : function(e){
52896         this.processEvent("dblclick", e);
52897     },
52898
52899     // private
52900     walkCells : function(row, col, step, fn, scope){
52901         var cm = this.colModel, clen = cm.getColumnCount();
52902         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52903         if(step < 0){
52904             if(col < 0){
52905                 row--;
52906                 first = false;
52907             }
52908             while(row >= 0){
52909                 if(!first){
52910                     col = clen-1;
52911                 }
52912                 first = false;
52913                 while(col >= 0){
52914                     if(fn.call(scope || this, row, col, cm) === true){
52915                         return [row, col];
52916                     }
52917                     col--;
52918                 }
52919                 row--;
52920             }
52921         } else {
52922             if(col >= clen){
52923                 row++;
52924                 first = false;
52925             }
52926             while(row < rlen){
52927                 if(!first){
52928                     col = 0;
52929                 }
52930                 first = false;
52931                 while(col < clen){
52932                     if(fn.call(scope || this, row, col, cm) === true){
52933                         return [row, col];
52934                     }
52935                     col++;
52936                 }
52937                 row++;
52938             }
52939         }
52940         return null;
52941     },
52942
52943     // private
52944     getSelections : function(){
52945         return this.selModel.getSelections();
52946     },
52947
52948     /**
52949      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52950      * but if manual update is required this method will initiate it.
52951      */
52952     autoSize : function(){
52953         if(this.rendered){
52954             this.view.layout();
52955             if(this.view.adjustForScroll){
52956                 this.view.adjustForScroll();
52957             }
52958         }
52959     },
52960
52961     /**
52962      * Returns the grid's underlying element.
52963      * @return {Element} The element
52964      */
52965     getGridEl : function(){
52966         return this.container;
52967     },
52968
52969     // private for compatibility, overridden by editor grid
52970     stopEditing : function(){},
52971
52972     /**
52973      * Returns the grid's SelectionModel.
52974      * @return {SelectionModel}
52975      */
52976     getSelectionModel : function(){
52977         if(!this.selModel){
52978             this.selModel = new Roo.grid.RowSelectionModel();
52979         }
52980         return this.selModel;
52981     },
52982
52983     /**
52984      * Returns the grid's DataSource.
52985      * @return {DataSource}
52986      */
52987     getDataSource : function(){
52988         return this.dataSource;
52989     },
52990
52991     /**
52992      * Returns the grid's ColumnModel.
52993      * @return {ColumnModel}
52994      */
52995     getColumnModel : function(){
52996         return this.colModel;
52997     },
52998
52999     /**
53000      * Returns the grid's GridView object.
53001      * @return {GridView}
53002      */
53003     getView : function(){
53004         if(!this.view){
53005             this.view = new Roo.grid.GridView(this.viewConfig);
53006         }
53007         return this.view;
53008     },
53009     /**
53010      * Called to get grid's drag proxy text, by default returns this.ddText.
53011      * @return {String}
53012      */
53013     getDragDropText : function(){
53014         var count = this.selModel.getCount();
53015         return String.format(this.ddText, count, count == 1 ? '' : 's');
53016     }
53017 });
53018 /**
53019  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53020  * %0 is replaced with the number of selected rows.
53021  * @type String
53022  */
53023 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53024  * Based on:
53025  * Ext JS Library 1.1.1
53026  * Copyright(c) 2006-2007, Ext JS, LLC.
53027  *
53028  * Originally Released Under LGPL - original licence link has changed is not relivant.
53029  *
53030  * Fork - LGPL
53031  * <script type="text/javascript">
53032  */
53033  
53034 Roo.grid.AbstractGridView = function(){
53035         this.grid = null;
53036         
53037         this.events = {
53038             "beforerowremoved" : true,
53039             "beforerowsinserted" : true,
53040             "beforerefresh" : true,
53041             "rowremoved" : true,
53042             "rowsinserted" : true,
53043             "rowupdated" : true,
53044             "refresh" : true
53045         };
53046     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53047 };
53048
53049 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53050     rowClass : "x-grid-row",
53051     cellClass : "x-grid-cell",
53052     tdClass : "x-grid-td",
53053     hdClass : "x-grid-hd",
53054     splitClass : "x-grid-hd-split",
53055     
53056     init: function(grid){
53057         this.grid = grid;
53058                 var cid = this.grid.getGridEl().id;
53059         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53060         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53061         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53062         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53063         },
53064         
53065     getColumnRenderers : function(){
53066         var renderers = [];
53067         var cm = this.grid.colModel;
53068         var colCount = cm.getColumnCount();
53069         for(var i = 0; i < colCount; i++){
53070             renderers[i] = cm.getRenderer(i);
53071         }
53072         return renderers;
53073     },
53074     
53075     getColumnIds : function(){
53076         var ids = [];
53077         var cm = this.grid.colModel;
53078         var colCount = cm.getColumnCount();
53079         for(var i = 0; i < colCount; i++){
53080             ids[i] = cm.getColumnId(i);
53081         }
53082         return ids;
53083     },
53084     
53085     getDataIndexes : function(){
53086         if(!this.indexMap){
53087             this.indexMap = this.buildIndexMap();
53088         }
53089         return this.indexMap.colToData;
53090     },
53091     
53092     getColumnIndexByDataIndex : function(dataIndex){
53093         if(!this.indexMap){
53094             this.indexMap = this.buildIndexMap();
53095         }
53096         return this.indexMap.dataToCol[dataIndex];
53097     },
53098     
53099     /**
53100      * Set a css style for a column dynamically. 
53101      * @param {Number} colIndex The index of the column
53102      * @param {String} name The css property name
53103      * @param {String} value The css value
53104      */
53105     setCSSStyle : function(colIndex, name, value){
53106         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53107         Roo.util.CSS.updateRule(selector, name, value);
53108     },
53109     
53110     generateRules : function(cm){
53111         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53112         Roo.util.CSS.removeStyleSheet(rulesId);
53113         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53114             var cid = cm.getColumnId(i);
53115             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53116                          this.tdSelector, cid, " {\n}\n",
53117                          this.hdSelector, cid, " {\n}\n",
53118                          this.splitSelector, cid, " {\n}\n");
53119         }
53120         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53121     }
53122 });/*
53123  * Based on:
53124  * Ext JS Library 1.1.1
53125  * Copyright(c) 2006-2007, Ext JS, LLC.
53126  *
53127  * Originally Released Under LGPL - original licence link has changed is not relivant.
53128  *
53129  * Fork - LGPL
53130  * <script type="text/javascript">
53131  */
53132
53133 // private
53134 // This is a support class used internally by the Grid components
53135 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53136     this.grid = grid;
53137     this.view = grid.getView();
53138     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53139     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53140     if(hd2){
53141         this.setHandleElId(Roo.id(hd));
53142         this.setOuterHandleElId(Roo.id(hd2));
53143     }
53144     this.scroll = false;
53145 };
53146 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53147     maxDragWidth: 120,
53148     getDragData : function(e){
53149         var t = Roo.lib.Event.getTarget(e);
53150         var h = this.view.findHeaderCell(t);
53151         if(h){
53152             return {ddel: h.firstChild, header:h};
53153         }
53154         return false;
53155     },
53156
53157     onInitDrag : function(e){
53158         this.view.headersDisabled = true;
53159         var clone = this.dragData.ddel.cloneNode(true);
53160         clone.id = Roo.id();
53161         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53162         this.proxy.update(clone);
53163         return true;
53164     },
53165
53166     afterValidDrop : function(){
53167         var v = this.view;
53168         setTimeout(function(){
53169             v.headersDisabled = false;
53170         }, 50);
53171     },
53172
53173     afterInvalidDrop : function(){
53174         var v = this.view;
53175         setTimeout(function(){
53176             v.headersDisabled = false;
53177         }, 50);
53178     }
53179 });
53180 /*
53181  * Based on:
53182  * Ext JS Library 1.1.1
53183  * Copyright(c) 2006-2007, Ext JS, LLC.
53184  *
53185  * Originally Released Under LGPL - original licence link has changed is not relivant.
53186  *
53187  * Fork - LGPL
53188  * <script type="text/javascript">
53189  */
53190 // private
53191 // This is a support class used internally by the Grid components
53192 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53193     this.grid = grid;
53194     this.view = grid.getView();
53195     // split the proxies so they don't interfere with mouse events
53196     this.proxyTop = Roo.DomHelper.append(document.body, {
53197         cls:"col-move-top", html:"&#160;"
53198     }, true);
53199     this.proxyBottom = Roo.DomHelper.append(document.body, {
53200         cls:"col-move-bottom", html:"&#160;"
53201     }, true);
53202     this.proxyTop.hide = this.proxyBottom.hide = function(){
53203         this.setLeftTop(-100,-100);
53204         this.setStyle("visibility", "hidden");
53205     };
53206     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53207     // temporarily disabled
53208     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53209     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53210 };
53211 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53212     proxyOffsets : [-4, -9],
53213     fly: Roo.Element.fly,
53214
53215     getTargetFromEvent : function(e){
53216         var t = Roo.lib.Event.getTarget(e);
53217         var cindex = this.view.findCellIndex(t);
53218         if(cindex !== false){
53219             return this.view.getHeaderCell(cindex);
53220         }
53221         return null;
53222     },
53223
53224     nextVisible : function(h){
53225         var v = this.view, cm = this.grid.colModel;
53226         h = h.nextSibling;
53227         while(h){
53228             if(!cm.isHidden(v.getCellIndex(h))){
53229                 return h;
53230             }
53231             h = h.nextSibling;
53232         }
53233         return null;
53234     },
53235
53236     prevVisible : function(h){
53237         var v = this.view, cm = this.grid.colModel;
53238         h = h.prevSibling;
53239         while(h){
53240             if(!cm.isHidden(v.getCellIndex(h))){
53241                 return h;
53242             }
53243             h = h.prevSibling;
53244         }
53245         return null;
53246     },
53247
53248     positionIndicator : function(h, n, e){
53249         var x = Roo.lib.Event.getPageX(e);
53250         var r = Roo.lib.Dom.getRegion(n.firstChild);
53251         var px, pt, py = r.top + this.proxyOffsets[1];
53252         if((r.right - x) <= (r.right-r.left)/2){
53253             px = r.right+this.view.borderWidth;
53254             pt = "after";
53255         }else{
53256             px = r.left;
53257             pt = "before";
53258         }
53259         var oldIndex = this.view.getCellIndex(h);
53260         var newIndex = this.view.getCellIndex(n);
53261
53262         if(this.grid.colModel.isFixed(newIndex)){
53263             return false;
53264         }
53265
53266         var locked = this.grid.colModel.isLocked(newIndex);
53267
53268         if(pt == "after"){
53269             newIndex++;
53270         }
53271         if(oldIndex < newIndex){
53272             newIndex--;
53273         }
53274         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53275             return false;
53276         }
53277         px +=  this.proxyOffsets[0];
53278         this.proxyTop.setLeftTop(px, py);
53279         this.proxyTop.show();
53280         if(!this.bottomOffset){
53281             this.bottomOffset = this.view.mainHd.getHeight();
53282         }
53283         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53284         this.proxyBottom.show();
53285         return pt;
53286     },
53287
53288     onNodeEnter : function(n, dd, e, data){
53289         if(data.header != n){
53290             this.positionIndicator(data.header, n, e);
53291         }
53292     },
53293
53294     onNodeOver : function(n, dd, e, data){
53295         var result = false;
53296         if(data.header != n){
53297             result = this.positionIndicator(data.header, n, e);
53298         }
53299         if(!result){
53300             this.proxyTop.hide();
53301             this.proxyBottom.hide();
53302         }
53303         return result ? this.dropAllowed : this.dropNotAllowed;
53304     },
53305
53306     onNodeOut : function(n, dd, e, data){
53307         this.proxyTop.hide();
53308         this.proxyBottom.hide();
53309     },
53310
53311     onNodeDrop : function(n, dd, e, data){
53312         var h = data.header;
53313         if(h != n){
53314             var cm = this.grid.colModel;
53315             var x = Roo.lib.Event.getPageX(e);
53316             var r = Roo.lib.Dom.getRegion(n.firstChild);
53317             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53318             var oldIndex = this.view.getCellIndex(h);
53319             var newIndex = this.view.getCellIndex(n);
53320             var locked = cm.isLocked(newIndex);
53321             if(pt == "after"){
53322                 newIndex++;
53323             }
53324             if(oldIndex < newIndex){
53325                 newIndex--;
53326             }
53327             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53328                 return false;
53329             }
53330             cm.setLocked(oldIndex, locked, true);
53331             cm.moveColumn(oldIndex, newIndex);
53332             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53333             return true;
53334         }
53335         return false;
53336     }
53337 });
53338 /*
53339  * Based on:
53340  * Ext JS Library 1.1.1
53341  * Copyright(c) 2006-2007, Ext JS, LLC.
53342  *
53343  * Originally Released Under LGPL - original licence link has changed is not relivant.
53344  *
53345  * Fork - LGPL
53346  * <script type="text/javascript">
53347  */
53348   
53349 /**
53350  * @class Roo.grid.GridView
53351  * @extends Roo.util.Observable
53352  *
53353  * @constructor
53354  * @param {Object} config
53355  */
53356 Roo.grid.GridView = function(config){
53357     Roo.grid.GridView.superclass.constructor.call(this);
53358     this.el = null;
53359
53360     Roo.apply(this, config);
53361 };
53362
53363 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53364
53365     unselectable :  'unselectable="on"',
53366     unselectableCls :  'x-unselectable',
53367     
53368     
53369     rowClass : "x-grid-row",
53370
53371     cellClass : "x-grid-col",
53372
53373     tdClass : "x-grid-td",
53374
53375     hdClass : "x-grid-hd",
53376
53377     splitClass : "x-grid-split",
53378
53379     sortClasses : ["sort-asc", "sort-desc"],
53380
53381     enableMoveAnim : false,
53382
53383     hlColor: "C3DAF9",
53384
53385     dh : Roo.DomHelper,
53386
53387     fly : Roo.Element.fly,
53388
53389     css : Roo.util.CSS,
53390
53391     borderWidth: 1,
53392
53393     splitOffset: 3,
53394
53395     scrollIncrement : 22,
53396
53397     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53398
53399     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53400
53401     bind : function(ds, cm){
53402         if(this.ds){
53403             this.ds.un("load", this.onLoad, this);
53404             this.ds.un("datachanged", this.onDataChange, this);
53405             this.ds.un("add", this.onAdd, this);
53406             this.ds.un("remove", this.onRemove, this);
53407             this.ds.un("update", this.onUpdate, this);
53408             this.ds.un("clear", this.onClear, this);
53409         }
53410         if(ds){
53411             ds.on("load", this.onLoad, this);
53412             ds.on("datachanged", this.onDataChange, this);
53413             ds.on("add", this.onAdd, this);
53414             ds.on("remove", this.onRemove, this);
53415             ds.on("update", this.onUpdate, this);
53416             ds.on("clear", this.onClear, this);
53417         }
53418         this.ds = ds;
53419
53420         if(this.cm){
53421             this.cm.un("widthchange", this.onColWidthChange, this);
53422             this.cm.un("headerchange", this.onHeaderChange, this);
53423             this.cm.un("hiddenchange", this.onHiddenChange, this);
53424             this.cm.un("columnmoved", this.onColumnMove, this);
53425             this.cm.un("columnlockchange", this.onColumnLock, this);
53426         }
53427         if(cm){
53428             this.generateRules(cm);
53429             cm.on("widthchange", this.onColWidthChange, this);
53430             cm.on("headerchange", this.onHeaderChange, this);
53431             cm.on("hiddenchange", this.onHiddenChange, this);
53432             cm.on("columnmoved", this.onColumnMove, this);
53433             cm.on("columnlockchange", this.onColumnLock, this);
53434         }
53435         this.cm = cm;
53436     },
53437
53438     init: function(grid){
53439         Roo.grid.GridView.superclass.init.call(this, grid);
53440
53441         this.bind(grid.dataSource, grid.colModel);
53442
53443         grid.on("headerclick", this.handleHeaderClick, this);
53444
53445         if(grid.trackMouseOver){
53446             grid.on("mouseover", this.onRowOver, this);
53447             grid.on("mouseout", this.onRowOut, this);
53448         }
53449         grid.cancelTextSelection = function(){};
53450         this.gridId = grid.id;
53451
53452         var tpls = this.templates || {};
53453
53454         if(!tpls.master){
53455             tpls.master = new Roo.Template(
53456                '<div class="x-grid" hidefocus="true">',
53457                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53458                   '<div class="x-grid-topbar"></div>',
53459                   '<div class="x-grid-scroller"><div></div></div>',
53460                   '<div class="x-grid-locked">',
53461                       '<div class="x-grid-header">{lockedHeader}</div>',
53462                       '<div class="x-grid-body">{lockedBody}</div>',
53463                   "</div>",
53464                   '<div class="x-grid-viewport">',
53465                       '<div class="x-grid-header">{header}</div>',
53466                       '<div class="x-grid-body">{body}</div>',
53467                   "</div>",
53468                   '<div class="x-grid-bottombar"></div>',
53469                  
53470                   '<div class="x-grid-resize-proxy">&#160;</div>',
53471                "</div>"
53472             );
53473             tpls.master.disableformats = true;
53474         }
53475
53476         if(!tpls.header){
53477             tpls.header = new Roo.Template(
53478                '<table border="0" cellspacing="0" cellpadding="0">',
53479                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53480                "</table>{splits}"
53481             );
53482             tpls.header.disableformats = true;
53483         }
53484         tpls.header.compile();
53485
53486         if(!tpls.hcell){
53487             tpls.hcell = new Roo.Template(
53488                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53489                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53490                 "</div></td>"
53491              );
53492              tpls.hcell.disableFormats = true;
53493         }
53494         tpls.hcell.compile();
53495
53496         if(!tpls.hsplit){
53497             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53498                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53499             tpls.hsplit.disableFormats = true;
53500         }
53501         tpls.hsplit.compile();
53502
53503         if(!tpls.body){
53504             tpls.body = new Roo.Template(
53505                '<table border="0" cellspacing="0" cellpadding="0">',
53506                "<tbody>{rows}</tbody>",
53507                "</table>"
53508             );
53509             tpls.body.disableFormats = true;
53510         }
53511         tpls.body.compile();
53512
53513         if(!tpls.row){
53514             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53515             tpls.row.disableFormats = true;
53516         }
53517         tpls.row.compile();
53518
53519         if(!tpls.cell){
53520             tpls.cell = new Roo.Template(
53521                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53522                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53523                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53524                 "</td>"
53525             );
53526             tpls.cell.disableFormats = true;
53527         }
53528         tpls.cell.compile();
53529
53530         this.templates = tpls;
53531     },
53532
53533     // remap these for backwards compat
53534     onColWidthChange : function(){
53535         this.updateColumns.apply(this, arguments);
53536     },
53537     onHeaderChange : function(){
53538         this.updateHeaders.apply(this, arguments);
53539     }, 
53540     onHiddenChange : function(){
53541         this.handleHiddenChange.apply(this, arguments);
53542     },
53543     onColumnMove : function(){
53544         this.handleColumnMove.apply(this, arguments);
53545     },
53546     onColumnLock : function(){
53547         this.handleLockChange.apply(this, arguments);
53548     },
53549
53550     onDataChange : function(){
53551         this.refresh();
53552         this.updateHeaderSortState();
53553     },
53554
53555     onClear : function(){
53556         this.refresh();
53557     },
53558
53559     onUpdate : function(ds, record){
53560         this.refreshRow(record);
53561     },
53562
53563     refreshRow : function(record){
53564         var ds = this.ds, index;
53565         if(typeof record == 'number'){
53566             index = record;
53567             record = ds.getAt(index);
53568         }else{
53569             index = ds.indexOf(record);
53570         }
53571         this.insertRows(ds, index, index, true);
53572         this.onRemove(ds, record, index+1, true);
53573         this.syncRowHeights(index, index);
53574         this.layout();
53575         this.fireEvent("rowupdated", this, index, record);
53576     },
53577
53578     onAdd : function(ds, records, index){
53579         this.insertRows(ds, index, index + (records.length-1));
53580     },
53581
53582     onRemove : function(ds, record, index, isUpdate){
53583         if(isUpdate !== true){
53584             this.fireEvent("beforerowremoved", this, index, record);
53585         }
53586         var bt = this.getBodyTable(), lt = this.getLockedTable();
53587         if(bt.rows[index]){
53588             bt.firstChild.removeChild(bt.rows[index]);
53589         }
53590         if(lt.rows[index]){
53591             lt.firstChild.removeChild(lt.rows[index]);
53592         }
53593         if(isUpdate !== true){
53594             this.stripeRows(index);
53595             this.syncRowHeights(index, index);
53596             this.layout();
53597             this.fireEvent("rowremoved", this, index, record);
53598         }
53599     },
53600
53601     onLoad : function(){
53602         this.scrollToTop();
53603     },
53604
53605     /**
53606      * Scrolls the grid to the top
53607      */
53608     scrollToTop : function(){
53609         if(this.scroller){
53610             this.scroller.dom.scrollTop = 0;
53611             this.syncScroll();
53612         }
53613     },
53614
53615     /**
53616      * Gets a panel in the header of the grid that can be used for toolbars etc.
53617      * After modifying the contents of this panel a call to grid.autoSize() may be
53618      * required to register any changes in size.
53619      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53620      * @return Roo.Element
53621      */
53622     getHeaderPanel : function(doShow){
53623         if(doShow){
53624             this.headerPanel.show();
53625         }
53626         return this.headerPanel;
53627     },
53628
53629     /**
53630      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53631      * After modifying the contents of this panel a call to grid.autoSize() may be
53632      * required to register any changes in size.
53633      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53634      * @return Roo.Element
53635      */
53636     getFooterPanel : function(doShow){
53637         if(doShow){
53638             this.footerPanel.show();
53639         }
53640         return this.footerPanel;
53641     },
53642
53643     initElements : function(){
53644         var E = Roo.Element;
53645         var el = this.grid.getGridEl().dom.firstChild;
53646         var cs = el.childNodes;
53647
53648         this.el = new E(el);
53649         
53650          this.focusEl = new E(el.firstChild);
53651         this.focusEl.swallowEvent("click", true);
53652         
53653         this.headerPanel = new E(cs[1]);
53654         this.headerPanel.enableDisplayMode("block");
53655
53656         this.scroller = new E(cs[2]);
53657         this.scrollSizer = new E(this.scroller.dom.firstChild);
53658
53659         this.lockedWrap = new E(cs[3]);
53660         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53661         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53662
53663         this.mainWrap = new E(cs[4]);
53664         this.mainHd = new E(this.mainWrap.dom.firstChild);
53665         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53666
53667         this.footerPanel = new E(cs[5]);
53668         this.footerPanel.enableDisplayMode("block");
53669
53670         this.resizeProxy = new E(cs[6]);
53671
53672         this.headerSelector = String.format(
53673            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53674            this.lockedHd.id, this.mainHd.id
53675         );
53676
53677         this.splitterSelector = String.format(
53678            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53679            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53680         );
53681     },
53682     idToCssName : function(s)
53683     {
53684         return s.replace(/[^a-z0-9]+/ig, '-');
53685     },
53686
53687     getHeaderCell : function(index){
53688         return Roo.DomQuery.select(this.headerSelector)[index];
53689     },
53690
53691     getHeaderCellMeasure : function(index){
53692         return this.getHeaderCell(index).firstChild;
53693     },
53694
53695     getHeaderCellText : function(index){
53696         return this.getHeaderCell(index).firstChild.firstChild;
53697     },
53698
53699     getLockedTable : function(){
53700         return this.lockedBody.dom.firstChild;
53701     },
53702
53703     getBodyTable : function(){
53704         return this.mainBody.dom.firstChild;
53705     },
53706
53707     getLockedRow : function(index){
53708         return this.getLockedTable().rows[index];
53709     },
53710
53711     getRow : function(index){
53712         return this.getBodyTable().rows[index];
53713     },
53714
53715     getRowComposite : function(index){
53716         if(!this.rowEl){
53717             this.rowEl = new Roo.CompositeElementLite();
53718         }
53719         var els = [], lrow, mrow;
53720         if(lrow = this.getLockedRow(index)){
53721             els.push(lrow);
53722         }
53723         if(mrow = this.getRow(index)){
53724             els.push(mrow);
53725         }
53726         this.rowEl.elements = els;
53727         return this.rowEl;
53728     },
53729     /**
53730      * Gets the 'td' of the cell
53731      * 
53732      * @param {Integer} rowIndex row to select
53733      * @param {Integer} colIndex column to select
53734      * 
53735      * @return {Object} 
53736      */
53737     getCell : function(rowIndex, colIndex){
53738         var locked = this.cm.getLockedCount();
53739         var source;
53740         if(colIndex < locked){
53741             source = this.lockedBody.dom.firstChild;
53742         }else{
53743             source = this.mainBody.dom.firstChild;
53744             colIndex -= locked;
53745         }
53746         return source.rows[rowIndex].childNodes[colIndex];
53747     },
53748
53749     getCellText : function(rowIndex, colIndex){
53750         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53751     },
53752
53753     getCellBox : function(cell){
53754         var b = this.fly(cell).getBox();
53755         if(Roo.isOpera){ // opera fails to report the Y
53756             b.y = cell.offsetTop + this.mainBody.getY();
53757         }
53758         return b;
53759     },
53760
53761     getCellIndex : function(cell){
53762         var id = String(cell.className).match(this.cellRE);
53763         if(id){
53764             return parseInt(id[1], 10);
53765         }
53766         return 0;
53767     },
53768
53769     findHeaderIndex : function(n){
53770         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53771         return r ? this.getCellIndex(r) : false;
53772     },
53773
53774     findHeaderCell : function(n){
53775         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53776         return r ? r : false;
53777     },
53778
53779     findRowIndex : function(n){
53780         if(!n){
53781             return false;
53782         }
53783         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53784         return r ? r.rowIndex : false;
53785     },
53786
53787     findCellIndex : function(node){
53788         var stop = this.el.dom;
53789         while(node && node != stop){
53790             if(this.findRE.test(node.className)){
53791                 return this.getCellIndex(node);
53792             }
53793             node = node.parentNode;
53794         }
53795         return false;
53796     },
53797
53798     getColumnId : function(index){
53799         return this.cm.getColumnId(index);
53800     },
53801
53802     getSplitters : function()
53803     {
53804         if(this.splitterSelector){
53805            return Roo.DomQuery.select(this.splitterSelector);
53806         }else{
53807             return null;
53808       }
53809     },
53810
53811     getSplitter : function(index){
53812         return this.getSplitters()[index];
53813     },
53814
53815     onRowOver : function(e, t){
53816         var row;
53817         if((row = this.findRowIndex(t)) !== false){
53818             this.getRowComposite(row).addClass("x-grid-row-over");
53819         }
53820     },
53821
53822     onRowOut : function(e, t){
53823         var row;
53824         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53825             this.getRowComposite(row).removeClass("x-grid-row-over");
53826         }
53827     },
53828
53829     renderHeaders : function(){
53830         var cm = this.cm;
53831         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53832         var cb = [], lb = [], sb = [], lsb = [], p = {};
53833         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53834             p.cellId = "x-grid-hd-0-" + i;
53835             p.splitId = "x-grid-csplit-0-" + i;
53836             p.id = cm.getColumnId(i);
53837             p.title = cm.getColumnTooltip(i) || "";
53838             p.value = cm.getColumnHeader(i) || "";
53839             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53840             if(!cm.isLocked(i)){
53841                 cb[cb.length] = ct.apply(p);
53842                 sb[sb.length] = st.apply(p);
53843             }else{
53844                 lb[lb.length] = ct.apply(p);
53845                 lsb[lsb.length] = st.apply(p);
53846             }
53847         }
53848         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53849                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53850     },
53851
53852     updateHeaders : function(){
53853         var html = this.renderHeaders();
53854         this.lockedHd.update(html[0]);
53855         this.mainHd.update(html[1]);
53856     },
53857
53858     /**
53859      * Focuses the specified row.
53860      * @param {Number} row The row index
53861      */
53862     focusRow : function(row)
53863     {
53864         //Roo.log('GridView.focusRow');
53865         var x = this.scroller.dom.scrollLeft;
53866         this.focusCell(row, 0, false);
53867         this.scroller.dom.scrollLeft = x;
53868     },
53869
53870     /**
53871      * Focuses the specified cell.
53872      * @param {Number} row The row index
53873      * @param {Number} col The column index
53874      * @param {Boolean} hscroll false to disable horizontal scrolling
53875      */
53876     focusCell : function(row, col, hscroll)
53877     {
53878         //Roo.log('GridView.focusCell');
53879         var el = this.ensureVisible(row, col, hscroll);
53880         this.focusEl.alignTo(el, "tl-tl");
53881         if(Roo.isGecko){
53882             this.focusEl.focus();
53883         }else{
53884             this.focusEl.focus.defer(1, this.focusEl);
53885         }
53886     },
53887
53888     /**
53889      * Scrolls the specified cell into view
53890      * @param {Number} row The row index
53891      * @param {Number} col The column index
53892      * @param {Boolean} hscroll false to disable horizontal scrolling
53893      */
53894     ensureVisible : function(row, col, hscroll)
53895     {
53896         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53897         //return null; //disable for testing.
53898         if(typeof row != "number"){
53899             row = row.rowIndex;
53900         }
53901         if(row < 0 && row >= this.ds.getCount()){
53902             return  null;
53903         }
53904         col = (col !== undefined ? col : 0);
53905         var cm = this.grid.colModel;
53906         while(cm.isHidden(col)){
53907             col++;
53908         }
53909
53910         var el = this.getCell(row, col);
53911         if(!el){
53912             return null;
53913         }
53914         var c = this.scroller.dom;
53915
53916         var ctop = parseInt(el.offsetTop, 10);
53917         var cleft = parseInt(el.offsetLeft, 10);
53918         var cbot = ctop + el.offsetHeight;
53919         var cright = cleft + el.offsetWidth;
53920         
53921         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53922         var stop = parseInt(c.scrollTop, 10);
53923         var sleft = parseInt(c.scrollLeft, 10);
53924         var sbot = stop + ch;
53925         var sright = sleft + c.clientWidth;
53926         /*
53927         Roo.log('GridView.ensureVisible:' +
53928                 ' ctop:' + ctop +
53929                 ' c.clientHeight:' + c.clientHeight +
53930                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53931                 ' stop:' + stop +
53932                 ' cbot:' + cbot +
53933                 ' sbot:' + sbot +
53934                 ' ch:' + ch  
53935                 );
53936         */
53937         if(ctop < stop){
53938              c.scrollTop = ctop;
53939             //Roo.log("set scrolltop to ctop DISABLE?");
53940         }else if(cbot > sbot){
53941             //Roo.log("set scrolltop to cbot-ch");
53942             c.scrollTop = cbot-ch;
53943         }
53944         
53945         if(hscroll !== false){
53946             if(cleft < sleft){
53947                 c.scrollLeft = cleft;
53948             }else if(cright > sright){
53949                 c.scrollLeft = cright-c.clientWidth;
53950             }
53951         }
53952          
53953         return el;
53954     },
53955
53956     updateColumns : function(){
53957         this.grid.stopEditing();
53958         var cm = this.grid.colModel, colIds = this.getColumnIds();
53959         //var totalWidth = cm.getTotalWidth();
53960         var pos = 0;
53961         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53962             //if(cm.isHidden(i)) continue;
53963             var w = cm.getColumnWidth(i);
53964             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53965             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53966         }
53967         this.updateSplitters();
53968     },
53969
53970     generateRules : function(cm){
53971         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53972         Roo.util.CSS.removeStyleSheet(rulesId);
53973         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53974             var cid = cm.getColumnId(i);
53975             var align = '';
53976             if(cm.config[i].align){
53977                 align = 'text-align:'+cm.config[i].align+';';
53978             }
53979             var hidden = '';
53980             if(cm.isHidden(i)){
53981                 hidden = 'display:none;';
53982             }
53983             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53984             ruleBuf.push(
53985                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53986                     this.hdSelector, cid, " {\n", align, width, "}\n",
53987                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53988                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53989         }
53990         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53991     },
53992
53993     updateSplitters : function(){
53994         var cm = this.cm, s = this.getSplitters();
53995         if(s){ // splitters not created yet
53996             var pos = 0, locked = true;
53997             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53998                 if(cm.isHidden(i)) continue;
53999                 var w = cm.getColumnWidth(i); // make sure it's a number
54000                 if(!cm.isLocked(i) && locked){
54001                     pos = 0;
54002                     locked = false;
54003                 }
54004                 pos += w;
54005                 s[i].style.left = (pos-this.splitOffset) + "px";
54006             }
54007         }
54008     },
54009
54010     handleHiddenChange : function(colModel, colIndex, hidden){
54011         if(hidden){
54012             this.hideColumn(colIndex);
54013         }else{
54014             this.unhideColumn(colIndex);
54015         }
54016     },
54017
54018     hideColumn : function(colIndex){
54019         var cid = this.getColumnId(colIndex);
54020         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54021         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54022         if(Roo.isSafari){
54023             this.updateHeaders();
54024         }
54025         this.updateSplitters();
54026         this.layout();
54027     },
54028
54029     unhideColumn : function(colIndex){
54030         var cid = this.getColumnId(colIndex);
54031         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54032         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54033
54034         if(Roo.isSafari){
54035             this.updateHeaders();
54036         }
54037         this.updateSplitters();
54038         this.layout();
54039     },
54040
54041     insertRows : function(dm, firstRow, lastRow, isUpdate){
54042         if(firstRow == 0 && lastRow == dm.getCount()-1){
54043             this.refresh();
54044         }else{
54045             if(!isUpdate){
54046                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54047             }
54048             var s = this.getScrollState();
54049             var markup = this.renderRows(firstRow, lastRow);
54050             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54051             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54052             this.restoreScroll(s);
54053             if(!isUpdate){
54054                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54055                 this.syncRowHeights(firstRow, lastRow);
54056                 this.stripeRows(firstRow);
54057                 this.layout();
54058             }
54059         }
54060     },
54061
54062     bufferRows : function(markup, target, index){
54063         var before = null, trows = target.rows, tbody = target.tBodies[0];
54064         if(index < trows.length){
54065             before = trows[index];
54066         }
54067         var b = document.createElement("div");
54068         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54069         var rows = b.firstChild.rows;
54070         for(var i = 0, len = rows.length; i < len; i++){
54071             if(before){
54072                 tbody.insertBefore(rows[0], before);
54073             }else{
54074                 tbody.appendChild(rows[0]);
54075             }
54076         }
54077         b.innerHTML = "";
54078         b = null;
54079     },
54080
54081     deleteRows : function(dm, firstRow, lastRow){
54082         if(dm.getRowCount()<1){
54083             this.fireEvent("beforerefresh", this);
54084             this.mainBody.update("");
54085             this.lockedBody.update("");
54086             this.fireEvent("refresh", this);
54087         }else{
54088             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54089             var bt = this.getBodyTable();
54090             var tbody = bt.firstChild;
54091             var rows = bt.rows;
54092             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54093                 tbody.removeChild(rows[firstRow]);
54094             }
54095             this.stripeRows(firstRow);
54096             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54097         }
54098     },
54099
54100     updateRows : function(dataSource, firstRow, lastRow){
54101         var s = this.getScrollState();
54102         this.refresh();
54103         this.restoreScroll(s);
54104     },
54105
54106     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54107         if(!noRefresh){
54108            this.refresh();
54109         }
54110         this.updateHeaderSortState();
54111     },
54112
54113     getScrollState : function(){
54114         
54115         var sb = this.scroller.dom;
54116         return {left: sb.scrollLeft, top: sb.scrollTop};
54117     },
54118
54119     stripeRows : function(startRow){
54120         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54121             return;
54122         }
54123         startRow = startRow || 0;
54124         var rows = this.getBodyTable().rows;
54125         var lrows = this.getLockedTable().rows;
54126         var cls = ' x-grid-row-alt ';
54127         for(var i = startRow, len = rows.length; i < len; i++){
54128             var row = rows[i], lrow = lrows[i];
54129             var isAlt = ((i+1) % 2 == 0);
54130             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54131             if(isAlt == hasAlt){
54132                 continue;
54133             }
54134             if(isAlt){
54135                 row.className += " x-grid-row-alt";
54136             }else{
54137                 row.className = row.className.replace("x-grid-row-alt", "");
54138             }
54139             if(lrow){
54140                 lrow.className = row.className;
54141             }
54142         }
54143     },
54144
54145     restoreScroll : function(state){
54146         //Roo.log('GridView.restoreScroll');
54147         var sb = this.scroller.dom;
54148         sb.scrollLeft = state.left;
54149         sb.scrollTop = state.top;
54150         this.syncScroll();
54151     },
54152
54153     syncScroll : function(){
54154         //Roo.log('GridView.syncScroll');
54155         var sb = this.scroller.dom;
54156         var sh = this.mainHd.dom;
54157         var bs = this.mainBody.dom;
54158         var lv = this.lockedBody.dom;
54159         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54160         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54161     },
54162
54163     handleScroll : function(e){
54164         this.syncScroll();
54165         var sb = this.scroller.dom;
54166         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54167         e.stopEvent();
54168     },
54169
54170     handleWheel : function(e){
54171         var d = e.getWheelDelta();
54172         this.scroller.dom.scrollTop -= d*22;
54173         // set this here to prevent jumpy scrolling on large tables
54174         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54175         e.stopEvent();
54176     },
54177
54178     renderRows : function(startRow, endRow){
54179         // pull in all the crap needed to render rows
54180         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54181         var colCount = cm.getColumnCount();
54182
54183         if(ds.getCount() < 1){
54184             return ["", ""];
54185         }
54186
54187         // build a map for all the columns
54188         var cs = [];
54189         for(var i = 0; i < colCount; i++){
54190             var name = cm.getDataIndex(i);
54191             cs[i] = {
54192                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54193                 renderer : cm.getRenderer(i),
54194                 id : cm.getColumnId(i),
54195                 locked : cm.isLocked(i)
54196             };
54197         }
54198
54199         startRow = startRow || 0;
54200         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54201
54202         // records to render
54203         var rs = ds.getRange(startRow, endRow);
54204
54205         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54206     },
54207
54208     // As much as I hate to duplicate code, this was branched because FireFox really hates
54209     // [].join("") on strings. The performance difference was substantial enough to
54210     // branch this function
54211     doRender : Roo.isGecko ?
54212             function(cs, rs, ds, startRow, colCount, stripe){
54213                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54214                 // buffers
54215                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54216                 
54217                 var hasListener = this.grid.hasListener('rowclass');
54218                 var rowcfg = {};
54219                 for(var j = 0, len = rs.length; j < len; j++){
54220                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54221                     for(var i = 0; i < colCount; i++){
54222                         c = cs[i];
54223                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54224                         p.id = c.id;
54225                         p.css = p.attr = "";
54226                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54227                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54228                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54229                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54230                         }
54231                         var markup = ct.apply(p);
54232                         if(!c.locked){
54233                             cb+= markup;
54234                         }else{
54235                             lcb+= markup;
54236                         }
54237                     }
54238                     var alt = [];
54239                     if(stripe && ((rowIndex+1) % 2 == 0)){
54240                         alt.push("x-grid-row-alt")
54241                     }
54242                     if(r.dirty){
54243                         alt.push(  " x-grid-dirty-row");
54244                     }
54245                     rp.cells = lcb;
54246                     if(this.getRowClass){
54247                         alt.push(this.getRowClass(r, rowIndex));
54248                     }
54249                     if (hasListener) {
54250                         rowcfg = {
54251                              
54252                             record: r,
54253                             rowIndex : rowIndex,
54254                             rowClass : ''
54255                         }
54256                         this.grid.fireEvent('rowclass', this, rowcfg);
54257                         alt.push(rowcfg.rowClass);
54258                     }
54259                     rp.alt = alt.join(" ");
54260                     lbuf+= rt.apply(rp);
54261                     rp.cells = cb;
54262                     buf+=  rt.apply(rp);
54263                 }
54264                 return [lbuf, buf];
54265             } :
54266             function(cs, rs, ds, startRow, colCount, stripe){
54267                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54268                 // buffers
54269                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54270                 var hasListener = this.grid.hasListener('rowclass');
54271  
54272                 var rowcfg = {};
54273                 for(var j = 0, len = rs.length; j < len; j++){
54274                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54275                     for(var i = 0; i < colCount; i++){
54276                         c = cs[i];
54277                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54278                         p.id = c.id;
54279                         p.css = p.attr = "";
54280                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54281                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54282                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54283                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54284                         }
54285                         
54286                         var markup = ct.apply(p);
54287                         if(!c.locked){
54288                             cb[cb.length] = markup;
54289                         }else{
54290                             lcb[lcb.length] = markup;
54291                         }
54292                     }
54293                     var alt = [];
54294                     if(stripe && ((rowIndex+1) % 2 == 0)){
54295                         alt.push( "x-grid-row-alt");
54296                     }
54297                     if(r.dirty){
54298                         alt.push(" x-grid-dirty-row");
54299                     }
54300                     rp.cells = lcb;
54301                     if(this.getRowClass){
54302                         alt.push( this.getRowClass(r, rowIndex));
54303                     }
54304                     if (hasListener) {
54305                         rowcfg = {
54306                              
54307                             record: r,
54308                             rowIndex : rowIndex,
54309                             rowClass : ''
54310                         }
54311                         this.grid.fireEvent('rowclass', this, rowcfg);
54312                         alt.push(rowcfg.rowClass);
54313                     }
54314                     rp.alt = alt.join(" ");
54315                     rp.cells = lcb.join("");
54316                     lbuf[lbuf.length] = rt.apply(rp);
54317                     rp.cells = cb.join("");
54318                     buf[buf.length] =  rt.apply(rp);
54319                 }
54320                 return [lbuf.join(""), buf.join("")];
54321             },
54322
54323     renderBody : function(){
54324         var markup = this.renderRows();
54325         var bt = this.templates.body;
54326         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54327     },
54328
54329     /**
54330      * Refreshes the grid
54331      * @param {Boolean} headersToo
54332      */
54333     refresh : function(headersToo){
54334         this.fireEvent("beforerefresh", this);
54335         this.grid.stopEditing();
54336         var result = this.renderBody();
54337         this.lockedBody.update(result[0]);
54338         this.mainBody.update(result[1]);
54339         if(headersToo === true){
54340             this.updateHeaders();
54341             this.updateColumns();
54342             this.updateSplitters();
54343             this.updateHeaderSortState();
54344         }
54345         this.syncRowHeights();
54346         this.layout();
54347         this.fireEvent("refresh", this);
54348     },
54349
54350     handleColumnMove : function(cm, oldIndex, newIndex){
54351         this.indexMap = null;
54352         var s = this.getScrollState();
54353         this.refresh(true);
54354         this.restoreScroll(s);
54355         this.afterMove(newIndex);
54356     },
54357
54358     afterMove : function(colIndex){
54359         if(this.enableMoveAnim && Roo.enableFx){
54360             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54361         }
54362         // if multisort - fix sortOrder, and reload..
54363         if (this.grid.dataSource.multiSort) {
54364             // the we can call sort again..
54365             var dm = this.grid.dataSource;
54366             var cm = this.grid.colModel;
54367             var so = [];
54368             for(var i = 0; i < cm.config.length; i++ ) {
54369                 
54370                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54371                     continue; // dont' bother, it's not in sort list or being set.
54372                 }
54373                 
54374                 so.push(cm.config[i].dataIndex);
54375             };
54376             dm.sortOrder = so;
54377             dm.load(dm.lastOptions);
54378             
54379             
54380         }
54381         
54382     },
54383
54384     updateCell : function(dm, rowIndex, dataIndex){
54385         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54386         if(typeof colIndex == "undefined"){ // not present in grid
54387             return;
54388         }
54389         var cm = this.grid.colModel;
54390         var cell = this.getCell(rowIndex, colIndex);
54391         var cellText = this.getCellText(rowIndex, colIndex);
54392
54393         var p = {
54394             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54395             id : cm.getColumnId(colIndex),
54396             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54397         };
54398         var renderer = cm.getRenderer(colIndex);
54399         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54400         if(typeof val == "undefined" || val === "") val = "&#160;";
54401         cellText.innerHTML = val;
54402         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54403         this.syncRowHeights(rowIndex, rowIndex);
54404     },
54405
54406     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54407         var maxWidth = 0;
54408         if(this.grid.autoSizeHeaders){
54409             var h = this.getHeaderCellMeasure(colIndex);
54410             maxWidth = Math.max(maxWidth, h.scrollWidth);
54411         }
54412         var tb, index;
54413         if(this.cm.isLocked(colIndex)){
54414             tb = this.getLockedTable();
54415             index = colIndex;
54416         }else{
54417             tb = this.getBodyTable();
54418             index = colIndex - this.cm.getLockedCount();
54419         }
54420         if(tb && tb.rows){
54421             var rows = tb.rows;
54422             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54423             for(var i = 0; i < stopIndex; i++){
54424                 var cell = rows[i].childNodes[index].firstChild;
54425                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54426             }
54427         }
54428         return maxWidth + /*margin for error in IE*/ 5;
54429     },
54430     /**
54431      * Autofit a column to its content.
54432      * @param {Number} colIndex
54433      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54434      */
54435      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54436          if(this.cm.isHidden(colIndex)){
54437              return; // can't calc a hidden column
54438          }
54439         if(forceMinSize){
54440             var cid = this.cm.getColumnId(colIndex);
54441             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54442            if(this.grid.autoSizeHeaders){
54443                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54444            }
54445         }
54446         var newWidth = this.calcColumnWidth(colIndex);
54447         this.cm.setColumnWidth(colIndex,
54448             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54449         if(!suppressEvent){
54450             this.grid.fireEvent("columnresize", colIndex, newWidth);
54451         }
54452     },
54453
54454     /**
54455      * Autofits all columns to their content and then expands to fit any extra space in the grid
54456      */
54457      autoSizeColumns : function(){
54458         var cm = this.grid.colModel;
54459         var colCount = cm.getColumnCount();
54460         for(var i = 0; i < colCount; i++){
54461             this.autoSizeColumn(i, true, true);
54462         }
54463         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54464             this.fitColumns();
54465         }else{
54466             this.updateColumns();
54467             this.layout();
54468         }
54469     },
54470
54471     /**
54472      * Autofits all columns to the grid's width proportionate with their current size
54473      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54474      */
54475     fitColumns : function(reserveScrollSpace){
54476         var cm = this.grid.colModel;
54477         var colCount = cm.getColumnCount();
54478         var cols = [];
54479         var width = 0;
54480         var i, w;
54481         for (i = 0; i < colCount; i++){
54482             if(!cm.isHidden(i) && !cm.isFixed(i)){
54483                 w = cm.getColumnWidth(i);
54484                 cols.push(i);
54485                 cols.push(w);
54486                 width += w;
54487             }
54488         }
54489         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54490         if(reserveScrollSpace){
54491             avail -= 17;
54492         }
54493         var frac = (avail - cm.getTotalWidth())/width;
54494         while (cols.length){
54495             w = cols.pop();
54496             i = cols.pop();
54497             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54498         }
54499         this.updateColumns();
54500         this.layout();
54501     },
54502
54503     onRowSelect : function(rowIndex){
54504         var row = this.getRowComposite(rowIndex);
54505         row.addClass("x-grid-row-selected");
54506     },
54507
54508     onRowDeselect : function(rowIndex){
54509         var row = this.getRowComposite(rowIndex);
54510         row.removeClass("x-grid-row-selected");
54511     },
54512
54513     onCellSelect : function(row, col){
54514         var cell = this.getCell(row, col);
54515         if(cell){
54516             Roo.fly(cell).addClass("x-grid-cell-selected");
54517         }
54518     },
54519
54520     onCellDeselect : function(row, col){
54521         var cell = this.getCell(row, col);
54522         if(cell){
54523             Roo.fly(cell).removeClass("x-grid-cell-selected");
54524         }
54525     },
54526
54527     updateHeaderSortState : function(){
54528         
54529         // sort state can be single { field: xxx, direction : yyy}
54530         // or   { xxx=>ASC , yyy : DESC ..... }
54531         
54532         var mstate = {};
54533         if (!this.ds.multiSort) { 
54534             var state = this.ds.getSortState();
54535             if(!state){
54536                 return;
54537             }
54538             mstate[state.field] = state.direction;
54539             // FIXME... - this is not used here.. but might be elsewhere..
54540             this.sortState = state;
54541             
54542         } else {
54543             mstate = this.ds.sortToggle;
54544         }
54545         //remove existing sort classes..
54546         
54547         var sc = this.sortClasses;
54548         var hds = this.el.select(this.headerSelector).removeClass(sc);
54549         
54550         for(var f in mstate) {
54551         
54552             var sortColumn = this.cm.findColumnIndex(f);
54553             
54554             if(sortColumn != -1){
54555                 var sortDir = mstate[f];        
54556                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54557             }
54558         }
54559         
54560          
54561         
54562     },
54563
54564
54565     handleHeaderClick : function(g, index,e){
54566         
54567         Roo.log("header click");
54568         
54569         if (Roo.isTouch) {
54570             // touch events on header are handled by context
54571             this.handleHdCtx(g,index,e);
54572             return;
54573         }
54574         
54575         
54576         if(this.headersDisabled){
54577             return;
54578         }
54579         var dm = g.dataSource, cm = g.colModel;
54580         if(!cm.isSortable(index)){
54581             return;
54582         }
54583         g.stopEditing();
54584         
54585         if (dm.multiSort) {
54586             // update the sortOrder
54587             var so = [];
54588             for(var i = 0; i < cm.config.length; i++ ) {
54589                 
54590                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54591                     continue; // dont' bother, it's not in sort list or being set.
54592                 }
54593                 
54594                 so.push(cm.config[i].dataIndex);
54595             };
54596             dm.sortOrder = so;
54597         }
54598         
54599         
54600         dm.sort(cm.getDataIndex(index));
54601     },
54602
54603
54604     destroy : function(){
54605         if(this.colMenu){
54606             this.colMenu.removeAll();
54607             Roo.menu.MenuMgr.unregister(this.colMenu);
54608             this.colMenu.getEl().remove();
54609             delete this.colMenu;
54610         }
54611         if(this.hmenu){
54612             this.hmenu.removeAll();
54613             Roo.menu.MenuMgr.unregister(this.hmenu);
54614             this.hmenu.getEl().remove();
54615             delete this.hmenu;
54616         }
54617         if(this.grid.enableColumnMove){
54618             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54619             if(dds){
54620                 for(var dd in dds){
54621                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54622                         var elid = dds[dd].dragElId;
54623                         dds[dd].unreg();
54624                         Roo.get(elid).remove();
54625                     } else if(dds[dd].config.isTarget){
54626                         dds[dd].proxyTop.remove();
54627                         dds[dd].proxyBottom.remove();
54628                         dds[dd].unreg();
54629                     }
54630                     if(Roo.dd.DDM.locationCache[dd]){
54631                         delete Roo.dd.DDM.locationCache[dd];
54632                     }
54633                 }
54634                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54635             }
54636         }
54637         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54638         this.bind(null, null);
54639         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54640     },
54641
54642     handleLockChange : function(){
54643         this.refresh(true);
54644     },
54645
54646     onDenyColumnLock : function(){
54647
54648     },
54649
54650     onDenyColumnHide : function(){
54651
54652     },
54653
54654     handleHdMenuClick : function(item){
54655         var index = this.hdCtxIndex;
54656         var cm = this.cm, ds = this.ds;
54657         switch(item.id){
54658             case "asc":
54659                 ds.sort(cm.getDataIndex(index), "ASC");
54660                 break;
54661             case "desc":
54662                 ds.sort(cm.getDataIndex(index), "DESC");
54663                 break;
54664             case "lock":
54665                 var lc = cm.getLockedCount();
54666                 if(cm.getColumnCount(true) <= lc+1){
54667                     this.onDenyColumnLock();
54668                     return;
54669                 }
54670                 if(lc != index){
54671                     cm.setLocked(index, true, true);
54672                     cm.moveColumn(index, lc);
54673                     this.grid.fireEvent("columnmove", index, lc);
54674                 }else{
54675                     cm.setLocked(index, true);
54676                 }
54677             break;
54678             case "unlock":
54679                 var lc = cm.getLockedCount();
54680                 if((lc-1) != index){
54681                     cm.setLocked(index, false, true);
54682                     cm.moveColumn(index, lc-1);
54683                     this.grid.fireEvent("columnmove", index, lc-1);
54684                 }else{
54685                     cm.setLocked(index, false);
54686                 }
54687             break;
54688             case 'wider': // used to expand cols on touch..
54689             case 'narrow':
54690                 var cw = cm.getColumnWidth(index);
54691                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54692                 cw = Math.max(0, cw);
54693                 cw = Math.min(cw,4000);
54694                 cm.setColumnWidth(index, cw);
54695                 break;
54696                 
54697             default:
54698                 index = cm.getIndexById(item.id.substr(4));
54699                 if(index != -1){
54700                     if(item.checked && cm.getColumnCount(true) <= 1){
54701                         this.onDenyColumnHide();
54702                         return false;
54703                     }
54704                     cm.setHidden(index, item.checked);
54705                 }
54706         }
54707         return true;
54708     },
54709
54710     beforeColMenuShow : function(){
54711         var cm = this.cm,  colCount = cm.getColumnCount();
54712         this.colMenu.removeAll();
54713         for(var i = 0; i < colCount; i++){
54714             this.colMenu.add(new Roo.menu.CheckItem({
54715                 id: "col-"+cm.getColumnId(i),
54716                 text: cm.getColumnHeader(i),
54717                 checked: !cm.isHidden(i),
54718                 hideOnClick:false
54719             }));
54720         }
54721     },
54722
54723     handleHdCtx : function(g, index, e){
54724         e.stopEvent();
54725         var hd = this.getHeaderCell(index);
54726         this.hdCtxIndex = index;
54727         var ms = this.hmenu.items, cm = this.cm;
54728         ms.get("asc").setDisabled(!cm.isSortable(index));
54729         ms.get("desc").setDisabled(!cm.isSortable(index));
54730         if(this.grid.enableColLock !== false){
54731             ms.get("lock").setDisabled(cm.isLocked(index));
54732             ms.get("unlock").setDisabled(!cm.isLocked(index));
54733         }
54734         this.hmenu.show(hd, "tl-bl");
54735     },
54736
54737     handleHdOver : function(e){
54738         var hd = this.findHeaderCell(e.getTarget());
54739         if(hd && !this.headersDisabled){
54740             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54741                this.fly(hd).addClass("x-grid-hd-over");
54742             }
54743         }
54744     },
54745
54746     handleHdOut : function(e){
54747         var hd = this.findHeaderCell(e.getTarget());
54748         if(hd){
54749             this.fly(hd).removeClass("x-grid-hd-over");
54750         }
54751     },
54752
54753     handleSplitDblClick : function(e, t){
54754         var i = this.getCellIndex(t);
54755         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54756             this.autoSizeColumn(i, true);
54757             this.layout();
54758         }
54759     },
54760
54761     render : function(){
54762
54763         var cm = this.cm;
54764         var colCount = cm.getColumnCount();
54765
54766         if(this.grid.monitorWindowResize === true){
54767             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54768         }
54769         var header = this.renderHeaders();
54770         var body = this.templates.body.apply({rows:""});
54771         var html = this.templates.master.apply({
54772             lockedBody: body,
54773             body: body,
54774             lockedHeader: header[0],
54775             header: header[1]
54776         });
54777
54778         //this.updateColumns();
54779
54780         this.grid.getGridEl().dom.innerHTML = html;
54781
54782         this.initElements();
54783         
54784         // a kludge to fix the random scolling effect in webkit
54785         this.el.on("scroll", function() {
54786             this.el.dom.scrollTop=0; // hopefully not recursive..
54787         },this);
54788
54789         this.scroller.on("scroll", this.handleScroll, this);
54790         this.lockedBody.on("mousewheel", this.handleWheel, this);
54791         this.mainBody.on("mousewheel", this.handleWheel, this);
54792
54793         this.mainHd.on("mouseover", this.handleHdOver, this);
54794         this.mainHd.on("mouseout", this.handleHdOut, this);
54795         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54796                 {delegate: "."+this.splitClass});
54797
54798         this.lockedHd.on("mouseover", this.handleHdOver, this);
54799         this.lockedHd.on("mouseout", this.handleHdOut, this);
54800         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54801                 {delegate: "."+this.splitClass});
54802
54803         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54804             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54805         }
54806
54807         this.updateSplitters();
54808
54809         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54810             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54811             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54812         }
54813
54814         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54815             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54816             this.hmenu.add(
54817                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54818                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54819             );
54820             if(this.grid.enableColLock !== false){
54821                 this.hmenu.add('-',
54822                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54823                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54824                 );
54825             }
54826             if (Roo.isTouch) {
54827                  this.hmenu.add('-',
54828                     {id:"wider", text: this.columnsWiderText},
54829                     {id:"narrow", text: this.columnsNarrowText }
54830                 );
54831                 
54832                  
54833             }
54834             
54835             if(this.grid.enableColumnHide !== false){
54836
54837                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54838                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54839                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54840
54841                 this.hmenu.add('-',
54842                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54843                 );
54844             }
54845             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54846
54847             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54848         }
54849
54850         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54851             this.dd = new Roo.grid.GridDragZone(this.grid, {
54852                 ddGroup : this.grid.ddGroup || 'GridDD'
54853             });
54854             
54855         }
54856
54857         /*
54858         for(var i = 0; i < colCount; i++){
54859             if(cm.isHidden(i)){
54860                 this.hideColumn(i);
54861             }
54862             if(cm.config[i].align){
54863                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54864                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54865             }
54866         }*/
54867         
54868         this.updateHeaderSortState();
54869
54870         this.beforeInitialResize();
54871         this.layout(true);
54872
54873         // two part rendering gives faster view to the user
54874         this.renderPhase2.defer(1, this);
54875     },
54876
54877     renderPhase2 : function(){
54878         // render the rows now
54879         this.refresh();
54880         if(this.grid.autoSizeColumns){
54881             this.autoSizeColumns();
54882         }
54883     },
54884
54885     beforeInitialResize : function(){
54886
54887     },
54888
54889     onColumnSplitterMoved : function(i, w){
54890         this.userResized = true;
54891         var cm = this.grid.colModel;
54892         cm.setColumnWidth(i, w, true);
54893         var cid = cm.getColumnId(i);
54894         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54895         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54896         this.updateSplitters();
54897         this.layout();
54898         this.grid.fireEvent("columnresize", i, w);
54899     },
54900
54901     syncRowHeights : function(startIndex, endIndex){
54902         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54903             startIndex = startIndex || 0;
54904             var mrows = this.getBodyTable().rows;
54905             var lrows = this.getLockedTable().rows;
54906             var len = mrows.length-1;
54907             endIndex = Math.min(endIndex || len, len);
54908             for(var i = startIndex; i <= endIndex; i++){
54909                 var m = mrows[i], l = lrows[i];
54910                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54911                 m.style.height = l.style.height = h + "px";
54912             }
54913         }
54914     },
54915
54916     layout : function(initialRender, is2ndPass){
54917         var g = this.grid;
54918         var auto = g.autoHeight;
54919         var scrollOffset = 16;
54920         var c = g.getGridEl(), cm = this.cm,
54921                 expandCol = g.autoExpandColumn,
54922                 gv = this;
54923         //c.beginMeasure();
54924
54925         if(!c.dom.offsetWidth){ // display:none?
54926             if(initialRender){
54927                 this.lockedWrap.show();
54928                 this.mainWrap.show();
54929             }
54930             return;
54931         }
54932
54933         var hasLock = this.cm.isLocked(0);
54934
54935         var tbh = this.headerPanel.getHeight();
54936         var bbh = this.footerPanel.getHeight();
54937
54938         if(auto){
54939             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54940             var newHeight = ch + c.getBorderWidth("tb");
54941             if(g.maxHeight){
54942                 newHeight = Math.min(g.maxHeight, newHeight);
54943             }
54944             c.setHeight(newHeight);
54945         }
54946
54947         if(g.autoWidth){
54948             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54949         }
54950
54951         var s = this.scroller;
54952
54953         var csize = c.getSize(true);
54954
54955         this.el.setSize(csize.width, csize.height);
54956
54957         this.headerPanel.setWidth(csize.width);
54958         this.footerPanel.setWidth(csize.width);
54959
54960         var hdHeight = this.mainHd.getHeight();
54961         var vw = csize.width;
54962         var vh = csize.height - (tbh + bbh);
54963
54964         s.setSize(vw, vh);
54965
54966         var bt = this.getBodyTable();
54967         var ltWidth = hasLock ?
54968                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54969
54970         var scrollHeight = bt.offsetHeight;
54971         var scrollWidth = ltWidth + bt.offsetWidth;
54972         var vscroll = false, hscroll = false;
54973
54974         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54975
54976         var lw = this.lockedWrap, mw = this.mainWrap;
54977         var lb = this.lockedBody, mb = this.mainBody;
54978
54979         setTimeout(function(){
54980             var t = s.dom.offsetTop;
54981             var w = s.dom.clientWidth,
54982                 h = s.dom.clientHeight;
54983
54984             lw.setTop(t);
54985             lw.setSize(ltWidth, h);
54986
54987             mw.setLeftTop(ltWidth, t);
54988             mw.setSize(w-ltWidth, h);
54989
54990             lb.setHeight(h-hdHeight);
54991             mb.setHeight(h-hdHeight);
54992
54993             if(is2ndPass !== true && !gv.userResized && expandCol){
54994                 // high speed resize without full column calculation
54995                 
54996                 var ci = cm.getIndexById(expandCol);
54997                 if (ci < 0) {
54998                     ci = cm.findColumnIndex(expandCol);
54999                 }
55000                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55001                 var expandId = cm.getColumnId(ci);
55002                 var  tw = cm.getTotalWidth(false);
55003                 var currentWidth = cm.getColumnWidth(ci);
55004                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55005                 if(currentWidth != cw){
55006                     cm.setColumnWidth(ci, cw, true);
55007                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55008                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55009                     gv.updateSplitters();
55010                     gv.layout(false, true);
55011                 }
55012             }
55013
55014             if(initialRender){
55015                 lw.show();
55016                 mw.show();
55017             }
55018             //c.endMeasure();
55019         }, 10);
55020     },
55021
55022     onWindowResize : function(){
55023         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55024             return;
55025         }
55026         this.layout();
55027     },
55028
55029     appendFooter : function(parentEl){
55030         return null;
55031     },
55032
55033     sortAscText : "Sort Ascending",
55034     sortDescText : "Sort Descending",
55035     lockText : "Lock Column",
55036     unlockText : "Unlock Column",
55037     columnsText : "Columns",
55038  
55039     columnsWiderText : "Wider",
55040     columnsNarrowText : "Thinner"
55041 });
55042
55043
55044 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55045     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55046     this.proxy.el.addClass('x-grid3-col-dd');
55047 };
55048
55049 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55050     handleMouseDown : function(e){
55051
55052     },
55053
55054     callHandleMouseDown : function(e){
55055         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55056     }
55057 });
55058 /*
55059  * Based on:
55060  * Ext JS Library 1.1.1
55061  * Copyright(c) 2006-2007, Ext JS, LLC.
55062  *
55063  * Originally Released Under LGPL - original licence link has changed is not relivant.
55064  *
55065  * Fork - LGPL
55066  * <script type="text/javascript">
55067  */
55068  
55069 // private
55070 // This is a support class used internally by the Grid components
55071 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55072     this.grid = grid;
55073     this.view = grid.getView();
55074     this.proxy = this.view.resizeProxy;
55075     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55076         "gridSplitters" + this.grid.getGridEl().id, {
55077         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55078     });
55079     this.setHandleElId(Roo.id(hd));
55080     this.setOuterHandleElId(Roo.id(hd2));
55081     this.scroll = false;
55082 };
55083 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55084     fly: Roo.Element.fly,
55085
55086     b4StartDrag : function(x, y){
55087         this.view.headersDisabled = true;
55088         this.proxy.setHeight(this.view.mainWrap.getHeight());
55089         var w = this.cm.getColumnWidth(this.cellIndex);
55090         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55091         this.resetConstraints();
55092         this.setXConstraint(minw, 1000);
55093         this.setYConstraint(0, 0);
55094         this.minX = x - minw;
55095         this.maxX = x + 1000;
55096         this.startPos = x;
55097         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55098     },
55099
55100
55101     handleMouseDown : function(e){
55102         ev = Roo.EventObject.setEvent(e);
55103         var t = this.fly(ev.getTarget());
55104         if(t.hasClass("x-grid-split")){
55105             this.cellIndex = this.view.getCellIndex(t.dom);
55106             this.split = t.dom;
55107             this.cm = this.grid.colModel;
55108             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55109                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55110             }
55111         }
55112     },
55113
55114     endDrag : function(e){
55115         this.view.headersDisabled = false;
55116         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55117         var diff = endX - this.startPos;
55118         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55119     },
55120
55121     autoOffset : function(){
55122         this.setDelta(0,0);
55123     }
55124 });/*
55125  * Based on:
55126  * Ext JS Library 1.1.1
55127  * Copyright(c) 2006-2007, Ext JS, LLC.
55128  *
55129  * Originally Released Under LGPL - original licence link has changed is not relivant.
55130  *
55131  * Fork - LGPL
55132  * <script type="text/javascript">
55133  */
55134  
55135 // private
55136 // This is a support class used internally by the Grid components
55137 Roo.grid.GridDragZone = function(grid, config){
55138     this.view = grid.getView();
55139     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55140     if(this.view.lockedBody){
55141         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55142         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55143     }
55144     this.scroll = false;
55145     this.grid = grid;
55146     this.ddel = document.createElement('div');
55147     this.ddel.className = 'x-grid-dd-wrap';
55148 };
55149
55150 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55151     ddGroup : "GridDD",
55152
55153     getDragData : function(e){
55154         var t = Roo.lib.Event.getTarget(e);
55155         var rowIndex = this.view.findRowIndex(t);
55156         var sm = this.grid.selModel;
55157             
55158         //Roo.log(rowIndex);
55159         
55160         if (sm.getSelectedCell) {
55161             // cell selection..
55162             if (!sm.getSelectedCell()) {
55163                 return false;
55164             }
55165             if (rowIndex != sm.getSelectedCell()[0]) {
55166                 return false;
55167             }
55168         
55169         }
55170         
55171         if(rowIndex !== false){
55172             
55173             // if editorgrid.. 
55174             
55175             
55176             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55177                
55178             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55179               //  
55180             //}
55181             if (e.hasModifier()){
55182                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55183             }
55184             
55185             Roo.log("getDragData");
55186             
55187             return {
55188                 grid: this.grid,
55189                 ddel: this.ddel,
55190                 rowIndex: rowIndex,
55191                 selections:sm.getSelections ? sm.getSelections() : (
55192                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55193                 )
55194             };
55195         }
55196         return false;
55197     },
55198
55199     onInitDrag : function(e){
55200         var data = this.dragData;
55201         this.ddel.innerHTML = this.grid.getDragDropText();
55202         this.proxy.update(this.ddel);
55203         // fire start drag?
55204     },
55205
55206     afterRepair : function(){
55207         this.dragging = false;
55208     },
55209
55210     getRepairXY : function(e, data){
55211         return false;
55212     },
55213
55214     onEndDrag : function(data, e){
55215         // fire end drag?
55216     },
55217
55218     onValidDrop : function(dd, e, id){
55219         // fire drag drop?
55220         this.hideProxy();
55221     },
55222
55223     beforeInvalidDrop : function(e, id){
55224
55225     }
55226 });/*
55227  * Based on:
55228  * Ext JS Library 1.1.1
55229  * Copyright(c) 2006-2007, Ext JS, LLC.
55230  *
55231  * Originally Released Under LGPL - original licence link has changed is not relivant.
55232  *
55233  * Fork - LGPL
55234  * <script type="text/javascript">
55235  */
55236  
55237
55238 /**
55239  * @class Roo.grid.ColumnModel
55240  * @extends Roo.util.Observable
55241  * This is the default implementation of a ColumnModel used by the Grid. It defines
55242  * the columns in the grid.
55243  * <br>Usage:<br>
55244  <pre><code>
55245  var colModel = new Roo.grid.ColumnModel([
55246         {header: "Ticker", width: 60, sortable: true, locked: true},
55247         {header: "Company Name", width: 150, sortable: true},
55248         {header: "Market Cap.", width: 100, sortable: true},
55249         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55250         {header: "Employees", width: 100, sortable: true, resizable: false}
55251  ]);
55252  </code></pre>
55253  * <p>
55254  
55255  * The config options listed for this class are options which may appear in each
55256  * individual column definition.
55257  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55258  * @constructor
55259  * @param {Object} config An Array of column config objects. See this class's
55260  * config objects for details.
55261 */
55262 Roo.grid.ColumnModel = function(config){
55263         /**
55264      * The config passed into the constructor
55265      */
55266     this.config = config;
55267     this.lookup = {};
55268
55269     // if no id, create one
55270     // if the column does not have a dataIndex mapping,
55271     // map it to the order it is in the config
55272     for(var i = 0, len = config.length; i < len; i++){
55273         var c = config[i];
55274         if(typeof c.dataIndex == "undefined"){
55275             c.dataIndex = i;
55276         }
55277         if(typeof c.renderer == "string"){
55278             c.renderer = Roo.util.Format[c.renderer];
55279         }
55280         if(typeof c.id == "undefined"){
55281             c.id = Roo.id();
55282         }
55283         if(c.editor && c.editor.xtype){
55284             c.editor  = Roo.factory(c.editor, Roo.grid);
55285         }
55286         if(c.editor && c.editor.isFormField){
55287             c.editor = new Roo.grid.GridEditor(c.editor);
55288         }
55289         this.lookup[c.id] = c;
55290     }
55291
55292     /**
55293      * The width of columns which have no width specified (defaults to 100)
55294      * @type Number
55295      */
55296     this.defaultWidth = 100;
55297
55298     /**
55299      * Default sortable of columns which have no sortable specified (defaults to false)
55300      * @type Boolean
55301      */
55302     this.defaultSortable = false;
55303
55304     this.addEvents({
55305         /**
55306              * @event widthchange
55307              * Fires when the width of a column changes.
55308              * @param {ColumnModel} this
55309              * @param {Number} columnIndex The column index
55310              * @param {Number} newWidth The new width
55311              */
55312             "widthchange": true,
55313         /**
55314              * @event headerchange
55315              * Fires when the text of a header changes.
55316              * @param {ColumnModel} this
55317              * @param {Number} columnIndex The column index
55318              * @param {Number} newText The new header text
55319              */
55320             "headerchange": true,
55321         /**
55322              * @event hiddenchange
55323              * Fires when a column is hidden or "unhidden".
55324              * @param {ColumnModel} this
55325              * @param {Number} columnIndex The column index
55326              * @param {Boolean} hidden true if hidden, false otherwise
55327              */
55328             "hiddenchange": true,
55329             /**
55330          * @event columnmoved
55331          * Fires when a column is moved.
55332          * @param {ColumnModel} this
55333          * @param {Number} oldIndex
55334          * @param {Number} newIndex
55335          */
55336         "columnmoved" : true,
55337         /**
55338          * @event columlockchange
55339          * Fires when a column's locked state is changed
55340          * @param {ColumnModel} this
55341          * @param {Number} colIndex
55342          * @param {Boolean} locked true if locked
55343          */
55344         "columnlockchange" : true
55345     });
55346     Roo.grid.ColumnModel.superclass.constructor.call(this);
55347 };
55348 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55349     /**
55350      * @cfg {String} header The header text to display in the Grid view.
55351      */
55352     /**
55353      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55354      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55355      * specified, the column's index is used as an index into the Record's data Array.
55356      */
55357     /**
55358      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55359      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55360      */
55361     /**
55362      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55363      * Defaults to the value of the {@link #defaultSortable} property.
55364      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55365      */
55366     /**
55367      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55368      */
55369     /**
55370      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55371      */
55372     /**
55373      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55374      */
55375     /**
55376      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55377      */
55378     /**
55379      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55380      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55381      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55382      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55383      */
55384        /**
55385      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55386      */
55387     /**
55388      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55389      */
55390     /**
55391      * @cfg {String} cursor (Optional)
55392      */
55393     /**
55394      * @cfg {String} tooltip (Optional)
55395      */
55396     /**
55397      * Returns the id of the column at the specified index.
55398      * @param {Number} index The column index
55399      * @return {String} the id
55400      */
55401     getColumnId : function(index){
55402         return this.config[index].id;
55403     },
55404
55405     /**
55406      * Returns the column for a specified id.
55407      * @param {String} id The column id
55408      * @return {Object} the column
55409      */
55410     getColumnById : function(id){
55411         return this.lookup[id];
55412     },
55413
55414     
55415     /**
55416      * Returns the column for a specified dataIndex.
55417      * @param {String} dataIndex The column dataIndex
55418      * @return {Object|Boolean} the column or false if not found
55419      */
55420     getColumnByDataIndex: function(dataIndex){
55421         var index = this.findColumnIndex(dataIndex);
55422         return index > -1 ? this.config[index] : false;
55423     },
55424     
55425     /**
55426      * Returns the index for a specified column id.
55427      * @param {String} id The column id
55428      * @return {Number} the index, or -1 if not found
55429      */
55430     getIndexById : function(id){
55431         for(var i = 0, len = this.config.length; i < len; i++){
55432             if(this.config[i].id == id){
55433                 return i;
55434             }
55435         }
55436         return -1;
55437     },
55438     
55439     /**
55440      * Returns the index for a specified column dataIndex.
55441      * @param {String} dataIndex The column dataIndex
55442      * @return {Number} the index, or -1 if not found
55443      */
55444     
55445     findColumnIndex : function(dataIndex){
55446         for(var i = 0, len = this.config.length; i < len; i++){
55447             if(this.config[i].dataIndex == dataIndex){
55448                 return i;
55449             }
55450         }
55451         return -1;
55452     },
55453     
55454     
55455     moveColumn : function(oldIndex, newIndex){
55456         var c = this.config[oldIndex];
55457         this.config.splice(oldIndex, 1);
55458         this.config.splice(newIndex, 0, c);
55459         this.dataMap = null;
55460         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55461     },
55462
55463     isLocked : function(colIndex){
55464         return this.config[colIndex].locked === true;
55465     },
55466
55467     setLocked : function(colIndex, value, suppressEvent){
55468         if(this.isLocked(colIndex) == value){
55469             return;
55470         }
55471         this.config[colIndex].locked = value;
55472         if(!suppressEvent){
55473             this.fireEvent("columnlockchange", this, colIndex, value);
55474         }
55475     },
55476
55477     getTotalLockedWidth : function(){
55478         var totalWidth = 0;
55479         for(var i = 0; i < this.config.length; i++){
55480             if(this.isLocked(i) && !this.isHidden(i)){
55481                 this.totalWidth += this.getColumnWidth(i);
55482             }
55483         }
55484         return totalWidth;
55485     },
55486
55487     getLockedCount : function(){
55488         for(var i = 0, len = this.config.length; i < len; i++){
55489             if(!this.isLocked(i)){
55490                 return i;
55491             }
55492         }
55493     },
55494
55495     /**
55496      * Returns the number of columns.
55497      * @return {Number}
55498      */
55499     getColumnCount : function(visibleOnly){
55500         if(visibleOnly === true){
55501             var c = 0;
55502             for(var i = 0, len = this.config.length; i < len; i++){
55503                 if(!this.isHidden(i)){
55504                     c++;
55505                 }
55506             }
55507             return c;
55508         }
55509         return this.config.length;
55510     },
55511
55512     /**
55513      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55514      * @param {Function} fn
55515      * @param {Object} scope (optional)
55516      * @return {Array} result
55517      */
55518     getColumnsBy : function(fn, scope){
55519         var r = [];
55520         for(var i = 0, len = this.config.length; i < len; i++){
55521             var c = this.config[i];
55522             if(fn.call(scope||this, c, i) === true){
55523                 r[r.length] = c;
55524             }
55525         }
55526         return r;
55527     },
55528
55529     /**
55530      * Returns true if the specified column is sortable.
55531      * @param {Number} col The column index
55532      * @return {Boolean}
55533      */
55534     isSortable : function(col){
55535         if(typeof this.config[col].sortable == "undefined"){
55536             return this.defaultSortable;
55537         }
55538         return this.config[col].sortable;
55539     },
55540
55541     /**
55542      * Returns the rendering (formatting) function defined for the column.
55543      * @param {Number} col The column index.
55544      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55545      */
55546     getRenderer : function(col){
55547         if(!this.config[col].renderer){
55548             return Roo.grid.ColumnModel.defaultRenderer;
55549         }
55550         return this.config[col].renderer;
55551     },
55552
55553     /**
55554      * Sets the rendering (formatting) function for a column.
55555      * @param {Number} col The column index
55556      * @param {Function} fn The function to use to process the cell's raw data
55557      * to return HTML markup for the grid view. The render function is called with
55558      * the following parameters:<ul>
55559      * <li>Data value.</li>
55560      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55561      * <li>css A CSS style string to apply to the table cell.</li>
55562      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55563      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55564      * <li>Row index</li>
55565      * <li>Column index</li>
55566      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55567      */
55568     setRenderer : function(col, fn){
55569         this.config[col].renderer = fn;
55570     },
55571
55572     /**
55573      * Returns the width for the specified column.
55574      * @param {Number} col The column index
55575      * @return {Number}
55576      */
55577     getColumnWidth : function(col){
55578         return this.config[col].width * 1 || this.defaultWidth;
55579     },
55580
55581     /**
55582      * Sets the width for a column.
55583      * @param {Number} col The column index
55584      * @param {Number} width The new width
55585      */
55586     setColumnWidth : function(col, width, suppressEvent){
55587         this.config[col].width = width;
55588         this.totalWidth = null;
55589         if(!suppressEvent){
55590              this.fireEvent("widthchange", this, col, width);
55591         }
55592     },
55593
55594     /**
55595      * Returns the total width of all columns.
55596      * @param {Boolean} includeHidden True to include hidden column widths
55597      * @return {Number}
55598      */
55599     getTotalWidth : function(includeHidden){
55600         if(!this.totalWidth){
55601             this.totalWidth = 0;
55602             for(var i = 0, len = this.config.length; i < len; i++){
55603                 if(includeHidden || !this.isHidden(i)){
55604                     this.totalWidth += this.getColumnWidth(i);
55605                 }
55606             }
55607         }
55608         return this.totalWidth;
55609     },
55610
55611     /**
55612      * Returns the header for the specified column.
55613      * @param {Number} col The column index
55614      * @return {String}
55615      */
55616     getColumnHeader : function(col){
55617         return this.config[col].header;
55618     },
55619
55620     /**
55621      * Sets the header for a column.
55622      * @param {Number} col The column index
55623      * @param {String} header The new header
55624      */
55625     setColumnHeader : function(col, header){
55626         this.config[col].header = header;
55627         this.fireEvent("headerchange", this, col, header);
55628     },
55629
55630     /**
55631      * Returns the tooltip for the specified column.
55632      * @param {Number} col The column index
55633      * @return {String}
55634      */
55635     getColumnTooltip : function(col){
55636             return this.config[col].tooltip;
55637     },
55638     /**
55639      * Sets the tooltip for a column.
55640      * @param {Number} col The column index
55641      * @param {String} tooltip The new tooltip
55642      */
55643     setColumnTooltip : function(col, tooltip){
55644             this.config[col].tooltip = tooltip;
55645     },
55646
55647     /**
55648      * Returns the dataIndex for the specified column.
55649      * @param {Number} col The column index
55650      * @return {Number}
55651      */
55652     getDataIndex : function(col){
55653         return this.config[col].dataIndex;
55654     },
55655
55656     /**
55657      * Sets the dataIndex for a column.
55658      * @param {Number} col The column index
55659      * @param {Number} dataIndex The new dataIndex
55660      */
55661     setDataIndex : function(col, dataIndex){
55662         this.config[col].dataIndex = dataIndex;
55663     },
55664
55665     
55666     
55667     /**
55668      * Returns true if the cell is editable.
55669      * @param {Number} colIndex The column index
55670      * @param {Number} rowIndex The row index
55671      * @return {Boolean}
55672      */
55673     isCellEditable : function(colIndex, rowIndex){
55674         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55675     },
55676
55677     /**
55678      * Returns the editor defined for the cell/column.
55679      * return false or null to disable editing.
55680      * @param {Number} colIndex The column index
55681      * @param {Number} rowIndex The row index
55682      * @return {Object}
55683      */
55684     getCellEditor : function(colIndex, rowIndex){
55685         return this.config[colIndex].editor;
55686     },
55687
55688     /**
55689      * Sets if a column is editable.
55690      * @param {Number} col The column index
55691      * @param {Boolean} editable True if the column is editable
55692      */
55693     setEditable : function(col, editable){
55694         this.config[col].editable = editable;
55695     },
55696
55697
55698     /**
55699      * Returns true if the column is hidden.
55700      * @param {Number} colIndex The column index
55701      * @return {Boolean}
55702      */
55703     isHidden : function(colIndex){
55704         return this.config[colIndex].hidden;
55705     },
55706
55707
55708     /**
55709      * Returns true if the column width cannot be changed
55710      */
55711     isFixed : function(colIndex){
55712         return this.config[colIndex].fixed;
55713     },
55714
55715     /**
55716      * Returns true if the column can be resized
55717      * @return {Boolean}
55718      */
55719     isResizable : function(colIndex){
55720         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55721     },
55722     /**
55723      * Sets if a column is hidden.
55724      * @param {Number} colIndex The column index
55725      * @param {Boolean} hidden True if the column is hidden
55726      */
55727     setHidden : function(colIndex, hidden){
55728         this.config[colIndex].hidden = hidden;
55729         this.totalWidth = null;
55730         this.fireEvent("hiddenchange", this, colIndex, hidden);
55731     },
55732
55733     /**
55734      * Sets the editor for a column.
55735      * @param {Number} col The column index
55736      * @param {Object} editor The editor object
55737      */
55738     setEditor : function(col, editor){
55739         this.config[col].editor = editor;
55740     }
55741 });
55742
55743 Roo.grid.ColumnModel.defaultRenderer = function(value){
55744         if(typeof value == "string" && value.length < 1){
55745             return "&#160;";
55746         }
55747         return value;
55748 };
55749
55750 // Alias for backwards compatibility
55751 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55752 /*
55753  * Based on:
55754  * Ext JS Library 1.1.1
55755  * Copyright(c) 2006-2007, Ext JS, LLC.
55756  *
55757  * Originally Released Under LGPL - original licence link has changed is not relivant.
55758  *
55759  * Fork - LGPL
55760  * <script type="text/javascript">
55761  */
55762
55763 /**
55764  * @class Roo.grid.AbstractSelectionModel
55765  * @extends Roo.util.Observable
55766  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55767  * implemented by descendant classes.  This class should not be directly instantiated.
55768  * @constructor
55769  */
55770 Roo.grid.AbstractSelectionModel = function(){
55771     this.locked = false;
55772     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55773 };
55774
55775 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55776     /** @ignore Called by the grid automatically. Do not call directly. */
55777     init : function(grid){
55778         this.grid = grid;
55779         this.initEvents();
55780     },
55781
55782     /**
55783      * Locks the selections.
55784      */
55785     lock : function(){
55786         this.locked = true;
55787     },
55788
55789     /**
55790      * Unlocks the selections.
55791      */
55792     unlock : function(){
55793         this.locked = false;
55794     },
55795
55796     /**
55797      * Returns true if the selections are locked.
55798      * @return {Boolean}
55799      */
55800     isLocked : function(){
55801         return this.locked;
55802     }
55803 });/*
55804  * Based on:
55805  * Ext JS Library 1.1.1
55806  * Copyright(c) 2006-2007, Ext JS, LLC.
55807  *
55808  * Originally Released Under LGPL - original licence link has changed is not relivant.
55809  *
55810  * Fork - LGPL
55811  * <script type="text/javascript">
55812  */
55813 /**
55814  * @extends Roo.grid.AbstractSelectionModel
55815  * @class Roo.grid.RowSelectionModel
55816  * The default SelectionModel used by {@link Roo.grid.Grid}.
55817  * It supports multiple selections and keyboard selection/navigation. 
55818  * @constructor
55819  * @param {Object} config
55820  */
55821 Roo.grid.RowSelectionModel = function(config){
55822     Roo.apply(this, config);
55823     this.selections = new Roo.util.MixedCollection(false, function(o){
55824         return o.id;
55825     });
55826
55827     this.last = false;
55828     this.lastActive = false;
55829
55830     this.addEvents({
55831         /**
55832              * @event selectionchange
55833              * Fires when the selection changes
55834              * @param {SelectionModel} this
55835              */
55836             "selectionchange" : true,
55837         /**
55838              * @event afterselectionchange
55839              * Fires after the selection changes (eg. by key press or clicking)
55840              * @param {SelectionModel} this
55841              */
55842             "afterselectionchange" : true,
55843         /**
55844              * @event beforerowselect
55845              * Fires when a row is selected being selected, return false to cancel.
55846              * @param {SelectionModel} this
55847              * @param {Number} rowIndex The selected index
55848              * @param {Boolean} keepExisting False if other selections will be cleared
55849              */
55850             "beforerowselect" : true,
55851         /**
55852              * @event rowselect
55853              * Fires when a row is selected.
55854              * @param {SelectionModel} this
55855              * @param {Number} rowIndex The selected index
55856              * @param {Roo.data.Record} r The record
55857              */
55858             "rowselect" : true,
55859         /**
55860              * @event rowdeselect
55861              * Fires when a row is deselected.
55862              * @param {SelectionModel} this
55863              * @param {Number} rowIndex The selected index
55864              */
55865         "rowdeselect" : true
55866     });
55867     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55868     this.locked = false;
55869 };
55870
55871 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55872     /**
55873      * @cfg {Boolean} singleSelect
55874      * True to allow selection of only one row at a time (defaults to false)
55875      */
55876     singleSelect : false,
55877
55878     // private
55879     initEvents : function(){
55880
55881         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55882             this.grid.on("mousedown", this.handleMouseDown, this);
55883         }else{ // allow click to work like normal
55884             this.grid.on("rowclick", this.handleDragableRowClick, this);
55885         }
55886
55887         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55888             "up" : function(e){
55889                 if(!e.shiftKey){
55890                     this.selectPrevious(e.shiftKey);
55891                 }else if(this.last !== false && this.lastActive !== false){
55892                     var last = this.last;
55893                     this.selectRange(this.last,  this.lastActive-1);
55894                     this.grid.getView().focusRow(this.lastActive);
55895                     if(last !== false){
55896                         this.last = last;
55897                     }
55898                 }else{
55899                     this.selectFirstRow();
55900                 }
55901                 this.fireEvent("afterselectionchange", this);
55902             },
55903             "down" : function(e){
55904                 if(!e.shiftKey){
55905                     this.selectNext(e.shiftKey);
55906                 }else if(this.last !== false && this.lastActive !== false){
55907                     var last = this.last;
55908                     this.selectRange(this.last,  this.lastActive+1);
55909                     this.grid.getView().focusRow(this.lastActive);
55910                     if(last !== false){
55911                         this.last = last;
55912                     }
55913                 }else{
55914                     this.selectFirstRow();
55915                 }
55916                 this.fireEvent("afterselectionchange", this);
55917             },
55918             scope: this
55919         });
55920
55921         var view = this.grid.view;
55922         view.on("refresh", this.onRefresh, this);
55923         view.on("rowupdated", this.onRowUpdated, this);
55924         view.on("rowremoved", this.onRemove, this);
55925     },
55926
55927     // private
55928     onRefresh : function(){
55929         var ds = this.grid.dataSource, i, v = this.grid.view;
55930         var s = this.selections;
55931         s.each(function(r){
55932             if((i = ds.indexOfId(r.id)) != -1){
55933                 v.onRowSelect(i);
55934                 s.add(ds.getAt(i)); // updating the selection relate data
55935             }else{
55936                 s.remove(r);
55937             }
55938         });
55939     },
55940
55941     // private
55942     onRemove : function(v, index, r){
55943         this.selections.remove(r);
55944     },
55945
55946     // private
55947     onRowUpdated : function(v, index, r){
55948         if(this.isSelected(r)){
55949             v.onRowSelect(index);
55950         }
55951     },
55952
55953     /**
55954      * Select records.
55955      * @param {Array} records The records to select
55956      * @param {Boolean} keepExisting (optional) True to keep existing selections
55957      */
55958     selectRecords : function(records, keepExisting){
55959         if(!keepExisting){
55960             this.clearSelections();
55961         }
55962         var ds = this.grid.dataSource;
55963         for(var i = 0, len = records.length; i < len; i++){
55964             this.selectRow(ds.indexOf(records[i]), true);
55965         }
55966     },
55967
55968     /**
55969      * Gets the number of selected rows.
55970      * @return {Number}
55971      */
55972     getCount : function(){
55973         return this.selections.length;
55974     },
55975
55976     /**
55977      * Selects the first row in the grid.
55978      */
55979     selectFirstRow : function(){
55980         this.selectRow(0);
55981     },
55982
55983     /**
55984      * Select the last row.
55985      * @param {Boolean} keepExisting (optional) True to keep existing selections
55986      */
55987     selectLastRow : function(keepExisting){
55988         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55989     },
55990
55991     /**
55992      * Selects the row immediately following the last selected row.
55993      * @param {Boolean} keepExisting (optional) True to keep existing selections
55994      */
55995     selectNext : function(keepExisting){
55996         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55997             this.selectRow(this.last+1, keepExisting);
55998             this.grid.getView().focusRow(this.last);
55999         }
56000     },
56001
56002     /**
56003      * Selects the row that precedes the last selected row.
56004      * @param {Boolean} keepExisting (optional) True to keep existing selections
56005      */
56006     selectPrevious : function(keepExisting){
56007         if(this.last){
56008             this.selectRow(this.last-1, keepExisting);
56009             this.grid.getView().focusRow(this.last);
56010         }
56011     },
56012
56013     /**
56014      * Returns the selected records
56015      * @return {Array} Array of selected records
56016      */
56017     getSelections : function(){
56018         return [].concat(this.selections.items);
56019     },
56020
56021     /**
56022      * Returns the first selected record.
56023      * @return {Record}
56024      */
56025     getSelected : function(){
56026         return this.selections.itemAt(0);
56027     },
56028
56029
56030     /**
56031      * Clears all selections.
56032      */
56033     clearSelections : function(fast){
56034         if(this.locked) return;
56035         if(fast !== true){
56036             var ds = this.grid.dataSource;
56037             var s = this.selections;
56038             s.each(function(r){
56039                 this.deselectRow(ds.indexOfId(r.id));
56040             }, this);
56041             s.clear();
56042         }else{
56043             this.selections.clear();
56044         }
56045         this.last = false;
56046     },
56047
56048
56049     /**
56050      * Selects all rows.
56051      */
56052     selectAll : function(){
56053         if(this.locked) return;
56054         this.selections.clear();
56055         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56056             this.selectRow(i, true);
56057         }
56058     },
56059
56060     /**
56061      * Returns True if there is a selection.
56062      * @return {Boolean}
56063      */
56064     hasSelection : function(){
56065         return this.selections.length > 0;
56066     },
56067
56068     /**
56069      * Returns True if the specified row is selected.
56070      * @param {Number/Record} record The record or index of the record to check
56071      * @return {Boolean}
56072      */
56073     isSelected : function(index){
56074         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56075         return (r && this.selections.key(r.id) ? true : false);
56076     },
56077
56078     /**
56079      * Returns True if the specified record id is selected.
56080      * @param {String} id The id of record to check
56081      * @return {Boolean}
56082      */
56083     isIdSelected : function(id){
56084         return (this.selections.key(id) ? true : false);
56085     },
56086
56087     // private
56088     handleMouseDown : function(e, t){
56089         var view = this.grid.getView(), rowIndex;
56090         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56091             return;
56092         };
56093         if(e.shiftKey && this.last !== false){
56094             var last = this.last;
56095             this.selectRange(last, rowIndex, e.ctrlKey);
56096             this.last = last; // reset the last
56097             view.focusRow(rowIndex);
56098         }else{
56099             var isSelected = this.isSelected(rowIndex);
56100             if(e.button !== 0 && isSelected){
56101                 view.focusRow(rowIndex);
56102             }else if(e.ctrlKey && isSelected){
56103                 this.deselectRow(rowIndex);
56104             }else if(!isSelected){
56105                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56106                 view.focusRow(rowIndex);
56107             }
56108         }
56109         this.fireEvent("afterselectionchange", this);
56110     },
56111     // private
56112     handleDragableRowClick :  function(grid, rowIndex, e) 
56113     {
56114         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56115             this.selectRow(rowIndex, false);
56116             grid.view.focusRow(rowIndex);
56117              this.fireEvent("afterselectionchange", this);
56118         }
56119     },
56120     
56121     /**
56122      * Selects multiple rows.
56123      * @param {Array} rows Array of the indexes of the row to select
56124      * @param {Boolean} keepExisting (optional) True to keep existing selections
56125      */
56126     selectRows : function(rows, keepExisting){
56127         if(!keepExisting){
56128             this.clearSelections();
56129         }
56130         for(var i = 0, len = rows.length; i < len; i++){
56131             this.selectRow(rows[i], true);
56132         }
56133     },
56134
56135     /**
56136      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56137      * @param {Number} startRow The index of the first row in the range
56138      * @param {Number} endRow The index of the last row in the range
56139      * @param {Boolean} keepExisting (optional) True to retain existing selections
56140      */
56141     selectRange : function(startRow, endRow, keepExisting){
56142         if(this.locked) return;
56143         if(!keepExisting){
56144             this.clearSelections();
56145         }
56146         if(startRow <= endRow){
56147             for(var i = startRow; i <= endRow; i++){
56148                 this.selectRow(i, true);
56149             }
56150         }else{
56151             for(var i = startRow; i >= endRow; i--){
56152                 this.selectRow(i, true);
56153             }
56154         }
56155     },
56156
56157     /**
56158      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56159      * @param {Number} startRow The index of the first row in the range
56160      * @param {Number} endRow The index of the last row in the range
56161      */
56162     deselectRange : function(startRow, endRow, preventViewNotify){
56163         if(this.locked) return;
56164         for(var i = startRow; i <= endRow; i++){
56165             this.deselectRow(i, preventViewNotify);
56166         }
56167     },
56168
56169     /**
56170      * Selects a row.
56171      * @param {Number} row The index of the row to select
56172      * @param {Boolean} keepExisting (optional) True to keep existing selections
56173      */
56174     selectRow : function(index, keepExisting, preventViewNotify){
56175         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56176         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56177             if(!keepExisting || this.singleSelect){
56178                 this.clearSelections();
56179             }
56180             var r = this.grid.dataSource.getAt(index);
56181             this.selections.add(r);
56182             this.last = this.lastActive = index;
56183             if(!preventViewNotify){
56184                 this.grid.getView().onRowSelect(index);
56185             }
56186             this.fireEvent("rowselect", this, index, r);
56187             this.fireEvent("selectionchange", this);
56188         }
56189     },
56190
56191     /**
56192      * Deselects a row.
56193      * @param {Number} row The index of the row to deselect
56194      */
56195     deselectRow : function(index, preventViewNotify){
56196         if(this.locked) return;
56197         if(this.last == index){
56198             this.last = false;
56199         }
56200         if(this.lastActive == index){
56201             this.lastActive = false;
56202         }
56203         var r = this.grid.dataSource.getAt(index);
56204         this.selections.remove(r);
56205         if(!preventViewNotify){
56206             this.grid.getView().onRowDeselect(index);
56207         }
56208         this.fireEvent("rowdeselect", this, index);
56209         this.fireEvent("selectionchange", this);
56210     },
56211
56212     // private
56213     restoreLast : function(){
56214         if(this._last){
56215             this.last = this._last;
56216         }
56217     },
56218
56219     // private
56220     acceptsNav : function(row, col, cm){
56221         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56222     },
56223
56224     // private
56225     onEditorKey : function(field, e){
56226         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56227         if(k == e.TAB){
56228             e.stopEvent();
56229             ed.completeEdit();
56230             if(e.shiftKey){
56231                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56232             }else{
56233                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56234             }
56235         }else if(k == e.ENTER && !e.ctrlKey){
56236             e.stopEvent();
56237             ed.completeEdit();
56238             if(e.shiftKey){
56239                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56240             }else{
56241                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56242             }
56243         }else if(k == e.ESC){
56244             ed.cancelEdit();
56245         }
56246         if(newCell){
56247             g.startEditing(newCell[0], newCell[1]);
56248         }
56249     }
56250 });/*
56251  * Based on:
56252  * Ext JS Library 1.1.1
56253  * Copyright(c) 2006-2007, Ext JS, LLC.
56254  *
56255  * Originally Released Under LGPL - original licence link has changed is not relivant.
56256  *
56257  * Fork - LGPL
56258  * <script type="text/javascript">
56259  */
56260 /**
56261  * @class Roo.grid.CellSelectionModel
56262  * @extends Roo.grid.AbstractSelectionModel
56263  * This class provides the basic implementation for cell selection in a grid.
56264  * @constructor
56265  * @param {Object} config The object containing the configuration of this model.
56266  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56267  */
56268 Roo.grid.CellSelectionModel = function(config){
56269     Roo.apply(this, config);
56270
56271     this.selection = null;
56272
56273     this.addEvents({
56274         /**
56275              * @event beforerowselect
56276              * Fires before a cell is selected.
56277              * @param {SelectionModel} this
56278              * @param {Number} rowIndex The selected row index
56279              * @param {Number} colIndex The selected cell index
56280              */
56281             "beforecellselect" : true,
56282         /**
56283              * @event cellselect
56284              * Fires when a cell is selected.
56285              * @param {SelectionModel} this
56286              * @param {Number} rowIndex The selected row index
56287              * @param {Number} colIndex The selected cell index
56288              */
56289             "cellselect" : true,
56290         /**
56291              * @event selectionchange
56292              * Fires when the active selection changes.
56293              * @param {SelectionModel} this
56294              * @param {Object} selection null for no selection or an object (o) with two properties
56295                 <ul>
56296                 <li>o.record: the record object for the row the selection is in</li>
56297                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56298                 </ul>
56299              */
56300             "selectionchange" : true,
56301         /**
56302              * @event tabend
56303              * Fires when the tab (or enter) was pressed on the last editable cell
56304              * You can use this to trigger add new row.
56305              * @param {SelectionModel} this
56306              */
56307             "tabend" : true,
56308          /**
56309              * @event beforeeditnext
56310              * Fires before the next editable sell is made active
56311              * You can use this to skip to another cell or fire the tabend
56312              *    if you set cell to false
56313              * @param {Object} eventdata object : { cell : [ row, col ] } 
56314              */
56315             "beforeeditnext" : true
56316     });
56317     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56318 };
56319
56320 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56321     
56322     enter_is_tab: false,
56323
56324     /** @ignore */
56325     initEvents : function(){
56326         this.grid.on("mousedown", this.handleMouseDown, this);
56327         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56328         var view = this.grid.view;
56329         view.on("refresh", this.onViewChange, this);
56330         view.on("rowupdated", this.onRowUpdated, this);
56331         view.on("beforerowremoved", this.clearSelections, this);
56332         view.on("beforerowsinserted", this.clearSelections, this);
56333         if(this.grid.isEditor){
56334             this.grid.on("beforeedit", this.beforeEdit,  this);
56335         }
56336     },
56337
56338         //private
56339     beforeEdit : function(e){
56340         this.select(e.row, e.column, false, true, e.record);
56341     },
56342
56343         //private
56344     onRowUpdated : function(v, index, r){
56345         if(this.selection && this.selection.record == r){
56346             v.onCellSelect(index, this.selection.cell[1]);
56347         }
56348     },
56349
56350         //private
56351     onViewChange : function(){
56352         this.clearSelections(true);
56353     },
56354
56355         /**
56356          * Returns the currently selected cell,.
56357          * @return {Array} The selected cell (row, column) or null if none selected.
56358          */
56359     getSelectedCell : function(){
56360         return this.selection ? this.selection.cell : null;
56361     },
56362
56363     /**
56364      * Clears all selections.
56365      * @param {Boolean} true to prevent the gridview from being notified about the change.
56366      */
56367     clearSelections : function(preventNotify){
56368         var s = this.selection;
56369         if(s){
56370             if(preventNotify !== true){
56371                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56372             }
56373             this.selection = null;
56374             this.fireEvent("selectionchange", this, null);
56375         }
56376     },
56377
56378     /**
56379      * Returns true if there is a selection.
56380      * @return {Boolean}
56381      */
56382     hasSelection : function(){
56383         return this.selection ? true : false;
56384     },
56385
56386     /** @ignore */
56387     handleMouseDown : function(e, t){
56388         var v = this.grid.getView();
56389         if(this.isLocked()){
56390             return;
56391         };
56392         var row = v.findRowIndex(t);
56393         var cell = v.findCellIndex(t);
56394         if(row !== false && cell !== false){
56395             this.select(row, cell);
56396         }
56397     },
56398
56399     /**
56400      * Selects a cell.
56401      * @param {Number} rowIndex
56402      * @param {Number} collIndex
56403      */
56404     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56405         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56406             this.clearSelections();
56407             r = r || this.grid.dataSource.getAt(rowIndex);
56408             this.selection = {
56409                 record : r,
56410                 cell : [rowIndex, colIndex]
56411             };
56412             if(!preventViewNotify){
56413                 var v = this.grid.getView();
56414                 v.onCellSelect(rowIndex, colIndex);
56415                 if(preventFocus !== true){
56416                     v.focusCell(rowIndex, colIndex);
56417                 }
56418             }
56419             this.fireEvent("cellselect", this, rowIndex, colIndex);
56420             this.fireEvent("selectionchange", this, this.selection);
56421         }
56422     },
56423
56424         //private
56425     isSelectable : function(rowIndex, colIndex, cm){
56426         return !cm.isHidden(colIndex);
56427     },
56428
56429     /** @ignore */
56430     handleKeyDown : function(e){
56431         //Roo.log('Cell Sel Model handleKeyDown');
56432         if(!e.isNavKeyPress()){
56433             return;
56434         }
56435         var g = this.grid, s = this.selection;
56436         if(!s){
56437             e.stopEvent();
56438             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56439             if(cell){
56440                 this.select(cell[0], cell[1]);
56441             }
56442             return;
56443         }
56444         var sm = this;
56445         var walk = function(row, col, step){
56446             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56447         };
56448         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56449         var newCell;
56450
56451       
56452
56453         switch(k){
56454             case e.TAB:
56455                 // handled by onEditorKey
56456                 if (g.isEditor && g.editing) {
56457                     return;
56458                 }
56459                 if(e.shiftKey) {
56460                     newCell = walk(r, c-1, -1);
56461                 } else {
56462                     newCell = walk(r, c+1, 1);
56463                 }
56464                 break;
56465             
56466             case e.DOWN:
56467                newCell = walk(r+1, c, 1);
56468                 break;
56469             
56470             case e.UP:
56471                 newCell = walk(r-1, c, -1);
56472                 break;
56473             
56474             case e.RIGHT:
56475                 newCell = walk(r, c+1, 1);
56476                 break;
56477             
56478             case e.LEFT:
56479                 newCell = walk(r, c-1, -1);
56480                 break;
56481             
56482             case e.ENTER:
56483                 
56484                 if(g.isEditor && !g.editing){
56485                    g.startEditing(r, c);
56486                    e.stopEvent();
56487                    return;
56488                 }
56489                 
56490                 
56491              break;
56492         };
56493         if(newCell){
56494             this.select(newCell[0], newCell[1]);
56495             e.stopEvent();
56496             
56497         }
56498     },
56499
56500     acceptsNav : function(row, col, cm){
56501         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56502     },
56503     /**
56504      * Selects a cell.
56505      * @param {Number} field (not used) - as it's normally used as a listener
56506      * @param {Number} e - event - fake it by using
56507      *
56508      * var e = Roo.EventObjectImpl.prototype;
56509      * e.keyCode = e.TAB
56510      *
56511      * 
56512      */
56513     onEditorKey : function(field, e){
56514         
56515         var k = e.getKey(),
56516             newCell,
56517             g = this.grid,
56518             ed = g.activeEditor,
56519             forward = false;
56520         ///Roo.log('onEditorKey' + k);
56521         
56522         
56523         if (this.enter_is_tab && k == e.ENTER) {
56524             k = e.TAB;
56525         }
56526         
56527         if(k == e.TAB){
56528             if(e.shiftKey){
56529                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56530             }else{
56531                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56532                 forward = true;
56533             }
56534             
56535             e.stopEvent();
56536             
56537         } else if(k == e.ENTER &&  !e.ctrlKey){
56538             ed.completeEdit();
56539             e.stopEvent();
56540             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56541         
56542                 } else if(k == e.ESC){
56543             ed.cancelEdit();
56544         }
56545                 
56546         if (newCell) {
56547             var ecall = { cell : newCell, forward : forward };
56548             this.fireEvent('beforeeditnext', ecall );
56549             newCell = ecall.cell;
56550                         forward = ecall.forward;
56551         }
56552                 
56553         if(newCell){
56554             //Roo.log('next cell after edit');
56555             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56556         } else if (forward) {
56557             // tabbed past last
56558             this.fireEvent.defer(100, this, ['tabend',this]);
56559         }
56560     }
56561 });/*
56562  * Based on:
56563  * Ext JS Library 1.1.1
56564  * Copyright(c) 2006-2007, Ext JS, LLC.
56565  *
56566  * Originally Released Under LGPL - original licence link has changed is not relivant.
56567  *
56568  * Fork - LGPL
56569  * <script type="text/javascript">
56570  */
56571  
56572 /**
56573  * @class Roo.grid.EditorGrid
56574  * @extends Roo.grid.Grid
56575  * Class for creating and editable grid.
56576  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56577  * The container MUST have some type of size defined for the grid to fill. The container will be 
56578  * automatically set to position relative if it isn't already.
56579  * @param {Object} dataSource The data model to bind to
56580  * @param {Object} colModel The column model with info about this grid's columns
56581  */
56582 Roo.grid.EditorGrid = function(container, config){
56583     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56584     this.getGridEl().addClass("xedit-grid");
56585
56586     if(!this.selModel){
56587         this.selModel = new Roo.grid.CellSelectionModel();
56588     }
56589
56590     this.activeEditor = null;
56591
56592         this.addEvents({
56593             /**
56594              * @event beforeedit
56595              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56596              * <ul style="padding:5px;padding-left:16px;">
56597              * <li>grid - This grid</li>
56598              * <li>record - The record being edited</li>
56599              * <li>field - The field name being edited</li>
56600              * <li>value - The value for the field being edited.</li>
56601              * <li>row - The grid row index</li>
56602              * <li>column - The grid column index</li>
56603              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56604              * </ul>
56605              * @param {Object} e An edit event (see above for description)
56606              */
56607             "beforeedit" : true,
56608             /**
56609              * @event afteredit
56610              * Fires after a cell is edited. <br />
56611              * <ul style="padding:5px;padding-left:16px;">
56612              * <li>grid - This grid</li>
56613              * <li>record - The record being edited</li>
56614              * <li>field - The field name being edited</li>
56615              * <li>value - The value being set</li>
56616              * <li>originalValue - The original value for the field, before the edit.</li>
56617              * <li>row - The grid row index</li>
56618              * <li>column - The grid column index</li>
56619              * </ul>
56620              * @param {Object} e An edit event (see above for description)
56621              */
56622             "afteredit" : true,
56623             /**
56624              * @event validateedit
56625              * Fires after a cell is edited, but before the value is set in the record. 
56626          * You can use this to modify the value being set in the field, Return false
56627              * to cancel the change. The edit event object has the following properties <br />
56628              * <ul style="padding:5px;padding-left:16px;">
56629          * <li>editor - This editor</li>
56630              * <li>grid - This grid</li>
56631              * <li>record - The record being edited</li>
56632              * <li>field - The field name being edited</li>
56633              * <li>value - The value being set</li>
56634              * <li>originalValue - The original value for the field, before the edit.</li>
56635              * <li>row - The grid row index</li>
56636              * <li>column - The grid column index</li>
56637              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56638              * </ul>
56639              * @param {Object} e An edit event (see above for description)
56640              */
56641             "validateedit" : true
56642         });
56643     this.on("bodyscroll", this.stopEditing,  this);
56644     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56645 };
56646
56647 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56648     /**
56649      * @cfg {Number} clicksToEdit
56650      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56651      */
56652     clicksToEdit: 2,
56653
56654     // private
56655     isEditor : true,
56656     // private
56657     trackMouseOver: false, // causes very odd FF errors
56658
56659     onCellDblClick : function(g, row, col){
56660         this.startEditing(row, col);
56661     },
56662
56663     onEditComplete : function(ed, value, startValue){
56664         this.editing = false;
56665         this.activeEditor = null;
56666         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56667         var r = ed.record;
56668         var field = this.colModel.getDataIndex(ed.col);
56669         var e = {
56670             grid: this,
56671             record: r,
56672             field: field,
56673             originalValue: startValue,
56674             value: value,
56675             row: ed.row,
56676             column: ed.col,
56677             cancel:false,
56678             editor: ed
56679         };
56680         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56681         cell.show();
56682           
56683         if(String(value) !== String(startValue)){
56684             
56685             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56686                 r.set(field, e.value);
56687                 // if we are dealing with a combo box..
56688                 // then we also set the 'name' colum to be the displayField
56689                 if (ed.field.displayField && ed.field.name) {
56690                     r.set(ed.field.name, ed.field.el.dom.value);
56691                 }
56692                 
56693                 delete e.cancel; //?? why!!!
56694                 this.fireEvent("afteredit", e);
56695             }
56696         } else {
56697             this.fireEvent("afteredit", e); // always fire it!
56698         }
56699         this.view.focusCell(ed.row, ed.col);
56700     },
56701
56702     /**
56703      * Starts editing the specified for the specified row/column
56704      * @param {Number} rowIndex
56705      * @param {Number} colIndex
56706      */
56707     startEditing : function(row, col){
56708         this.stopEditing();
56709         if(this.colModel.isCellEditable(col, row)){
56710             this.view.ensureVisible(row, col, true);
56711           
56712             var r = this.dataSource.getAt(row);
56713             var field = this.colModel.getDataIndex(col);
56714             var cell = Roo.get(this.view.getCell(row,col));
56715             var e = {
56716                 grid: this,
56717                 record: r,
56718                 field: field,
56719                 value: r.data[field],
56720                 row: row,
56721                 column: col,
56722                 cancel:false 
56723             };
56724             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56725                 this.editing = true;
56726                 var ed = this.colModel.getCellEditor(col, row);
56727                 
56728                 if (!ed) {
56729                     return;
56730                 }
56731                 if(!ed.rendered){
56732                     ed.render(ed.parentEl || document.body);
56733                 }
56734                 ed.field.reset();
56735                
56736                 cell.hide();
56737                 
56738                 (function(){ // complex but required for focus issues in safari, ie and opera
56739                     ed.row = row;
56740                     ed.col = col;
56741                     ed.record = r;
56742                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56743                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56744                     this.activeEditor = ed;
56745                     var v = r.data[field];
56746                     ed.startEdit(this.view.getCell(row, col), v);
56747                     // combo's with 'displayField and name set
56748                     if (ed.field.displayField && ed.field.name) {
56749                         ed.field.el.dom.value = r.data[ed.field.name];
56750                     }
56751                     
56752                     
56753                 }).defer(50, this);
56754             }
56755         }
56756     },
56757         
56758     /**
56759      * Stops any active editing
56760      */
56761     stopEditing : function(){
56762         if(this.activeEditor){
56763             this.activeEditor.completeEdit();
56764         }
56765         this.activeEditor = null;
56766     },
56767         
56768          /**
56769      * Called to get grid's drag proxy text, by default returns this.ddText.
56770      * @return {String}
56771      */
56772     getDragDropText : function(){
56773         var count = this.selModel.getSelectedCell() ? 1 : 0;
56774         return String.format(this.ddText, count, count == 1 ? '' : 's');
56775     }
56776         
56777 });/*
56778  * Based on:
56779  * Ext JS Library 1.1.1
56780  * Copyright(c) 2006-2007, Ext JS, LLC.
56781  *
56782  * Originally Released Under LGPL - original licence link has changed is not relivant.
56783  *
56784  * Fork - LGPL
56785  * <script type="text/javascript">
56786  */
56787
56788 // private - not really -- you end up using it !
56789 // This is a support class used internally by the Grid components
56790
56791 /**
56792  * @class Roo.grid.GridEditor
56793  * @extends Roo.Editor
56794  * Class for creating and editable grid elements.
56795  * @param {Object} config any settings (must include field)
56796  */
56797 Roo.grid.GridEditor = function(field, config){
56798     if (!config && field.field) {
56799         config = field;
56800         field = Roo.factory(config.field, Roo.form);
56801     }
56802     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56803     field.monitorTab = false;
56804 };
56805
56806 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56807     
56808     /**
56809      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56810      */
56811     
56812     alignment: "tl-tl",
56813     autoSize: "width",
56814     hideEl : false,
56815     cls: "x-small-editor x-grid-editor",
56816     shim:false,
56817     shadow:"frame"
56818 });/*
56819  * Based on:
56820  * Ext JS Library 1.1.1
56821  * Copyright(c) 2006-2007, Ext JS, LLC.
56822  *
56823  * Originally Released Under LGPL - original licence link has changed is not relivant.
56824  *
56825  * Fork - LGPL
56826  * <script type="text/javascript">
56827  */
56828   
56829
56830   
56831 Roo.grid.PropertyRecord = Roo.data.Record.create([
56832     {name:'name',type:'string'},  'value'
56833 ]);
56834
56835
56836 Roo.grid.PropertyStore = function(grid, source){
56837     this.grid = grid;
56838     this.store = new Roo.data.Store({
56839         recordType : Roo.grid.PropertyRecord
56840     });
56841     this.store.on('update', this.onUpdate,  this);
56842     if(source){
56843         this.setSource(source);
56844     }
56845     Roo.grid.PropertyStore.superclass.constructor.call(this);
56846 };
56847
56848
56849
56850 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56851     setSource : function(o){
56852         this.source = o;
56853         this.store.removeAll();
56854         var data = [];
56855         for(var k in o){
56856             if(this.isEditableValue(o[k])){
56857                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56858             }
56859         }
56860         this.store.loadRecords({records: data}, {}, true);
56861     },
56862
56863     onUpdate : function(ds, record, type){
56864         if(type == Roo.data.Record.EDIT){
56865             var v = record.data['value'];
56866             var oldValue = record.modified['value'];
56867             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56868                 this.source[record.id] = v;
56869                 record.commit();
56870                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56871             }else{
56872                 record.reject();
56873             }
56874         }
56875     },
56876
56877     getProperty : function(row){
56878        return this.store.getAt(row);
56879     },
56880
56881     isEditableValue: function(val){
56882         if(val && val instanceof Date){
56883             return true;
56884         }else if(typeof val == 'object' || typeof val == 'function'){
56885             return false;
56886         }
56887         return true;
56888     },
56889
56890     setValue : function(prop, value){
56891         this.source[prop] = value;
56892         this.store.getById(prop).set('value', value);
56893     },
56894
56895     getSource : function(){
56896         return this.source;
56897     }
56898 });
56899
56900 Roo.grid.PropertyColumnModel = function(grid, store){
56901     this.grid = grid;
56902     var g = Roo.grid;
56903     g.PropertyColumnModel.superclass.constructor.call(this, [
56904         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56905         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56906     ]);
56907     this.store = store;
56908     this.bselect = Roo.DomHelper.append(document.body, {
56909         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56910             {tag: 'option', value: 'true', html: 'true'},
56911             {tag: 'option', value: 'false', html: 'false'}
56912         ]
56913     });
56914     Roo.id(this.bselect);
56915     var f = Roo.form;
56916     this.editors = {
56917         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56918         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56919         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56920         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56921         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56922     };
56923     this.renderCellDelegate = this.renderCell.createDelegate(this);
56924     this.renderPropDelegate = this.renderProp.createDelegate(this);
56925 };
56926
56927 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56928     
56929     
56930     nameText : 'Name',
56931     valueText : 'Value',
56932     
56933     dateFormat : 'm/j/Y',
56934     
56935     
56936     renderDate : function(dateVal){
56937         return dateVal.dateFormat(this.dateFormat);
56938     },
56939
56940     renderBool : function(bVal){
56941         return bVal ? 'true' : 'false';
56942     },
56943
56944     isCellEditable : function(colIndex, rowIndex){
56945         return colIndex == 1;
56946     },
56947
56948     getRenderer : function(col){
56949         return col == 1 ?
56950             this.renderCellDelegate : this.renderPropDelegate;
56951     },
56952
56953     renderProp : function(v){
56954         return this.getPropertyName(v);
56955     },
56956
56957     renderCell : function(val){
56958         var rv = val;
56959         if(val instanceof Date){
56960             rv = this.renderDate(val);
56961         }else if(typeof val == 'boolean'){
56962             rv = this.renderBool(val);
56963         }
56964         return Roo.util.Format.htmlEncode(rv);
56965     },
56966
56967     getPropertyName : function(name){
56968         var pn = this.grid.propertyNames;
56969         return pn && pn[name] ? pn[name] : name;
56970     },
56971
56972     getCellEditor : function(colIndex, rowIndex){
56973         var p = this.store.getProperty(rowIndex);
56974         var n = p.data['name'], val = p.data['value'];
56975         
56976         if(typeof(this.grid.customEditors[n]) == 'string'){
56977             return this.editors[this.grid.customEditors[n]];
56978         }
56979         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56980             return this.grid.customEditors[n];
56981         }
56982         if(val instanceof Date){
56983             return this.editors['date'];
56984         }else if(typeof val == 'number'){
56985             return this.editors['number'];
56986         }else if(typeof val == 'boolean'){
56987             return this.editors['boolean'];
56988         }else{
56989             return this.editors['string'];
56990         }
56991     }
56992 });
56993
56994 /**
56995  * @class Roo.grid.PropertyGrid
56996  * @extends Roo.grid.EditorGrid
56997  * This class represents the  interface of a component based property grid control.
56998  * <br><br>Usage:<pre><code>
56999  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57000       
57001  });
57002  // set any options
57003  grid.render();
57004  * </code></pre>
57005   
57006  * @constructor
57007  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57008  * The container MUST have some type of size defined for the grid to fill. The container will be
57009  * automatically set to position relative if it isn't already.
57010  * @param {Object} config A config object that sets properties on this grid.
57011  */
57012 Roo.grid.PropertyGrid = function(container, config){
57013     config = config || {};
57014     var store = new Roo.grid.PropertyStore(this);
57015     this.store = store;
57016     var cm = new Roo.grid.PropertyColumnModel(this, store);
57017     store.store.sort('name', 'ASC');
57018     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57019         ds: store.store,
57020         cm: cm,
57021         enableColLock:false,
57022         enableColumnMove:false,
57023         stripeRows:false,
57024         trackMouseOver: false,
57025         clicksToEdit:1
57026     }, config));
57027     this.getGridEl().addClass('x-props-grid');
57028     this.lastEditRow = null;
57029     this.on('columnresize', this.onColumnResize, this);
57030     this.addEvents({
57031          /**
57032              * @event beforepropertychange
57033              * Fires before a property changes (return false to stop?)
57034              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57035              * @param {String} id Record Id
57036              * @param {String} newval New Value
57037          * @param {String} oldval Old Value
57038              */
57039         "beforepropertychange": true,
57040         /**
57041              * @event propertychange
57042              * Fires after a property changes
57043              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57044              * @param {String} id Record Id
57045              * @param {String} newval New Value
57046          * @param {String} oldval Old Value
57047              */
57048         "propertychange": true
57049     });
57050     this.customEditors = this.customEditors || {};
57051 };
57052 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57053     
57054      /**
57055      * @cfg {Object} customEditors map of colnames=> custom editors.
57056      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57057      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57058      * false disables editing of the field.
57059          */
57060     
57061       /**
57062      * @cfg {Object} propertyNames map of property Names to their displayed value
57063          */
57064     
57065     render : function(){
57066         Roo.grid.PropertyGrid.superclass.render.call(this);
57067         this.autoSize.defer(100, this);
57068     },
57069
57070     autoSize : function(){
57071         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57072         if(this.view){
57073             this.view.fitColumns();
57074         }
57075     },
57076
57077     onColumnResize : function(){
57078         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57079         this.autoSize();
57080     },
57081     /**
57082      * Sets the data for the Grid
57083      * accepts a Key => Value object of all the elements avaiable.
57084      * @param {Object} data  to appear in grid.
57085      */
57086     setSource : function(source){
57087         this.store.setSource(source);
57088         //this.autoSize();
57089     },
57090     /**
57091      * Gets all the data from the grid.
57092      * @return {Object} data  data stored in grid
57093      */
57094     getSource : function(){
57095         return this.store.getSource();
57096     }
57097 });/*
57098   
57099  * Licence LGPL
57100  
57101  */
57102  
57103 /**
57104  * @class Roo.grid.Calendar
57105  * @extends Roo.util.Grid
57106  * This class extends the Grid to provide a calendar widget
57107  * <br><br>Usage:<pre><code>
57108  var grid = new Roo.grid.Calendar("my-container-id", {
57109      ds: myDataStore,
57110      cm: myColModel,
57111      selModel: mySelectionModel,
57112      autoSizeColumns: true,
57113      monitorWindowResize: false,
57114      trackMouseOver: true
57115      eventstore : real data store..
57116  });
57117  // set any options
57118  grid.render();
57119   
57120   * @constructor
57121  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57122  * The container MUST have some type of size defined for the grid to fill. The container will be
57123  * automatically set to position relative if it isn't already.
57124  * @param {Object} config A config object that sets properties on this grid.
57125  */
57126 Roo.grid.Calendar = function(container, config){
57127         // initialize the container
57128         this.container = Roo.get(container);
57129         this.container.update("");
57130         this.container.setStyle("overflow", "hidden");
57131     this.container.addClass('x-grid-container');
57132
57133     this.id = this.container.id;
57134
57135     Roo.apply(this, config);
57136     // check and correct shorthanded configs
57137     
57138     var rows = [];
57139     var d =1;
57140     for (var r = 0;r < 6;r++) {
57141         
57142         rows[r]=[];
57143         for (var c =0;c < 7;c++) {
57144             rows[r][c]= '';
57145         }
57146     }
57147     if (this.eventStore) {
57148         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57149         this.eventStore.on('load',this.onLoad, this);
57150         this.eventStore.on('beforeload',this.clearEvents, this);
57151          
57152     }
57153     
57154     this.dataSource = new Roo.data.Store({
57155             proxy: new Roo.data.MemoryProxy(rows),
57156             reader: new Roo.data.ArrayReader({}, [
57157                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57158     });
57159
57160     this.dataSource.load();
57161     this.ds = this.dataSource;
57162     this.ds.xmodule = this.xmodule || false;
57163     
57164     
57165     var cellRender = function(v,x,r)
57166     {
57167         return String.format(
57168             '<div class="fc-day  fc-widget-content"><div>' +
57169                 '<div class="fc-event-container"></div>' +
57170                 '<div class="fc-day-number">{0}</div>'+
57171                 
57172                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57173             '</div></div>', v);
57174     
57175     }
57176     
57177     
57178     this.colModel = new Roo.grid.ColumnModel( [
57179         {
57180             xtype: 'ColumnModel',
57181             xns: Roo.grid,
57182             dataIndex : 'weekday0',
57183             header : 'Sunday',
57184             renderer : cellRender
57185         },
57186         {
57187             xtype: 'ColumnModel',
57188             xns: Roo.grid,
57189             dataIndex : 'weekday1',
57190             header : 'Monday',
57191             renderer : cellRender
57192         },
57193         {
57194             xtype: 'ColumnModel',
57195             xns: Roo.grid,
57196             dataIndex : 'weekday2',
57197             header : 'Tuesday',
57198             renderer : cellRender
57199         },
57200         {
57201             xtype: 'ColumnModel',
57202             xns: Roo.grid,
57203             dataIndex : 'weekday3',
57204             header : 'Wednesday',
57205             renderer : cellRender
57206         },
57207         {
57208             xtype: 'ColumnModel',
57209             xns: Roo.grid,
57210             dataIndex : 'weekday4',
57211             header : 'Thursday',
57212             renderer : cellRender
57213         },
57214         {
57215             xtype: 'ColumnModel',
57216             xns: Roo.grid,
57217             dataIndex : 'weekday5',
57218             header : 'Friday',
57219             renderer : cellRender
57220         },
57221         {
57222             xtype: 'ColumnModel',
57223             xns: Roo.grid,
57224             dataIndex : 'weekday6',
57225             header : 'Saturday',
57226             renderer : cellRender
57227         }
57228     ]);
57229     this.cm = this.colModel;
57230     this.cm.xmodule = this.xmodule || false;
57231  
57232         
57233           
57234     //this.selModel = new Roo.grid.CellSelectionModel();
57235     //this.sm = this.selModel;
57236     //this.selModel.init(this);
57237     
57238     
57239     if(this.width){
57240         this.container.setWidth(this.width);
57241     }
57242
57243     if(this.height){
57244         this.container.setHeight(this.height);
57245     }
57246     /** @private */
57247         this.addEvents({
57248         // raw events
57249         /**
57250          * @event click
57251          * The raw click event for the entire grid.
57252          * @param {Roo.EventObject} e
57253          */
57254         "click" : true,
57255         /**
57256          * @event dblclick
57257          * The raw dblclick event for the entire grid.
57258          * @param {Roo.EventObject} e
57259          */
57260         "dblclick" : true,
57261         /**
57262          * @event contextmenu
57263          * The raw contextmenu event for the entire grid.
57264          * @param {Roo.EventObject} e
57265          */
57266         "contextmenu" : true,
57267         /**
57268          * @event mousedown
57269          * The raw mousedown event for the entire grid.
57270          * @param {Roo.EventObject} e
57271          */
57272         "mousedown" : true,
57273         /**
57274          * @event mouseup
57275          * The raw mouseup event for the entire grid.
57276          * @param {Roo.EventObject} e
57277          */
57278         "mouseup" : true,
57279         /**
57280          * @event mouseover
57281          * The raw mouseover event for the entire grid.
57282          * @param {Roo.EventObject} e
57283          */
57284         "mouseover" : true,
57285         /**
57286          * @event mouseout
57287          * The raw mouseout event for the entire grid.
57288          * @param {Roo.EventObject} e
57289          */
57290         "mouseout" : true,
57291         /**
57292          * @event keypress
57293          * The raw keypress event for the entire grid.
57294          * @param {Roo.EventObject} e
57295          */
57296         "keypress" : true,
57297         /**
57298          * @event keydown
57299          * The raw keydown event for the entire grid.
57300          * @param {Roo.EventObject} e
57301          */
57302         "keydown" : true,
57303
57304         // custom events
57305
57306         /**
57307          * @event cellclick
57308          * Fires when a cell is clicked
57309          * @param {Grid} this
57310          * @param {Number} rowIndex
57311          * @param {Number} columnIndex
57312          * @param {Roo.EventObject} e
57313          */
57314         "cellclick" : true,
57315         /**
57316          * @event celldblclick
57317          * Fires when a cell is double clicked
57318          * @param {Grid} this
57319          * @param {Number} rowIndex
57320          * @param {Number} columnIndex
57321          * @param {Roo.EventObject} e
57322          */
57323         "celldblclick" : true,
57324         /**
57325          * @event rowclick
57326          * Fires when a row is clicked
57327          * @param {Grid} this
57328          * @param {Number} rowIndex
57329          * @param {Roo.EventObject} e
57330          */
57331         "rowclick" : true,
57332         /**
57333          * @event rowdblclick
57334          * Fires when a row is double clicked
57335          * @param {Grid} this
57336          * @param {Number} rowIndex
57337          * @param {Roo.EventObject} e
57338          */
57339         "rowdblclick" : true,
57340         /**
57341          * @event headerclick
57342          * Fires when a header is clicked
57343          * @param {Grid} this
57344          * @param {Number} columnIndex
57345          * @param {Roo.EventObject} e
57346          */
57347         "headerclick" : true,
57348         /**
57349          * @event headerdblclick
57350          * Fires when a header cell is double clicked
57351          * @param {Grid} this
57352          * @param {Number} columnIndex
57353          * @param {Roo.EventObject} e
57354          */
57355         "headerdblclick" : true,
57356         /**
57357          * @event rowcontextmenu
57358          * Fires when a row is right clicked
57359          * @param {Grid} this
57360          * @param {Number} rowIndex
57361          * @param {Roo.EventObject} e
57362          */
57363         "rowcontextmenu" : true,
57364         /**
57365          * @event cellcontextmenu
57366          * Fires when a cell is right clicked
57367          * @param {Grid} this
57368          * @param {Number} rowIndex
57369          * @param {Number} cellIndex
57370          * @param {Roo.EventObject} e
57371          */
57372          "cellcontextmenu" : true,
57373         /**
57374          * @event headercontextmenu
57375          * Fires when a header is right clicked
57376          * @param {Grid} this
57377          * @param {Number} columnIndex
57378          * @param {Roo.EventObject} e
57379          */
57380         "headercontextmenu" : true,
57381         /**
57382          * @event bodyscroll
57383          * Fires when the body element is scrolled
57384          * @param {Number} scrollLeft
57385          * @param {Number} scrollTop
57386          */
57387         "bodyscroll" : true,
57388         /**
57389          * @event columnresize
57390          * Fires when the user resizes a column
57391          * @param {Number} columnIndex
57392          * @param {Number} newSize
57393          */
57394         "columnresize" : true,
57395         /**
57396          * @event columnmove
57397          * Fires when the user moves a column
57398          * @param {Number} oldIndex
57399          * @param {Number} newIndex
57400          */
57401         "columnmove" : true,
57402         /**
57403          * @event startdrag
57404          * Fires when row(s) start being dragged
57405          * @param {Grid} this
57406          * @param {Roo.GridDD} dd The drag drop object
57407          * @param {event} e The raw browser event
57408          */
57409         "startdrag" : true,
57410         /**
57411          * @event enddrag
57412          * Fires when a drag operation is complete
57413          * @param {Grid} this
57414          * @param {Roo.GridDD} dd The drag drop object
57415          * @param {event} e The raw browser event
57416          */
57417         "enddrag" : true,
57418         /**
57419          * @event dragdrop
57420          * Fires when dragged row(s) are dropped on a valid DD target
57421          * @param {Grid} this
57422          * @param {Roo.GridDD} dd The drag drop object
57423          * @param {String} targetId The target drag drop object
57424          * @param {event} e The raw browser event
57425          */
57426         "dragdrop" : true,
57427         /**
57428          * @event dragover
57429          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57430          * @param {Grid} this
57431          * @param {Roo.GridDD} dd The drag drop object
57432          * @param {String} targetId The target drag drop object
57433          * @param {event} e The raw browser event
57434          */
57435         "dragover" : true,
57436         /**
57437          * @event dragenter
57438          *  Fires when the dragged row(s) first cross another DD target while being dragged
57439          * @param {Grid} this
57440          * @param {Roo.GridDD} dd The drag drop object
57441          * @param {String} targetId The target drag drop object
57442          * @param {event} e The raw browser event
57443          */
57444         "dragenter" : true,
57445         /**
57446          * @event dragout
57447          * Fires when the dragged row(s) leave another DD target while being dragged
57448          * @param {Grid} this
57449          * @param {Roo.GridDD} dd The drag drop object
57450          * @param {String} targetId The target drag drop object
57451          * @param {event} e The raw browser event
57452          */
57453         "dragout" : true,
57454         /**
57455          * @event rowclass
57456          * Fires when a row is rendered, so you can change add a style to it.
57457          * @param {GridView} gridview   The grid view
57458          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57459          */
57460         'rowclass' : true,
57461
57462         /**
57463          * @event render
57464          * Fires when the grid is rendered
57465          * @param {Grid} grid
57466          */
57467         'render' : true,
57468             /**
57469              * @event select
57470              * Fires when a date is selected
57471              * @param {DatePicker} this
57472              * @param {Date} date The selected date
57473              */
57474         'select': true,
57475         /**
57476              * @event monthchange
57477              * Fires when the displayed month changes 
57478              * @param {DatePicker} this
57479              * @param {Date} date The selected month
57480              */
57481         'monthchange': true,
57482         /**
57483              * @event evententer
57484              * Fires when mouse over an event
57485              * @param {Calendar} this
57486              * @param {event} Event
57487              */
57488         'evententer': true,
57489         /**
57490              * @event eventleave
57491              * Fires when the mouse leaves an
57492              * @param {Calendar} this
57493              * @param {event}
57494              */
57495         'eventleave': true,
57496         /**
57497              * @event eventclick
57498              * Fires when the mouse click an
57499              * @param {Calendar} this
57500              * @param {event}
57501              */
57502         'eventclick': true,
57503         /**
57504              * @event eventrender
57505              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57506              * @param {Calendar} this
57507              * @param {data} data to be modified
57508              */
57509         'eventrender': true
57510         
57511     });
57512
57513     Roo.grid.Grid.superclass.constructor.call(this);
57514     this.on('render', function() {
57515         this.view.el.addClass('x-grid-cal'); 
57516         
57517         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57518
57519     },this);
57520     
57521     if (!Roo.grid.Calendar.style) {
57522         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57523             
57524             
57525             '.x-grid-cal .x-grid-col' :  {
57526                 height: 'auto !important',
57527                 'vertical-align': 'top'
57528             },
57529             '.x-grid-cal  .fc-event-hori' : {
57530                 height: '14px'
57531             }
57532              
57533             
57534         }, Roo.id());
57535     }
57536
57537     
57538     
57539 };
57540 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57541     /**
57542      * @cfg {Store} eventStore The store that loads events.
57543      */
57544     eventStore : 25,
57545
57546      
57547     activeDate : false,
57548     startDay : 0,
57549     autoWidth : true,
57550     monitorWindowResize : false,
57551
57552     
57553     resizeColumns : function() {
57554         var col = (this.view.el.getWidth() / 7) - 3;
57555         // loop through cols, and setWidth
57556         for(var i =0 ; i < 7 ; i++){
57557             this.cm.setColumnWidth(i, col);
57558         }
57559     },
57560      setDate :function(date) {
57561         
57562         Roo.log('setDate?');
57563         
57564         this.resizeColumns();
57565         var vd = this.activeDate;
57566         this.activeDate = date;
57567 //        if(vd && this.el){
57568 //            var t = date.getTime();
57569 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57570 //                Roo.log('using add remove');
57571 //                
57572 //                this.fireEvent('monthchange', this, date);
57573 //                
57574 //                this.cells.removeClass("fc-state-highlight");
57575 //                this.cells.each(function(c){
57576 //                   if(c.dateValue == t){
57577 //                       c.addClass("fc-state-highlight");
57578 //                       setTimeout(function(){
57579 //                            try{c.dom.firstChild.focus();}catch(e){}
57580 //                       }, 50);
57581 //                       return false;
57582 //                   }
57583 //                   return true;
57584 //                });
57585 //                return;
57586 //            }
57587 //        }
57588         
57589         var days = date.getDaysInMonth();
57590         
57591         var firstOfMonth = date.getFirstDateOfMonth();
57592         var startingPos = firstOfMonth.getDay()-this.startDay;
57593         
57594         if(startingPos < this.startDay){
57595             startingPos += 7;
57596         }
57597         
57598         var pm = date.add(Date.MONTH, -1);
57599         var prevStart = pm.getDaysInMonth()-startingPos;
57600 //        
57601         
57602         
57603         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57604         
57605         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57606         //this.cells.addClassOnOver('fc-state-hover');
57607         
57608         var cells = this.cells.elements;
57609         var textEls = this.textNodes;
57610         
57611         //Roo.each(cells, function(cell){
57612         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57613         //});
57614         
57615         days += startingPos;
57616
57617         // convert everything to numbers so it's fast
57618         var day = 86400000;
57619         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57620         //Roo.log(d);
57621         //Roo.log(pm);
57622         //Roo.log(prevStart);
57623         
57624         var today = new Date().clearTime().getTime();
57625         var sel = date.clearTime().getTime();
57626         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57627         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57628         var ddMatch = this.disabledDatesRE;
57629         var ddText = this.disabledDatesText;
57630         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57631         var ddaysText = this.disabledDaysText;
57632         var format = this.format;
57633         
57634         var setCellClass = function(cal, cell){
57635             
57636             //Roo.log('set Cell Class');
57637             cell.title = "";
57638             var t = d.getTime();
57639             
57640             //Roo.log(d);
57641             
57642             
57643             cell.dateValue = t;
57644             if(t == today){
57645                 cell.className += " fc-today";
57646                 cell.className += " fc-state-highlight";
57647                 cell.title = cal.todayText;
57648             }
57649             if(t == sel){
57650                 // disable highlight in other month..
57651                 cell.className += " fc-state-highlight";
57652                 
57653             }
57654             // disabling
57655             if(t < min) {
57656                 //cell.className = " fc-state-disabled";
57657                 cell.title = cal.minText;
57658                 return;
57659             }
57660             if(t > max) {
57661                 //cell.className = " fc-state-disabled";
57662                 cell.title = cal.maxText;
57663                 return;
57664             }
57665             if(ddays){
57666                 if(ddays.indexOf(d.getDay()) != -1){
57667                     // cell.title = ddaysText;
57668                    // cell.className = " fc-state-disabled";
57669                 }
57670             }
57671             if(ddMatch && format){
57672                 var fvalue = d.dateFormat(format);
57673                 if(ddMatch.test(fvalue)){
57674                     cell.title = ddText.replace("%0", fvalue);
57675                    cell.className = " fc-state-disabled";
57676                 }
57677             }
57678             
57679             if (!cell.initialClassName) {
57680                 cell.initialClassName = cell.dom.className;
57681             }
57682             
57683             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57684         };
57685
57686         var i = 0;
57687         
57688         for(; i < startingPos; i++) {
57689             cells[i].dayName =  (++prevStart);
57690             Roo.log(textEls[i]);
57691             d.setDate(d.getDate()+1);
57692             
57693             //cells[i].className = "fc-past fc-other-month";
57694             setCellClass(this, cells[i]);
57695         }
57696         
57697         var intDay = 0;
57698         
57699         for(; i < days; i++){
57700             intDay = i - startingPos + 1;
57701             cells[i].dayName =  (intDay);
57702             d.setDate(d.getDate()+1);
57703             
57704             cells[i].className = ''; // "x-date-active";
57705             setCellClass(this, cells[i]);
57706         }
57707         var extraDays = 0;
57708         
57709         for(; i < 42; i++) {
57710             //textEls[i].innerHTML = (++extraDays);
57711             
57712             d.setDate(d.getDate()+1);
57713             cells[i].dayName = (++extraDays);
57714             cells[i].className = "fc-future fc-other-month";
57715             setCellClass(this, cells[i]);
57716         }
57717         
57718         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57719         
57720         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57721         
57722         // this will cause all the cells to mis
57723         var rows= [];
57724         var i =0;
57725         for (var r = 0;r < 6;r++) {
57726             for (var c =0;c < 7;c++) {
57727                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57728             }    
57729         }
57730         
57731         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57732         for(i=0;i<cells.length;i++) {
57733             
57734             this.cells.elements[i].dayName = cells[i].dayName ;
57735             this.cells.elements[i].className = cells[i].className;
57736             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57737             this.cells.elements[i].title = cells[i].title ;
57738             this.cells.elements[i].dateValue = cells[i].dateValue ;
57739         }
57740         
57741         
57742         
57743         
57744         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57745         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57746         
57747         ////if(totalRows != 6){
57748             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57749            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57750        // }
57751         
57752         this.fireEvent('monthchange', this, date);
57753         
57754         
57755     },
57756  /**
57757      * Returns the grid's SelectionModel.
57758      * @return {SelectionModel}
57759      */
57760     getSelectionModel : function(){
57761         if(!this.selModel){
57762             this.selModel = new Roo.grid.CellSelectionModel();
57763         }
57764         return this.selModel;
57765     },
57766
57767     load: function() {
57768         this.eventStore.load()
57769         
57770         
57771         
57772     },
57773     
57774     findCell : function(dt) {
57775         dt = dt.clearTime().getTime();
57776         var ret = false;
57777         this.cells.each(function(c){
57778             //Roo.log("check " +c.dateValue + '?=' + dt);
57779             if(c.dateValue == dt){
57780                 ret = c;
57781                 return false;
57782             }
57783             return true;
57784         });
57785         
57786         return ret;
57787     },
57788     
57789     findCells : function(rec) {
57790         var s = rec.data.start_dt.clone().clearTime().getTime();
57791        // Roo.log(s);
57792         var e= rec.data.end_dt.clone().clearTime().getTime();
57793        // Roo.log(e);
57794         var ret = [];
57795         this.cells.each(function(c){
57796              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57797             
57798             if(c.dateValue > e){
57799                 return ;
57800             }
57801             if(c.dateValue < s){
57802                 return ;
57803             }
57804             ret.push(c);
57805         });
57806         
57807         return ret;    
57808     },
57809     
57810     findBestRow: function(cells)
57811     {
57812         var ret = 0;
57813         
57814         for (var i =0 ; i < cells.length;i++) {
57815             ret  = Math.max(cells[i].rows || 0,ret);
57816         }
57817         return ret;
57818         
57819     },
57820     
57821     
57822     addItem : function(rec)
57823     {
57824         // look for vertical location slot in
57825         var cells = this.findCells(rec);
57826         
57827         rec.row = this.findBestRow(cells);
57828         
57829         // work out the location.
57830         
57831         var crow = false;
57832         var rows = [];
57833         for(var i =0; i < cells.length; i++) {
57834             if (!crow) {
57835                 crow = {
57836                     start : cells[i],
57837                     end :  cells[i]
57838                 };
57839                 continue;
57840             }
57841             if (crow.start.getY() == cells[i].getY()) {
57842                 // on same row.
57843                 crow.end = cells[i];
57844                 continue;
57845             }
57846             // different row.
57847             rows.push(crow);
57848             crow = {
57849                 start: cells[i],
57850                 end : cells[i]
57851             };
57852             
57853         }
57854         
57855         rows.push(crow);
57856         rec.els = [];
57857         rec.rows = rows;
57858         rec.cells = cells;
57859         for (var i = 0; i < cells.length;i++) {
57860             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57861             
57862         }
57863         
57864         
57865     },
57866     
57867     clearEvents: function() {
57868         
57869         if (!this.eventStore.getCount()) {
57870             return;
57871         }
57872         // reset number of rows in cells.
57873         Roo.each(this.cells.elements, function(c){
57874             c.rows = 0;
57875         });
57876         
57877         this.eventStore.each(function(e) {
57878             this.clearEvent(e);
57879         },this);
57880         
57881     },
57882     
57883     clearEvent : function(ev)
57884     {
57885         if (ev.els) {
57886             Roo.each(ev.els, function(el) {
57887                 el.un('mouseenter' ,this.onEventEnter, this);
57888                 el.un('mouseleave' ,this.onEventLeave, this);
57889                 el.remove();
57890             },this);
57891             ev.els = [];
57892         }
57893     },
57894     
57895     
57896     renderEvent : function(ev,ctr) {
57897         if (!ctr) {
57898              ctr = this.view.el.select('.fc-event-container',true).first();
57899         }
57900         
57901          
57902         this.clearEvent(ev);
57903             //code
57904        
57905         
57906         
57907         ev.els = [];
57908         var cells = ev.cells;
57909         var rows = ev.rows;
57910         this.fireEvent('eventrender', this, ev);
57911         
57912         for(var i =0; i < rows.length; i++) {
57913             
57914             cls = '';
57915             if (i == 0) {
57916                 cls += ' fc-event-start';
57917             }
57918             if ((i+1) == rows.length) {
57919                 cls += ' fc-event-end';
57920             }
57921             
57922             //Roo.log(ev.data);
57923             // how many rows should it span..
57924             var cg = this.eventTmpl.append(ctr,Roo.apply({
57925                 fccls : cls
57926                 
57927             }, ev.data) , true);
57928             
57929             
57930             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57931             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57932             cg.on('click', this.onEventClick, this, ev);
57933             
57934             ev.els.push(cg);
57935             
57936             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57937             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57938             //Roo.log(cg);
57939              
57940             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57941             cg.setWidth(ebox.right - sbox.x -2);
57942         }
57943     },
57944     
57945     renderEvents: function()
57946     {   
57947         // first make sure there is enough space..
57948         
57949         if (!this.eventTmpl) {
57950             this.eventTmpl = new Roo.Template(
57951                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57952                     '<div class="fc-event-inner">' +
57953                         '<span class="fc-event-time">{time}</span>' +
57954                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57955                     '</div>' +
57956                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57957                 '</div>'
57958             );
57959                 
57960         }
57961                
57962         
57963         
57964         this.cells.each(function(c) {
57965             //Roo.log(c.select('.fc-day-content div',true).first());
57966             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57967         });
57968         
57969         var ctr = this.view.el.select('.fc-event-container',true).first();
57970         
57971         var cls;
57972         this.eventStore.each(function(ev){
57973             
57974             this.renderEvent(ev);
57975              
57976              
57977         }, this);
57978         this.view.layout();
57979         
57980     },
57981     
57982     onEventEnter: function (e, el,event,d) {
57983         this.fireEvent('evententer', this, el, event);
57984     },
57985     
57986     onEventLeave: function (e, el,event,d) {
57987         this.fireEvent('eventleave', this, el, event);
57988     },
57989     
57990     onEventClick: function (e, el,event,d) {
57991         this.fireEvent('eventclick', this, el, event);
57992     },
57993     
57994     onMonthChange: function () {
57995         this.store.load();
57996     },
57997     
57998     onLoad: function () {
57999         
58000         //Roo.log('calendar onload');
58001 //         
58002         if(this.eventStore.getCount() > 0){
58003             
58004            
58005             
58006             this.eventStore.each(function(d){
58007                 
58008                 
58009                 // FIXME..
58010                 var add =   d.data;
58011                 if (typeof(add.end_dt) == 'undefined')  {
58012                     Roo.log("Missing End time in calendar data: ");
58013                     Roo.log(d);
58014                     return;
58015                 }
58016                 if (typeof(add.start_dt) == 'undefined')  {
58017                     Roo.log("Missing Start time in calendar data: ");
58018                     Roo.log(d);
58019                     return;
58020                 }
58021                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58022                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58023                 add.id = add.id || d.id;
58024                 add.title = add.title || '??';
58025                 
58026                 this.addItem(d);
58027                 
58028              
58029             },this);
58030         }
58031         
58032         this.renderEvents();
58033     }
58034     
58035
58036 });
58037 /*
58038  grid : {
58039                 xtype: 'Grid',
58040                 xns: Roo.grid,
58041                 listeners : {
58042                     render : function ()
58043                     {
58044                         _this.grid = this;
58045                         
58046                         if (!this.view.el.hasClass('course-timesheet')) {
58047                             this.view.el.addClass('course-timesheet');
58048                         }
58049                         if (this.tsStyle) {
58050                             this.ds.load({});
58051                             return; 
58052                         }
58053                         Roo.log('width');
58054                         Roo.log(_this.grid.view.el.getWidth());
58055                         
58056                         
58057                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58058                             '.course-timesheet .x-grid-row' : {
58059                                 height: '80px'
58060                             },
58061                             '.x-grid-row td' : {
58062                                 'vertical-align' : 0
58063                             },
58064                             '.course-edit-link' : {
58065                                 'color' : 'blue',
58066                                 'text-overflow' : 'ellipsis',
58067                                 'overflow' : 'hidden',
58068                                 'white-space' : 'nowrap',
58069                                 'cursor' : 'pointer'
58070                             },
58071                             '.sub-link' : {
58072                                 'color' : 'green'
58073                             },
58074                             '.de-act-sup-link' : {
58075                                 'color' : 'purple',
58076                                 'text-decoration' : 'line-through'
58077                             },
58078                             '.de-act-link' : {
58079                                 'color' : 'red',
58080                                 'text-decoration' : 'line-through'
58081                             },
58082                             '.course-timesheet .course-highlight' : {
58083                                 'border-top-style': 'dashed !important',
58084                                 'border-bottom-bottom': 'dashed !important'
58085                             },
58086                             '.course-timesheet .course-item' : {
58087                                 'font-family'   : 'tahoma, arial, helvetica',
58088                                 'font-size'     : '11px',
58089                                 'overflow'      : 'hidden',
58090                                 'padding-left'  : '10px',
58091                                 'padding-right' : '10px',
58092                                 'padding-top' : '10px' 
58093                             }
58094                             
58095                         }, Roo.id());
58096                                 this.ds.load({});
58097                     }
58098                 },
58099                 autoWidth : true,
58100                 monitorWindowResize : false,
58101                 cellrenderer : function(v,x,r)
58102                 {
58103                     return v;
58104                 },
58105                 sm : {
58106                     xtype: 'CellSelectionModel',
58107                     xns: Roo.grid
58108                 },
58109                 dataSource : {
58110                     xtype: 'Store',
58111                     xns: Roo.data,
58112                     listeners : {
58113                         beforeload : function (_self, options)
58114                         {
58115                             options.params = options.params || {};
58116                             options.params._month = _this.monthField.getValue();
58117                             options.params.limit = 9999;
58118                             options.params['sort'] = 'when_dt';    
58119                             options.params['dir'] = 'ASC';    
58120                             this.proxy.loadResponse = this.loadResponse;
58121                             Roo.log("load?");
58122                             //this.addColumns();
58123                         },
58124                         load : function (_self, records, options)
58125                         {
58126                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58127                                 // if you click on the translation.. you can edit it...
58128                                 var el = Roo.get(this);
58129                                 var id = el.dom.getAttribute('data-id');
58130                                 var d = el.dom.getAttribute('data-date');
58131                                 var t = el.dom.getAttribute('data-time');
58132                                 //var id = this.child('span').dom.textContent;
58133                                 
58134                                 //Roo.log(this);
58135                                 Pman.Dialog.CourseCalendar.show({
58136                                     id : id,
58137                                     when_d : d,
58138                                     when_t : t,
58139                                     productitem_active : id ? 1 : 0
58140                                 }, function() {
58141                                     _this.grid.ds.load({});
58142                                 });
58143                            
58144                            });
58145                            
58146                            _this.panel.fireEvent('resize', [ '', '' ]);
58147                         }
58148                     },
58149                     loadResponse : function(o, success, response){
58150                             // this is overridden on before load..
58151                             
58152                             Roo.log("our code?");       
58153                             //Roo.log(success);
58154                             //Roo.log(response)
58155                             delete this.activeRequest;
58156                             if(!success){
58157                                 this.fireEvent("loadexception", this, o, response);
58158                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58159                                 return;
58160                             }
58161                             var result;
58162                             try {
58163                                 result = o.reader.read(response);
58164                             }catch(e){
58165                                 Roo.log("load exception?");
58166                                 this.fireEvent("loadexception", this, o, response, e);
58167                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58168                                 return;
58169                             }
58170                             Roo.log("ready...");        
58171                             // loop through result.records;
58172                             // and set this.tdate[date] = [] << array of records..
58173                             _this.tdata  = {};
58174                             Roo.each(result.records, function(r){
58175                                 //Roo.log(r.data);
58176                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58177                                     _this.tdata[r.data.when_dt.format('j')] = [];
58178                                 }
58179                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58180                             });
58181                             
58182                             //Roo.log(_this.tdata);
58183                             
58184                             result.records = [];
58185                             result.totalRecords = 6;
58186                     
58187                             // let's generate some duumy records for the rows.
58188                             //var st = _this.dateField.getValue();
58189                             
58190                             // work out monday..
58191                             //st = st.add(Date.DAY, -1 * st.format('w'));
58192                             
58193                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58194                             
58195                             var firstOfMonth = date.getFirstDayOfMonth();
58196                             var days = date.getDaysInMonth();
58197                             var d = 1;
58198                             var firstAdded = false;
58199                             for (var i = 0; i < result.totalRecords ; i++) {
58200                                 //var d= st.add(Date.DAY, i);
58201                                 var row = {};
58202                                 var added = 0;
58203                                 for(var w = 0 ; w < 7 ; w++){
58204                                     if(!firstAdded && firstOfMonth != w){
58205                                         continue;
58206                                     }
58207                                     if(d > days){
58208                                         continue;
58209                                     }
58210                                     firstAdded = true;
58211                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58212                                     row['weekday'+w] = String.format(
58213                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58214                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58215                                                     d,
58216                                                     date.format('Y-m-')+dd
58217                                                 );
58218                                     added++;
58219                                     if(typeof(_this.tdata[d]) != 'undefined'){
58220                                         Roo.each(_this.tdata[d], function(r){
58221                                             var is_sub = '';
58222                                             var deactive = '';
58223                                             var id = r.id;
58224                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58225                                             if(r.parent_id*1>0){
58226                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58227                                                 id = r.parent_id;
58228                                             }
58229                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58230                                                 deactive = 'de-act-link';
58231                                             }
58232                                             
58233                                             row['weekday'+w] += String.format(
58234                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58235                                                     id, //0
58236                                                     r.product_id_name, //1
58237                                                     r.when_dt.format('h:ia'), //2
58238                                                     is_sub, //3
58239                                                     deactive, //4
58240                                                     desc // 5
58241                                             );
58242                                         });
58243                                     }
58244                                     d++;
58245                                 }
58246                                 
58247                                 // only do this if something added..
58248                                 if(added > 0){ 
58249                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58250                                 }
58251                                 
58252                                 
58253                                 // push it twice. (second one with an hour..
58254                                 
58255                             }
58256                             //Roo.log(result);
58257                             this.fireEvent("load", this, o, o.request.arg);
58258                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58259                         },
58260                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58261                     proxy : {
58262                         xtype: 'HttpProxy',
58263                         xns: Roo.data,
58264                         method : 'GET',
58265                         url : baseURL + '/Roo/Shop_course.php'
58266                     },
58267                     reader : {
58268                         xtype: 'JsonReader',
58269                         xns: Roo.data,
58270                         id : 'id',
58271                         fields : [
58272                             {
58273                                 'name': 'id',
58274                                 'type': 'int'
58275                             },
58276                             {
58277                                 'name': 'when_dt',
58278                                 'type': 'string'
58279                             },
58280                             {
58281                                 'name': 'end_dt',
58282                                 'type': 'string'
58283                             },
58284                             {
58285                                 'name': 'parent_id',
58286                                 'type': 'int'
58287                             },
58288                             {
58289                                 'name': 'product_id',
58290                                 'type': 'int'
58291                             },
58292                             {
58293                                 'name': 'productitem_id',
58294                                 'type': 'int'
58295                             },
58296                             {
58297                                 'name': 'guid',
58298                                 'type': 'int'
58299                             }
58300                         ]
58301                     }
58302                 },
58303                 toolbar : {
58304                     xtype: 'Toolbar',
58305                     xns: Roo,
58306                     items : [
58307                         {
58308                             xtype: 'Button',
58309                             xns: Roo.Toolbar,
58310                             listeners : {
58311                                 click : function (_self, e)
58312                                 {
58313                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58314                                     sd.setMonth(sd.getMonth()-1);
58315                                     _this.monthField.setValue(sd.format('Y-m-d'));
58316                                     _this.grid.ds.load({});
58317                                 }
58318                             },
58319                             text : "Back"
58320                         },
58321                         {
58322                             xtype: 'Separator',
58323                             xns: Roo.Toolbar
58324                         },
58325                         {
58326                             xtype: 'MonthField',
58327                             xns: Roo.form,
58328                             listeners : {
58329                                 render : function (_self)
58330                                 {
58331                                     _this.monthField = _self;
58332                                    // _this.monthField.set  today
58333                                 },
58334                                 select : function (combo, date)
58335                                 {
58336                                     _this.grid.ds.load({});
58337                                 }
58338                             },
58339                             value : (function() { return new Date(); })()
58340                         },
58341                         {
58342                             xtype: 'Separator',
58343                             xns: Roo.Toolbar
58344                         },
58345                         {
58346                             xtype: 'TextItem',
58347                             xns: Roo.Toolbar,
58348                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58349                         },
58350                         {
58351                             xtype: 'Fill',
58352                             xns: Roo.Toolbar
58353                         },
58354                         {
58355                             xtype: 'Button',
58356                             xns: Roo.Toolbar,
58357                             listeners : {
58358                                 click : function (_self, e)
58359                                 {
58360                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58361                                     sd.setMonth(sd.getMonth()+1);
58362                                     _this.monthField.setValue(sd.format('Y-m-d'));
58363                                     _this.grid.ds.load({});
58364                                 }
58365                             },
58366                             text : "Next"
58367                         }
58368                     ]
58369                 },
58370                  
58371             }
58372         };
58373         
58374         *//*
58375  * Based on:
58376  * Ext JS Library 1.1.1
58377  * Copyright(c) 2006-2007, Ext JS, LLC.
58378  *
58379  * Originally Released Under LGPL - original licence link has changed is not relivant.
58380  *
58381  * Fork - LGPL
58382  * <script type="text/javascript">
58383  */
58384  
58385 /**
58386  * @class Roo.LoadMask
58387  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58388  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58389  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58390  * element's UpdateManager load indicator and will be destroyed after the initial load.
58391  * @constructor
58392  * Create a new LoadMask
58393  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58394  * @param {Object} config The config object
58395  */
58396 Roo.LoadMask = function(el, config){
58397     this.el = Roo.get(el);
58398     Roo.apply(this, config);
58399     if(this.store){
58400         this.store.on('beforeload', this.onBeforeLoad, this);
58401         this.store.on('load', this.onLoad, this);
58402         this.store.on('loadexception', this.onLoadException, this);
58403         this.removeMask = false;
58404     }else{
58405         var um = this.el.getUpdateManager();
58406         um.showLoadIndicator = false; // disable the default indicator
58407         um.on('beforeupdate', this.onBeforeLoad, this);
58408         um.on('update', this.onLoad, this);
58409         um.on('failure', this.onLoad, this);
58410         this.removeMask = true;
58411     }
58412 };
58413
58414 Roo.LoadMask.prototype = {
58415     /**
58416      * @cfg {Boolean} removeMask
58417      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58418      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58419      */
58420     /**
58421      * @cfg {String} msg
58422      * The text to display in a centered loading message box (defaults to 'Loading...')
58423      */
58424     msg : 'Loading...',
58425     /**
58426      * @cfg {String} msgCls
58427      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58428      */
58429     msgCls : 'x-mask-loading',
58430
58431     /**
58432      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58433      * @type Boolean
58434      */
58435     disabled: false,
58436
58437     /**
58438      * Disables the mask to prevent it from being displayed
58439      */
58440     disable : function(){
58441        this.disabled = true;
58442     },
58443
58444     /**
58445      * Enables the mask so that it can be displayed
58446      */
58447     enable : function(){
58448         this.disabled = false;
58449     },
58450     
58451     onLoadException : function()
58452     {
58453         Roo.log(arguments);
58454         
58455         if (typeof(arguments[3]) != 'undefined') {
58456             Roo.MessageBox.alert("Error loading",arguments[3]);
58457         } 
58458         /*
58459         try {
58460             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58461                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58462             }   
58463         } catch(e) {
58464             
58465         }
58466         */
58467     
58468         
58469         
58470         this.el.unmask(this.removeMask);
58471     },
58472     // private
58473     onLoad : function()
58474     {
58475         this.el.unmask(this.removeMask);
58476     },
58477
58478     // private
58479     onBeforeLoad : function(){
58480         if(!this.disabled){
58481             this.el.mask(this.msg, this.msgCls);
58482         }
58483     },
58484
58485     // private
58486     destroy : function(){
58487         if(this.store){
58488             this.store.un('beforeload', this.onBeforeLoad, this);
58489             this.store.un('load', this.onLoad, this);
58490             this.store.un('loadexception', this.onLoadException, this);
58491         }else{
58492             var um = this.el.getUpdateManager();
58493             um.un('beforeupdate', this.onBeforeLoad, this);
58494             um.un('update', this.onLoad, this);
58495             um.un('failure', this.onLoad, this);
58496         }
58497     }
58498 };/*
58499  * Based on:
58500  * Ext JS Library 1.1.1
58501  * Copyright(c) 2006-2007, Ext JS, LLC.
58502  *
58503  * Originally Released Under LGPL - original licence link has changed is not relivant.
58504  *
58505  * Fork - LGPL
58506  * <script type="text/javascript">
58507  */
58508
58509
58510 /**
58511  * @class Roo.XTemplate
58512  * @extends Roo.Template
58513  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58514 <pre><code>
58515 var t = new Roo.XTemplate(
58516         '&lt;select name="{name}"&gt;',
58517                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58518         '&lt;/select&gt;'
58519 );
58520  
58521 // then append, applying the master template values
58522  </code></pre>
58523  *
58524  * Supported features:
58525  *
58526  *  Tags:
58527
58528 <pre><code>
58529       {a_variable} - output encoded.
58530       {a_variable.format:("Y-m-d")} - call a method on the variable
58531       {a_variable:raw} - unencoded output
58532       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58533       {a_variable:this.method_on_template(...)} - call a method on the template object.
58534  
58535 </code></pre>
58536  *  The tpl tag:
58537 <pre><code>
58538         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58539         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58540         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58541         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58542   
58543         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58544         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58545 </code></pre>
58546  *      
58547  */
58548 Roo.XTemplate = function()
58549 {
58550     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58551     if (this.html) {
58552         this.compile();
58553     }
58554 };
58555
58556
58557 Roo.extend(Roo.XTemplate, Roo.Template, {
58558
58559     /**
58560      * The various sub templates
58561      */
58562     tpls : false,
58563     /**
58564      *
58565      * basic tag replacing syntax
58566      * WORD:WORD()
58567      *
58568      * // you can fake an object call by doing this
58569      *  x.t:(test,tesT) 
58570      * 
58571      */
58572     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58573
58574     /**
58575      * compile the template
58576      *
58577      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58578      *
58579      */
58580     compile: function()
58581     {
58582         var s = this.html;
58583      
58584         s = ['<tpl>', s, '</tpl>'].join('');
58585     
58586         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58587             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58588             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58589             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58590             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58591             m,
58592             id     = 0,
58593             tpls   = [];
58594     
58595         while(true == !!(m = s.match(re))){
58596             var forMatch   = m[0].match(nameRe),
58597                 ifMatch   = m[0].match(ifRe),
58598                 execMatch   = m[0].match(execRe),
58599                 namedMatch   = m[0].match(namedRe),
58600                 
58601                 exp  = null, 
58602                 fn   = null,
58603                 exec = null,
58604                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58605                 
58606             if (ifMatch) {
58607                 // if - puts fn into test..
58608                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58609                 if(exp){
58610                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58611                 }
58612             }
58613             
58614             if (execMatch) {
58615                 // exec - calls a function... returns empty if true is  returned.
58616                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58617                 if(exp){
58618                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58619                 }
58620             }
58621             
58622             
58623             if (name) {
58624                 // for = 
58625                 switch(name){
58626                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58627                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58628                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58629                 }
58630             }
58631             var uid = namedMatch ? namedMatch[1] : id;
58632             
58633             
58634             tpls.push({
58635                 id:     namedMatch ? namedMatch[1] : id,
58636                 target: name,
58637                 exec:   exec,
58638                 test:   fn,
58639                 body:   m[1] || ''
58640             });
58641             if (namedMatch) {
58642                 s = s.replace(m[0], '');
58643             } else { 
58644                 s = s.replace(m[0], '{xtpl'+ id + '}');
58645             }
58646             ++id;
58647         }
58648         this.tpls = [];
58649         for(var i = tpls.length-1; i >= 0; --i){
58650             this.compileTpl(tpls[i]);
58651             this.tpls[tpls[i].id] = tpls[i];
58652         }
58653         this.master = tpls[tpls.length-1];
58654         return this;
58655     },
58656     /**
58657      * same as applyTemplate, except it's done to one of the subTemplates
58658      * when using named templates, you can do:
58659      *
58660      * var str = pl.applySubTemplate('your-name', values);
58661      *
58662      * 
58663      * @param {Number} id of the template
58664      * @param {Object} values to apply to template
58665      * @param {Object} parent (normaly the instance of this object)
58666      */
58667     applySubTemplate : function(id, values, parent)
58668     {
58669         
58670         
58671         var t = this.tpls[id];
58672         
58673         
58674         try { 
58675             if(t.test && !t.test.call(this, values, parent)){
58676                 return '';
58677             }
58678         } catch(e) {
58679             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58680             Roo.log(e.toString());
58681             Roo.log(t.test);
58682             return ''
58683         }
58684         try { 
58685             
58686             if(t.exec && t.exec.call(this, values, parent)){
58687                 return '';
58688             }
58689         } catch(e) {
58690             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58691             Roo.log(e.toString());
58692             Roo.log(t.exec);
58693             return ''
58694         }
58695         try {
58696             var vs = t.target ? t.target.call(this, values, parent) : values;
58697             parent = t.target ? values : parent;
58698             if(t.target && vs instanceof Array){
58699                 var buf = [];
58700                 for(var i = 0, len = vs.length; i < len; i++){
58701                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58702                 }
58703                 return buf.join('');
58704             }
58705             return t.compiled.call(this, vs, parent);
58706         } catch (e) {
58707             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58708             Roo.log(e.toString());
58709             Roo.log(t.compiled);
58710             return '';
58711         }
58712     },
58713
58714     compileTpl : function(tpl)
58715     {
58716         var fm = Roo.util.Format;
58717         var useF = this.disableFormats !== true;
58718         var sep = Roo.isGecko ? "+" : ",";
58719         var undef = function(str) {
58720             Roo.log("Property not found :"  + str);
58721             return '';
58722         };
58723         
58724         var fn = function(m, name, format, args)
58725         {
58726             //Roo.log(arguments);
58727             args = args ? args.replace(/\\'/g,"'") : args;
58728             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58729             if (typeof(format) == 'undefined') {
58730                 format= 'htmlEncode';
58731             }
58732             if (format == 'raw' ) {
58733                 format = false;
58734             }
58735             
58736             if(name.substr(0, 4) == 'xtpl'){
58737                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58738             }
58739             
58740             // build an array of options to determine if value is undefined..
58741             
58742             // basically get 'xxxx.yyyy' then do
58743             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58744             //    (function () { Roo.log("Property not found"); return ''; })() :
58745             //    ......
58746             
58747             var udef_ar = [];
58748             var lookfor = '';
58749             Roo.each(name.split('.'), function(st) {
58750                 lookfor += (lookfor.length ? '.': '') + st;
58751                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58752             });
58753             
58754             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58755             
58756             
58757             if(format && useF){
58758                 
58759                 args = args ? ',' + args : "";
58760                  
58761                 if(format.substr(0, 5) != "this."){
58762                     format = "fm." + format + '(';
58763                 }else{
58764                     format = 'this.call("'+ format.substr(5) + '", ';
58765                     args = ", values";
58766                 }
58767                 
58768                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58769             }
58770              
58771             if (args.length) {
58772                 // called with xxyx.yuu:(test,test)
58773                 // change to ()
58774                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58775             }
58776             // raw.. - :raw modifier..
58777             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58778             
58779         };
58780         var body;
58781         // branched to use + in gecko and [].join() in others
58782         if(Roo.isGecko){
58783             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58784                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58785                     "';};};";
58786         }else{
58787             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58788             body.push(tpl.body.replace(/(\r\n|\n)/g,
58789                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58790             body.push("'].join('');};};");
58791             body = body.join('');
58792         }
58793         
58794         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58795        
58796         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58797         eval(body);
58798         
58799         return this;
58800     },
58801
58802     applyTemplate : function(values){
58803         return this.master.compiled.call(this, values, {});
58804         //var s = this.subs;
58805     },
58806
58807     apply : function(){
58808         return this.applyTemplate.apply(this, arguments);
58809     }
58810
58811  });
58812
58813 Roo.XTemplate.from = function(el){
58814     el = Roo.getDom(el);
58815     return new Roo.XTemplate(el.value || el.innerHTML);
58816 };