0371de08d079ceeb0fb662c6b5ade2879bd53857
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isTouch =  (function() {
67             try {  
68                 document.createEvent("TouchEvent");  
69                 return true;  
70             } catch (e) {  
71                 return false;  
72             } 
73             
74         })();
75     // remove css image flicker
76         if(isIE && !isIE7){
77         try{
78             document.execCommand("BackgroundImageCache", false, true);
79         }catch(e){}
80     }
81     
82     Roo.apply(Roo, {
83         /**
84          * True if the browser is in strict mode
85          * @type Boolean
86          */
87         isStrict : isStrict,
88         /**
89          * True if the page is running over SSL
90          * @type Boolean
91          */
92         isSecure : isSecure,
93         /**
94          * True when the document is fully initialized and ready for action
95          * @type Boolean
96          */
97         isReady : false,
98         /**
99          * Turn on debugging output (currently only the factory uses this)
100          * @type Boolean
101          */
102         
103         debug: false,
104
105         /**
106          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
107          * @type Boolean
108          */
109         enableGarbageCollector : true,
110
111         /**
112          * True to automatically purge event listeners after uncaching an element (defaults to false).
113          * Note: this only happens if enableGarbageCollector is true.
114          * @type Boolean
115          */
116         enableListenerCollection:false,
117
118         /**
119          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
120          * the IE insecure content warning (defaults to javascript:false).
121          * @type String
122          */
123         SSL_SECURE_URL : "javascript:false",
124
125         /**
126          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
127          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
128          * @type String
129          */
130         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
131
132         emptyFn : function(){},
133         
134         /**
135          * Copies all the properties of config to obj if they don't already exist.
136          * @param {Object} obj The receiver of the properties
137          * @param {Object} config The source of the properties
138          * @return {Object} returns obj
139          */
140         applyIf : function(o, c){
141             if(o && c){
142                 for(var p in c){
143                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
144                 }
145             }
146             return o;
147         },
148
149         /**
150          * Applies event listeners to elements by selectors when the document is ready.
151          * The event name is specified with an @ suffix.
152 <pre><code>
153 Roo.addBehaviors({
154    // add a listener for click on all anchors in element with id foo
155    '#foo a@click' : function(e, t){
156        // do something
157    },
158
159    // add the same listener to multiple selectors (separated by comma BEFORE the @)
160    '#foo a, #bar span.some-class@mouseover' : function(){
161        // do something
162    }
163 });
164 </code></pre>
165          * @param {Object} obj The list of behaviors to apply
166          */
167         addBehaviors : function(o){
168             if(!Roo.isReady){
169                 Roo.onReady(function(){
170                     Roo.addBehaviors(o);
171                 });
172                 return;
173             }
174             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
175             for(var b in o){
176                 var parts = b.split('@');
177                 if(parts[1]){ // for Object prototype breakers
178                     var s = parts[0];
179                     if(!cache[s]){
180                         cache[s] = Roo.select(s);
181                     }
182                     cache[s].on(parts[1], o[b]);
183                 }
184             }
185             cache = null;
186         },
187
188         /**
189          * Generates unique ids. If the element already has an id, it is unchanged
190          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
191          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
192          * @return {String} The generated Id.
193          */
194         id : function(el, prefix){
195             prefix = prefix || "roo-gen";
196             el = Roo.getDom(el);
197             var id = prefix + (++idSeed);
198             return el ? (el.id ? el.id : (el.id = id)) : id;
199         },
200          
201        
202         /**
203          * Extends one class with another class and optionally overrides members with the passed literal. This class
204          * also adds the function "override()" to the class that can be used to override
205          * members on an instance.
206          * @param {Object} subclass The class inheriting the functionality
207          * @param {Object} superclass The class being extended
208          * @param {Object} overrides (optional) A literal with members
209          * @method extend
210          */
211         extend : function(){
212             // inline overrides
213             var io = function(o){
214                 for(var m in o){
215                     this[m] = o[m];
216                 }
217             };
218             return function(sb, sp, overrides){
219                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
220                     overrides = sp;
221                     sp = sb;
222                     sb = function(){sp.apply(this, arguments);};
223                 }
224                 var F = function(){}, sbp, spp = sp.prototype;
225                 F.prototype = spp;
226                 sbp = sb.prototype = new F();
227                 sbp.constructor=sb;
228                 sb.superclass=spp;
229                 
230                 if(spp.constructor == Object.prototype.constructor){
231                     spp.constructor=sp;
232                    
233                 }
234                 
235                 sb.override = function(o){
236                     Roo.override(sb, o);
237                 };
238                 sbp.override = io;
239                 Roo.override(sb, overrides);
240                 return sb;
241             };
242         }(),
243
244         /**
245          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
246          * Usage:<pre><code>
247 Roo.override(MyClass, {
248     newMethod1: function(){
249         // etc.
250     },
251     newMethod2: function(foo){
252         // etc.
253     }
254 });
255  </code></pre>
256          * @param {Object} origclass The class to override
257          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
258          * containing one or more methods.
259          * @method override
260          */
261         override : function(origclass, overrides){
262             if(overrides){
263                 var p = origclass.prototype;
264                 for(var method in overrides){
265                     p[method] = overrides[method];
266                 }
267             }
268         },
269         /**
270          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
271          * <pre><code>
272 Roo.namespace('Company', 'Company.data');
273 Company.Widget = function() { ... }
274 Company.data.CustomStore = function(config) { ... }
275 </code></pre>
276          * @param {String} namespace1
277          * @param {String} namespace2
278          * @param {String} etc
279          * @method namespace
280          */
281         namespace : function(){
282             var a=arguments, o=null, i, j, d, rt;
283             for (i=0; i<a.length; ++i) {
284                 d=a[i].split(".");
285                 rt = d[0];
286                 /** eval:var:o */
287                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
288                 for (j=1; j<d.length; ++j) {
289                     o[d[j]]=o[d[j]] || {};
290                     o=o[d[j]];
291                 }
292             }
293         },
294         /**
295          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
296          * <pre><code>
297 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
298 Roo.factory(conf, Roo.data);
299 </code></pre>
300          * @param {String} classname
301          * @param {String} namespace (optional)
302          * @method factory
303          */
304          
305         factory : function(c, ns)
306         {
307             // no xtype, no ns or c.xns - or forced off by c.xns
308             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
309                 return c;
310             }
311             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
312             if (c.constructor == ns[c.xtype]) {// already created...
313                 return c;
314             }
315             if (ns[c.xtype]) {
316                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
317                 var ret = new ns[c.xtype](c);
318                 ret.xns = false;
319                 return ret;
320             }
321             c.xns = false; // prevent recursion..
322             return c;
323         },
324          /**
325          * Logs to console if it can.
326          *
327          * @param {String|Object} string
328          * @method log
329          */
330         log : function(s)
331         {
332             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
333                 return; // alerT?
334             }
335             console.log(s);
336             
337         },
338         /**
339          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
340          * @param {Object} o
341          * @return {String}
342          */
343         urlEncode : function(o){
344             if(!o){
345                 return "";
346             }
347             var buf = [];
348             for(var key in o){
349                 var ov = o[key], k = Roo.encodeURIComponent(key);
350                 var type = typeof ov;
351                 if(type == 'undefined'){
352                     buf.push(k, "=&");
353                 }else if(type != "function" && type != "object"){
354                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
355                 }else if(ov instanceof Array){
356                     if (ov.length) {
357                             for(var i = 0, len = ov.length; i < len; i++) {
358                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
359                             }
360                         } else {
361                             buf.push(k, "=&");
362                         }
363                 }
364             }
365             buf.pop();
366             return buf.join("");
367         },
368          /**
369          * Safe version of encodeURIComponent
370          * @param {String} data 
371          * @return {String} 
372          */
373         
374         encodeURIComponent : function (data)
375         {
376             try {
377                 return encodeURIComponent(data);
378             } catch(e) {} // should be an uri encode error.
379             
380             if (data == '' || data == null){
381                return '';
382             }
383             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
384             function nibble_to_hex(nibble){
385                 var chars = '0123456789ABCDEF';
386                 return chars.charAt(nibble);
387             }
388             data = data.toString();
389             var buffer = '';
390             for(var i=0; i<data.length; i++){
391                 var c = data.charCodeAt(i);
392                 var bs = new Array();
393                 if (c > 0x10000){
394                         // 4 bytes
395                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
396                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
397                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
398                     bs[3] = 0x80 | (c & 0x3F);
399                 }else if (c > 0x800){
400                          // 3 bytes
401                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
402                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
403                     bs[2] = 0x80 | (c & 0x3F);
404                 }else if (c > 0x80){
405                        // 2 bytes
406                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
407                     bs[1] = 0x80 | (c & 0x3F);
408                 }else{
409                         // 1 byte
410                     bs[0] = c;
411                 }
412                 for(var j=0; j<bs.length; j++){
413                     var b = bs[j];
414                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
415                             + nibble_to_hex(b &0x0F);
416                     buffer += '%'+hex;
417                }
418             }
419             return buffer;    
420              
421         },
422
423         /**
424          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
425          * @param {String} string
426          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
427          * @return {Object} A literal with members
428          */
429         urlDecode : function(string, overwrite){
430             if(!string || !string.length){
431                 return {};
432             }
433             var obj = {};
434             var pairs = string.split('&');
435             var pair, name, value;
436             for(var i = 0, len = pairs.length; i < len; i++){
437                 pair = pairs[i].split('=');
438                 name = decodeURIComponent(pair[0]);
439                 value = decodeURIComponent(pair[1]);
440                 if(overwrite !== true){
441                     if(typeof obj[name] == "undefined"){
442                         obj[name] = value;
443                     }else if(typeof obj[name] == "string"){
444                         obj[name] = [obj[name]];
445                         obj[name].push(value);
446                     }else{
447                         obj[name].push(value);
448                     }
449                 }else{
450                     obj[name] = value;
451                 }
452             }
453             return obj;
454         },
455
456         /**
457          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
458          * passed array is not really an array, your function is called once with it.
459          * The supplied function is called with (Object item, Number index, Array allItems).
460          * @param {Array/NodeList/Mixed} array
461          * @param {Function} fn
462          * @param {Object} scope
463          */
464         each : function(array, fn, scope){
465             if(typeof array.length == "undefined" || typeof array == "string"){
466                 array = [array];
467             }
468             for(var i = 0, len = array.length; i < len; i++){
469                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
470             }
471         },
472
473         // deprecated
474         combine : function(){
475             var as = arguments, l = as.length, r = [];
476             for(var i = 0; i < l; i++){
477                 var a = as[i];
478                 if(a instanceof Array){
479                     r = r.concat(a);
480                 }else if(a.length !== undefined && !a.substr){
481                     r = r.concat(Array.prototype.slice.call(a, 0));
482                 }else{
483                     r.push(a);
484                 }
485             }
486             return r;
487         },
488
489         /**
490          * Escapes the passed string for use in a regular expression
491          * @param {String} str
492          * @return {String}
493          */
494         escapeRe : function(s) {
495             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
496         },
497
498         // internal
499         callback : function(cb, scope, args, delay){
500             if(typeof cb == "function"){
501                 if(delay){
502                     cb.defer(delay, scope, args || []);
503                 }else{
504                     cb.apply(scope, args || []);
505                 }
506             }
507         },
508
509         /**
510          * Return the dom node for the passed string (id), dom node, or Roo.Element
511          * @param {String/HTMLElement/Roo.Element} el
512          * @return HTMLElement
513          */
514         getDom : function(el){
515             if(!el){
516                 return null;
517             }
518             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
519         },
520
521         /**
522         * Shorthand for {@link Roo.ComponentMgr#get}
523         * @param {String} id
524         * @return Roo.Component
525         */
526         getCmp : function(id){
527             return Roo.ComponentMgr.get(id);
528         },
529          
530         num : function(v, defaultValue){
531             if(typeof v != 'number'){
532                 return defaultValue;
533             }
534             return v;
535         },
536
537         destroy : function(){
538             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
539                 var as = a[i];
540                 if(as){
541                     if(as.dom){
542                         as.removeAllListeners();
543                         as.remove();
544                         continue;
545                     }
546                     if(typeof as.purgeListeners == 'function'){
547                         as.purgeListeners();
548                     }
549                     if(typeof as.destroy == 'function'){
550                         as.destroy();
551                     }
552                 }
553             }
554         },
555
556         // inpired by a similar function in mootools library
557         /**
558          * Returns the type of object that is passed in. If the object passed in is null or undefined it
559          * return false otherwise it returns one of the following values:<ul>
560          * <li><b>string</b>: If the object passed is a string</li>
561          * <li><b>number</b>: If the object passed is a number</li>
562          * <li><b>boolean</b>: If the object passed is a boolean value</li>
563          * <li><b>function</b>: If the object passed is a function reference</li>
564          * <li><b>object</b>: If the object passed is an object</li>
565          * <li><b>array</b>: If the object passed is an array</li>
566          * <li><b>regexp</b>: If the object passed is a regular expression</li>
567          * <li><b>element</b>: If the object passed is a DOM Element</li>
568          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
569          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
570          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
571          * @param {Mixed} object
572          * @return {String}
573          */
574         type : function(o){
575             if(o === undefined || o === null){
576                 return false;
577             }
578             if(o.htmlElement){
579                 return 'element';
580             }
581             var t = typeof o;
582             if(t == 'object' && o.nodeName) {
583                 switch(o.nodeType) {
584                     case 1: return 'element';
585                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
586                 }
587             }
588             if(t == 'object' || t == 'function') {
589                 switch(o.constructor) {
590                     case Array: return 'array';
591                     case RegExp: return 'regexp';
592                 }
593                 if(typeof o.length == 'number' && typeof o.item == 'function') {
594                     return 'nodelist';
595                 }
596             }
597             return t;
598         },
599
600         /**
601          * Returns true if the passed value is null, undefined or an empty string (optional).
602          * @param {Mixed} value The value to test
603          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
604          * @return {Boolean}
605          */
606         isEmpty : function(v, allowBlank){
607             return v === null || v === undefined || (!allowBlank ? v === '' : false);
608         },
609         
610         /** @type Boolean */
611         isOpera : isOpera,
612         /** @type Boolean */
613         isSafari : isSafari,
614         /** @type Boolean */
615         isFirefox : isFirefox,
616         /** @type Boolean */
617         isIE : isIE,
618         /** @type Boolean */
619         isIE7 : isIE7,
620         /** @type Boolean */
621         isIE11 : isIE11,
622         /** @type Boolean */
623         isGecko : isGecko,
624         /** @type Boolean */
625         isBorderBox : isBorderBox,
626         /** @type Boolean */
627         isWindows : isWindows,
628         /** @type Boolean */
629         isLinux : isLinux,
630         /** @type Boolean */
631         isMac : isMac,
632         /** @type Boolean */
633         isTouch : isTouch,
634
635         /**
636          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
637          * you may want to set this to true.
638          * @type Boolean
639          */
640         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
641         
642         
643                 
644         /**
645          * Selects a single element as a Roo Element
646          * This is about as close as you can get to jQuery's $('do crazy stuff')
647          * @param {String} selector The selector/xpath query
648          * @param {Node} root (optional) The start of the query (defaults to document).
649          * @return {Roo.Element}
650          */
651         selectNode : function(selector, root) 
652         {
653             var node = Roo.DomQuery.selectNode(selector,root);
654             return node ? Roo.get(node) : new Roo.Element(false);
655         }
656         
657     });
658
659
660 })();
661
662 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
663                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
664                 "Roo.app", "Roo.ux",
665                 "Roo.bootstrap",
666                 "Roo.bootstrap.dash");
667 /*
668  * Based on:
669  * Ext JS Library 1.1.1
670  * Copyright(c) 2006-2007, Ext JS, LLC.
671  *
672  * Originally Released Under LGPL - original licence link has changed is not relivant.
673  *
674  * Fork - LGPL
675  * <script type="text/javascript">
676  */
677
678 (function() {    
679     // wrappedn so fnCleanup is not in global scope...
680     if(Roo.isIE) {
681         function fnCleanUp() {
682             var p = Function.prototype;
683             delete p.createSequence;
684             delete p.defer;
685             delete p.createDelegate;
686             delete p.createCallback;
687             delete p.createInterceptor;
688
689             window.detachEvent("onunload", fnCleanUp);
690         }
691         window.attachEvent("onunload", fnCleanUp);
692     }
693 })();
694
695
696 /**
697  * @class Function
698  * These functions are available on every Function object (any JavaScript function).
699  */
700 Roo.apply(Function.prototype, {
701      /**
702      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
703      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
704      * Will create a function that is bound to those 2 args.
705      * @return {Function} The new function
706     */
707     createCallback : function(/*args...*/){
708         // make args available, in function below
709         var args = arguments;
710         var method = this;
711         return function() {
712             return method.apply(window, args);
713         };
714     },
715
716     /**
717      * Creates a delegate (callback) that sets the scope to obj.
718      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
719      * Will create a function that is automatically scoped to this.
720      * @param {Object} obj (optional) The object for which the scope is set
721      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
722      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
723      *                                             if a number the args are inserted at the specified position
724      * @return {Function} The new function
725      */
726     createDelegate : function(obj, args, appendArgs){
727         var method = this;
728         return function() {
729             var callArgs = args || arguments;
730             if(appendArgs === true){
731                 callArgs = Array.prototype.slice.call(arguments, 0);
732                 callArgs = callArgs.concat(args);
733             }else if(typeof appendArgs == "number"){
734                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
735                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
736                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
737             }
738             return method.apply(obj || window, callArgs);
739         };
740     },
741
742     /**
743      * Calls this function after the number of millseconds specified.
744      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
745      * @param {Object} obj (optional) The object for which the scope is set
746      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
747      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
748      *                                             if a number the args are inserted at the specified position
749      * @return {Number} The timeout id that can be used with clearTimeout
750      */
751     defer : function(millis, obj, args, appendArgs){
752         var fn = this.createDelegate(obj, args, appendArgs);
753         if(millis){
754             return setTimeout(fn, millis);
755         }
756         fn();
757         return 0;
758     },
759     /**
760      * Create a combined function call sequence of the original function + the passed function.
761      * The resulting function returns the results of the original function.
762      * The passed fcn is called with the parameters of the original function
763      * @param {Function} fcn The function to sequence
764      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
765      * @return {Function} The new function
766      */
767     createSequence : function(fcn, scope){
768         if(typeof fcn != "function"){
769             return this;
770         }
771         var method = this;
772         return function() {
773             var retval = method.apply(this || window, arguments);
774             fcn.apply(scope || this || window, arguments);
775             return retval;
776         };
777     },
778
779     /**
780      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
781      * The resulting function returns the results of the original function.
782      * The passed fcn is called with the parameters of the original function.
783      * @addon
784      * @param {Function} fcn The function to call before the original
785      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
786      * @return {Function} The new function
787      */
788     createInterceptor : function(fcn, scope){
789         if(typeof fcn != "function"){
790             return this;
791         }
792         var method = this;
793         return function() {
794             fcn.target = this;
795             fcn.method = method;
796             if(fcn.apply(scope || this || window, arguments) === false){
797                 return;
798             }
799             return method.apply(this || window, arguments);
800         };
801     }
802 });
803 /*
804  * Based on:
805  * Ext JS Library 1.1.1
806  * Copyright(c) 2006-2007, Ext JS, LLC.
807  *
808  * Originally Released Under LGPL - original licence link has changed is not relivant.
809  *
810  * Fork - LGPL
811  * <script type="text/javascript">
812  */
813
814 Roo.applyIf(String, {
815     
816     /** @scope String */
817     
818     /**
819      * Escapes the passed string for ' and \
820      * @param {String} string The string to escape
821      * @return {String} The escaped string
822      * @static
823      */
824     escape : function(string) {
825         return string.replace(/('|\\)/g, "\\$1");
826     },
827
828     /**
829      * Pads the left side of a string with a specified character.  This is especially useful
830      * for normalizing number and date strings.  Example usage:
831      * <pre><code>
832 var s = String.leftPad('123', 5, '0');
833 // s now contains the string: '00123'
834 </code></pre>
835      * @param {String} string The original string
836      * @param {Number} size The total length of the output string
837      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
838      * @return {String} The padded string
839      * @static
840      */
841     leftPad : function (val, size, ch) {
842         var result = new String(val);
843         if(ch === null || ch === undefined || ch === '') {
844             ch = " ";
845         }
846         while (result.length < size) {
847             result = ch + result;
848         }
849         return result;
850     },
851
852     /**
853      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
854      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
855      * <pre><code>
856 var cls = 'my-class', text = 'Some text';
857 var s = String.format('<div class="{0}">{1}</div>', cls, text);
858 // s now contains the string: '<div class="my-class">Some text</div>'
859 </code></pre>
860      * @param {String} string The tokenized string to be formatted
861      * @param {String} value1 The value to replace token {0}
862      * @param {String} value2 Etc...
863      * @return {String} The formatted string
864      * @static
865      */
866     format : function(format){
867         var args = Array.prototype.slice.call(arguments, 1);
868         return format.replace(/\{(\d+)\}/g, function(m, i){
869             return Roo.util.Format.htmlEncode(args[i]);
870         });
871     }
872 });
873
874 /**
875  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
876  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
877  * they are already different, the first value passed in is returned.  Note that this method returns the new value
878  * but does not change the current string.
879  * <pre><code>
880 // alternate sort directions
881 sort = sort.toggle('ASC', 'DESC');
882
883 // instead of conditional logic:
884 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
885 </code></pre>
886  * @param {String} value The value to compare to the current string
887  * @param {String} other The new value to use if the string already equals the first value passed in
888  * @return {String} The new value
889  */
890  
891 String.prototype.toggle = function(value, other){
892     return this == value ? other : value;
893 };/*
894  * Based on:
895  * Ext JS Library 1.1.1
896  * Copyright(c) 2006-2007, Ext JS, LLC.
897  *
898  * Originally Released Under LGPL - original licence link has changed is not relivant.
899  *
900  * Fork - LGPL
901  * <script type="text/javascript">
902  */
903
904  /**
905  * @class Number
906  */
907 Roo.applyIf(Number.prototype, {
908     /**
909      * Checks whether or not the current number is within a desired range.  If the number is already within the
910      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
911      * exceeded.  Note that this method returns the constrained value but does not change the current number.
912      * @param {Number} min The minimum number in the range
913      * @param {Number} max The maximum number in the range
914      * @return {Number} The constrained value if outside the range, otherwise the current value
915      */
916     constrain : function(min, max){
917         return Math.min(Math.max(this, min), max);
918     }
919 });/*
920  * Based on:
921  * Ext JS Library 1.1.1
922  * Copyright(c) 2006-2007, Ext JS, LLC.
923  *
924  * Originally Released Under LGPL - original licence link has changed is not relivant.
925  *
926  * Fork - LGPL
927  * <script type="text/javascript">
928  */
929  /**
930  * @class Array
931  */
932 Roo.applyIf(Array.prototype, {
933     /**
934      * 
935      * Checks whether or not the specified object exists in the array.
936      * @param {Object} o The object to check for
937      * @return {Number} The index of o in the array (or -1 if it is not found)
938      */
939     indexOf : function(o){
940        for (var i = 0, len = this.length; i < len; i++){
941               if(this[i] == o) return i;
942        }
943            return -1;
944     },
945
946     /**
947      * Removes the specified object from the array.  If the object is not found nothing happens.
948      * @param {Object} o The object to remove
949      */
950     remove : function(o){
951        var index = this.indexOf(o);
952        if(index != -1){
953            this.splice(index, 1);
954        }
955     },
956     /**
957      * Map (JS 1.6 compatibility)
958      * @param {Function} function  to call
959      */
960     map : function(fun )
961     {
962         var len = this.length >>> 0;
963         if (typeof fun != "function")
964             throw new TypeError();
965
966         var res = new Array(len);
967         var thisp = arguments[1];
968         for (var i = 0; i < len; i++)
969         {
970             if (i in this)
971                 res[i] = fun.call(thisp, this[i], i, this);
972         }
973
974         return res;
975     }
976     
977 });
978
979
980  /*
981  * Based on:
982  * Ext JS Library 1.1.1
983  * Copyright(c) 2006-2007, Ext JS, LLC.
984  *
985  * Originally Released Under LGPL - original licence link has changed is not relivant.
986  *
987  * Fork - LGPL
988  * <script type="text/javascript">
989  */
990
991 /**
992  * @class Date
993  *
994  * The date parsing and format syntax is a subset of
995  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
996  * supported will provide results equivalent to their PHP versions.
997  *
998  * Following is the list of all currently supported formats:
999  *<pre>
1000 Sample date:
1001 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1002
1003 Format  Output      Description
1004 ------  ----------  --------------------------------------------------------------
1005   d      10         Day of the month, 2 digits with leading zeros
1006   D      Wed        A textual representation of a day, three letters
1007   j      10         Day of the month without leading zeros
1008   l      Wednesday  A full textual representation of the day of the week
1009   S      th         English ordinal day of month suffix, 2 chars (use with j)
1010   w      3          Numeric representation of the day of the week
1011   z      9          The julian date, or day of the year (0-365)
1012   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1013   F      January    A full textual representation of the month
1014   m      01         Numeric representation of a month, with leading zeros
1015   M      Jan        Month name abbreviation, three letters
1016   n      1          Numeric representation of a month, without leading zeros
1017   t      31         Number of days in the given month
1018   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1019   Y      2007       A full numeric representation of a year, 4 digits
1020   y      07         A two digit representation of a year
1021   a      pm         Lowercase Ante meridiem and Post meridiem
1022   A      PM         Uppercase Ante meridiem and Post meridiem
1023   g      3          12-hour format of an hour without leading zeros
1024   G      15         24-hour format of an hour without leading zeros
1025   h      03         12-hour format of an hour with leading zeros
1026   H      15         24-hour format of an hour with leading zeros
1027   i      05         Minutes with leading zeros
1028   s      01         Seconds, with leading zeros
1029   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1030   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1031   T      CST        Timezone setting of the machine running the code
1032   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1033 </pre>
1034  *
1035  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1036  * <pre><code>
1037 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1038 document.write(dt.format('Y-m-d'));                         //2007-01-10
1039 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1040 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1041  </code></pre>
1042  *
1043  * Here are some standard date/time patterns that you might find helpful.  They
1044  * are not part of the source of Date.js, but to use them you can simply copy this
1045  * block of code into any script that is included after Date.js and they will also become
1046  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1047  * <pre><code>
1048 Date.patterns = {
1049     ISO8601Long:"Y-m-d H:i:s",
1050     ISO8601Short:"Y-m-d",
1051     ShortDate: "n/j/Y",
1052     LongDate: "l, F d, Y",
1053     FullDateTime: "l, F d, Y g:i:s A",
1054     MonthDay: "F d",
1055     ShortTime: "g:i A",
1056     LongTime: "g:i:s A",
1057     SortableDateTime: "Y-m-d\\TH:i:s",
1058     UniversalSortableDateTime: "Y-m-d H:i:sO",
1059     YearMonth: "F, Y"
1060 };
1061 </code></pre>
1062  *
1063  * Example usage:
1064  * <pre><code>
1065 var dt = new Date();
1066 document.write(dt.format(Date.patterns.ShortDate));
1067  </code></pre>
1068  */
1069
1070 /*
1071  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1072  * They generate precompiled functions from date formats instead of parsing and
1073  * processing the pattern every time you format a date.  These functions are available
1074  * on every Date object (any javascript function).
1075  *
1076  * The original article and download are here:
1077  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1078  *
1079  */
1080  
1081  
1082  // was in core
1083 /**
1084  Returns the number of milliseconds between this date and date
1085  @param {Date} date (optional) Defaults to now
1086  @return {Number} The diff in milliseconds
1087  @member Date getElapsed
1088  */
1089 Date.prototype.getElapsed = function(date) {
1090         return Math.abs((date || new Date()).getTime()-this.getTime());
1091 };
1092 // was in date file..
1093
1094
1095 // private
1096 Date.parseFunctions = {count:0};
1097 // private
1098 Date.parseRegexes = [];
1099 // private
1100 Date.formatFunctions = {count:0};
1101
1102 // private
1103 Date.prototype.dateFormat = function(format) {
1104     if (Date.formatFunctions[format] == null) {
1105         Date.createNewFormat(format);
1106     }
1107     var func = Date.formatFunctions[format];
1108     return this[func]();
1109 };
1110
1111
1112 /**
1113  * Formats a date given the supplied format string
1114  * @param {String} format The format string
1115  * @return {String} The formatted date
1116  * @method
1117  */
1118 Date.prototype.format = Date.prototype.dateFormat;
1119
1120 // private
1121 Date.createNewFormat = function(format) {
1122     var funcName = "format" + Date.formatFunctions.count++;
1123     Date.formatFunctions[format] = funcName;
1124     var code = "Date.prototype." + funcName + " = function(){return ";
1125     var special = false;
1126     var ch = '';
1127     for (var i = 0; i < format.length; ++i) {
1128         ch = format.charAt(i);
1129         if (!special && ch == "\\") {
1130             special = true;
1131         }
1132         else if (special) {
1133             special = false;
1134             code += "'" + String.escape(ch) + "' + ";
1135         }
1136         else {
1137             code += Date.getFormatCode(ch);
1138         }
1139     }
1140     /** eval:var:zzzzzzzzzzzzz */
1141     eval(code.substring(0, code.length - 3) + ";}");
1142 };
1143
1144 // private
1145 Date.getFormatCode = function(character) {
1146     switch (character) {
1147     case "d":
1148         return "String.leftPad(this.getDate(), 2, '0') + ";
1149     case "D":
1150         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1151     case "j":
1152         return "this.getDate() + ";
1153     case "l":
1154         return "Date.dayNames[this.getDay()] + ";
1155     case "S":
1156         return "this.getSuffix() + ";
1157     case "w":
1158         return "this.getDay() + ";
1159     case "z":
1160         return "this.getDayOfYear() + ";
1161     case "W":
1162         return "this.getWeekOfYear() + ";
1163     case "F":
1164         return "Date.monthNames[this.getMonth()] + ";
1165     case "m":
1166         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1167     case "M":
1168         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1169     case "n":
1170         return "(this.getMonth() + 1) + ";
1171     case "t":
1172         return "this.getDaysInMonth() + ";
1173     case "L":
1174         return "(this.isLeapYear() ? 1 : 0) + ";
1175     case "Y":
1176         return "this.getFullYear() + ";
1177     case "y":
1178         return "('' + this.getFullYear()).substring(2, 4) + ";
1179     case "a":
1180         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1181     case "A":
1182         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1183     case "g":
1184         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1185     case "G":
1186         return "this.getHours() + ";
1187     case "h":
1188         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1189     case "H":
1190         return "String.leftPad(this.getHours(), 2, '0') + ";
1191     case "i":
1192         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1193     case "s":
1194         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1195     case "O":
1196         return "this.getGMTOffset() + ";
1197     case "P":
1198         return "this.getGMTColonOffset() + ";
1199     case "T":
1200         return "this.getTimezone() + ";
1201     case "Z":
1202         return "(this.getTimezoneOffset() * -60) + ";
1203     default:
1204         return "'" + String.escape(character) + "' + ";
1205     }
1206 };
1207
1208 /**
1209  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1210  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1211  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1212  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1213  * string or the parse operation will fail.
1214  * Example Usage:
1215 <pre><code>
1216 //dt = Fri May 25 2007 (current date)
1217 var dt = new Date();
1218
1219 //dt = Thu May 25 2006 (today's month/day in 2006)
1220 dt = Date.parseDate("2006", "Y");
1221
1222 //dt = Sun Jan 15 2006 (all date parts specified)
1223 dt = Date.parseDate("2006-1-15", "Y-m-d");
1224
1225 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1226 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1227 </code></pre>
1228  * @param {String} input The unparsed date as a string
1229  * @param {String} format The format the date is in
1230  * @return {Date} The parsed date
1231  * @static
1232  */
1233 Date.parseDate = function(input, format) {
1234     if (Date.parseFunctions[format] == null) {
1235         Date.createParser(format);
1236     }
1237     var func = Date.parseFunctions[format];
1238     return Date[func](input);
1239 };
1240 /**
1241  * @private
1242  */
1243
1244 Date.createParser = function(format) {
1245     var funcName = "parse" + Date.parseFunctions.count++;
1246     var regexNum = Date.parseRegexes.length;
1247     var currentGroup = 1;
1248     Date.parseFunctions[format] = funcName;
1249
1250     var code = "Date." + funcName + " = function(input){\n"
1251         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1252         + "var d = new Date();\n"
1253         + "y = d.getFullYear();\n"
1254         + "m = d.getMonth();\n"
1255         + "d = d.getDate();\n"
1256         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1257         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1258         + "if (results && results.length > 0) {";
1259     var regex = "";
1260
1261     var special = false;
1262     var ch = '';
1263     for (var i = 0; i < format.length; ++i) {
1264         ch = format.charAt(i);
1265         if (!special && ch == "\\") {
1266             special = true;
1267         }
1268         else if (special) {
1269             special = false;
1270             regex += String.escape(ch);
1271         }
1272         else {
1273             var obj = Date.formatCodeToRegex(ch, currentGroup);
1274             currentGroup += obj.g;
1275             regex += obj.s;
1276             if (obj.g && obj.c) {
1277                 code += obj.c;
1278             }
1279         }
1280     }
1281
1282     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1283         + "{v = new Date(y, m, d, h, i, s);}\n"
1284         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1285         + "{v = new Date(y, m, d, h, i);}\n"
1286         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1287         + "{v = new Date(y, m, d, h);}\n"
1288         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1289         + "{v = new Date(y, m, d);}\n"
1290         + "else if (y >= 0 && m >= 0)\n"
1291         + "{v = new Date(y, m);}\n"
1292         + "else if (y >= 0)\n"
1293         + "{v = new Date(y);}\n"
1294         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1295         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1296         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1297         + ";}";
1298
1299     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1300     /** eval:var:zzzzzzzzzzzzz */
1301     eval(code);
1302 };
1303
1304 // private
1305 Date.formatCodeToRegex = function(character, currentGroup) {
1306     switch (character) {
1307     case "D":
1308         return {g:0,
1309         c:null,
1310         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1311     case "j":
1312         return {g:1,
1313             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1314             s:"(\\d{1,2})"}; // day of month without leading zeroes
1315     case "d":
1316         return {g:1,
1317             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1318             s:"(\\d{2})"}; // day of month with leading zeroes
1319     case "l":
1320         return {g:0,
1321             c:null,
1322             s:"(?:" + Date.dayNames.join("|") + ")"};
1323     case "S":
1324         return {g:0,
1325             c:null,
1326             s:"(?:st|nd|rd|th)"};
1327     case "w":
1328         return {g:0,
1329             c:null,
1330             s:"\\d"};
1331     case "z":
1332         return {g:0,
1333             c:null,
1334             s:"(?:\\d{1,3})"};
1335     case "W":
1336         return {g:0,
1337             c:null,
1338             s:"(?:\\d{2})"};
1339     case "F":
1340         return {g:1,
1341             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1342             s:"(" + Date.monthNames.join("|") + ")"};
1343     case "M":
1344         return {g:1,
1345             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1346             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1347     case "n":
1348         return {g:1,
1349             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1350             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1351     case "m":
1352         return {g:1,
1353             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1354             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1355     case "t":
1356         return {g:0,
1357             c:null,
1358             s:"\\d{1,2}"};
1359     case "L":
1360         return {g:0,
1361             c:null,
1362             s:"(?:1|0)"};
1363     case "Y":
1364         return {g:1,
1365             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{4})"};
1367     case "y":
1368         return {g:1,
1369             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1370                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1371             s:"(\\d{1,2})"};
1372     case "a":
1373         return {g:1,
1374             c:"if (results[" + currentGroup + "] == 'am') {\n"
1375                 + "if (h == 12) { h = 0; }\n"
1376                 + "} else { if (h < 12) { h += 12; }}",
1377             s:"(am|pm)"};
1378     case "A":
1379         return {g:1,
1380             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1381                 + "if (h == 12) { h = 0; }\n"
1382                 + "} else { if (h < 12) { h += 12; }}",
1383             s:"(AM|PM)"};
1384     case "g":
1385     case "G":
1386         return {g:1,
1387             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1389     case "h":
1390     case "H":
1391         return {g:1,
1392             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1394     case "i":
1395         return {g:1,
1396             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1397             s:"(\\d{2})"};
1398     case "s":
1399         return {g:1,
1400             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1401             s:"(\\d{2})"};
1402     case "O":
1403         return {g:1,
1404             c:[
1405                 "o = results[", currentGroup, "];\n",
1406                 "var sn = o.substring(0,1);\n", // get + / - sign
1407                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1408                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1409                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1410                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1411             ].join(""),
1412             s:"([+\-]\\d{2,4})"};
1413     
1414     
1415     case "P":
1416         return {g:1,
1417                 c:[
1418                    "o = results[", currentGroup, "];\n",
1419                    "var sn = o.substring(0,1);\n",
1420                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1421                    "var mn = o.substring(4,6) % 60;\n",
1422                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1423                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1424             ].join(""),
1425             s:"([+\-]\\d{4})"};
1426     case "T":
1427         return {g:0,
1428             c:null,
1429             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1430     case "Z":
1431         return {g:1,
1432             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1433                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1434             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1435     default:
1436         return {g:0,
1437             c:null,
1438             s:String.escape(character)};
1439     }
1440 };
1441
1442 /**
1443  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1444  * @return {String} The abbreviated timezone name (e.g. 'CST')
1445  */
1446 Date.prototype.getTimezone = function() {
1447     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1448 };
1449
1450 /**
1451  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1452  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1453  */
1454 Date.prototype.getGMTOffset = function() {
1455     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1456         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1457         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1458 };
1459
1460 /**
1461  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1462  * @return {String} 2-characters representing hours and 2-characters representing minutes
1463  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1464  */
1465 Date.prototype.getGMTColonOffset = function() {
1466         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1467                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1468                 + ":"
1469                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1470 }
1471
1472 /**
1473  * Get the numeric day number of the year, adjusted for leap year.
1474  * @return {Number} 0 through 364 (365 in leap years)
1475  */
1476 Date.prototype.getDayOfYear = function() {
1477     var num = 0;
1478     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1479     for (var i = 0; i < this.getMonth(); ++i) {
1480         num += Date.daysInMonth[i];
1481     }
1482     return num + this.getDate() - 1;
1483 };
1484
1485 /**
1486  * Get the string representation of the numeric week number of the year
1487  * (equivalent to the format specifier 'W').
1488  * @return {String} '00' through '52'
1489  */
1490 Date.prototype.getWeekOfYear = function() {
1491     // Skip to Thursday of this week
1492     var now = this.getDayOfYear() + (4 - this.getDay());
1493     // Find the first Thursday of the year
1494     var jan1 = new Date(this.getFullYear(), 0, 1);
1495     var then = (7 - jan1.getDay() + 4);
1496     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1497 };
1498
1499 /**
1500  * Whether or not the current date is in a leap year.
1501  * @return {Boolean} True if the current date is in a leap year, else false
1502  */
1503 Date.prototype.isLeapYear = function() {
1504     var year = this.getFullYear();
1505     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1506 };
1507
1508 /**
1509  * Get the first day of the current month, adjusted for leap year.  The returned value
1510  * is the numeric day index within the week (0-6) which can be used in conjunction with
1511  * the {@link #monthNames} array to retrieve the textual day name.
1512  * Example:
1513  *<pre><code>
1514 var dt = new Date('1/10/2007');
1515 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1516 </code></pre>
1517  * @return {Number} The day number (0-6)
1518  */
1519 Date.prototype.getFirstDayOfMonth = function() {
1520     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1521     return (day < 0) ? (day + 7) : day;
1522 };
1523
1524 /**
1525  * Get the last day of the current month, adjusted for leap year.  The returned value
1526  * is the numeric day index within the week (0-6) which can be used in conjunction with
1527  * the {@link #monthNames} array to retrieve the textual day name.
1528  * Example:
1529  *<pre><code>
1530 var dt = new Date('1/10/2007');
1531 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1532 </code></pre>
1533  * @return {Number} The day number (0-6)
1534  */
1535 Date.prototype.getLastDayOfMonth = function() {
1536     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1537     return (day < 0) ? (day + 7) : day;
1538 };
1539
1540
1541 /**
1542  * Get the first date of this date's month
1543  * @return {Date}
1544  */
1545 Date.prototype.getFirstDateOfMonth = function() {
1546     return new Date(this.getFullYear(), this.getMonth(), 1);
1547 };
1548
1549 /**
1550  * Get the last date of this date's month
1551  * @return {Date}
1552  */
1553 Date.prototype.getLastDateOfMonth = function() {
1554     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1555 };
1556 /**
1557  * Get the number of days in the current month, adjusted for leap year.
1558  * @return {Number} The number of days in the month
1559  */
1560 Date.prototype.getDaysInMonth = function() {
1561     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1562     return Date.daysInMonth[this.getMonth()];
1563 };
1564
1565 /**
1566  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1567  * @return {String} 'st, 'nd', 'rd' or 'th'
1568  */
1569 Date.prototype.getSuffix = function() {
1570     switch (this.getDate()) {
1571         case 1:
1572         case 21:
1573         case 31:
1574             return "st";
1575         case 2:
1576         case 22:
1577             return "nd";
1578         case 3:
1579         case 23:
1580             return "rd";
1581         default:
1582             return "th";
1583     }
1584 };
1585
1586 // private
1587 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1588
1589 /**
1590  * An array of textual month names.
1591  * Override these values for international dates, for example...
1592  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1593  * @type Array
1594  * @static
1595  */
1596 Date.monthNames =
1597    ["January",
1598     "February",
1599     "March",
1600     "April",
1601     "May",
1602     "June",
1603     "July",
1604     "August",
1605     "September",
1606     "October",
1607     "November",
1608     "December"];
1609
1610 /**
1611  * An array of textual day names.
1612  * Override these values for international dates, for example...
1613  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1614  * @type Array
1615  * @static
1616  */
1617 Date.dayNames =
1618    ["Sunday",
1619     "Monday",
1620     "Tuesday",
1621     "Wednesday",
1622     "Thursday",
1623     "Friday",
1624     "Saturday"];
1625
1626 // private
1627 Date.y2kYear = 50;
1628 // private
1629 Date.monthNumbers = {
1630     Jan:0,
1631     Feb:1,
1632     Mar:2,
1633     Apr:3,
1634     May:4,
1635     Jun:5,
1636     Jul:6,
1637     Aug:7,
1638     Sep:8,
1639     Oct:9,
1640     Nov:10,
1641     Dec:11};
1642
1643 /**
1644  * Creates and returns a new Date instance with the exact same date value as the called instance.
1645  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1646  * variable will also be changed.  When the intention is to create a new variable that will not
1647  * modify the original instance, you should create a clone.
1648  *
1649  * Example of correctly cloning a date:
1650  * <pre><code>
1651 //wrong way:
1652 var orig = new Date('10/1/2006');
1653 var copy = orig;
1654 copy.setDate(5);
1655 document.write(orig);  //returns 'Thu Oct 05 2006'!
1656
1657 //correct way:
1658 var orig = new Date('10/1/2006');
1659 var copy = orig.clone();
1660 copy.setDate(5);
1661 document.write(orig);  //returns 'Thu Oct 01 2006'
1662 </code></pre>
1663  * @return {Date} The new Date instance
1664  */
1665 Date.prototype.clone = function() {
1666         return new Date(this.getTime());
1667 };
1668
1669 /**
1670  * Clears any time information from this date
1671  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1672  @return {Date} this or the clone
1673  */
1674 Date.prototype.clearTime = function(clone){
1675     if(clone){
1676         return this.clone().clearTime();
1677     }
1678     this.setHours(0);
1679     this.setMinutes(0);
1680     this.setSeconds(0);
1681     this.setMilliseconds(0);
1682     return this;
1683 };
1684
1685 // private
1686 // safari setMonth is broken
1687 if(Roo.isSafari){
1688     Date.brokenSetMonth = Date.prototype.setMonth;
1689         Date.prototype.setMonth = function(num){
1690                 if(num <= -1){
1691                         var n = Math.ceil(-num);
1692                         var back_year = Math.ceil(n/12);
1693                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1694                         this.setFullYear(this.getFullYear() - back_year);
1695                         return Date.brokenSetMonth.call(this, month);
1696                 } else {
1697                         return Date.brokenSetMonth.apply(this, arguments);
1698                 }
1699         };
1700 }
1701
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MILLI = "ms";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.SECOND = "s";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.MINUTE = "mi";
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.HOUR = "h";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.DAY = "d";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MONTH = "mo";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.YEAR = "y";
1730
1731 /**
1732  * Provides a convenient method of performing basic date arithmetic.  This method
1733  * does not modify the Date instance being called - it creates and returns
1734  * a new Date instance containing the resulting date value.
1735  *
1736  * Examples:
1737  * <pre><code>
1738 //Basic usage:
1739 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1740 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1741
1742 //Negative values will subtract correctly:
1743 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1744 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1745
1746 //You can even chain several calls together in one line!
1747 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1748 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1749  </code></pre>
1750  *
1751  * @param {String} interval   A valid date interval enum value
1752  * @param {Number} value      The amount to add to the current date
1753  * @return {Date} The new Date instance
1754  */
1755 Date.prototype.add = function(interval, value){
1756   var d = this.clone();
1757   if (!interval || value === 0) return d;
1758   switch(interval.toLowerCase()){
1759     case Date.MILLI:
1760       d.setMilliseconds(this.getMilliseconds() + value);
1761       break;
1762     case Date.SECOND:
1763       d.setSeconds(this.getSeconds() + value);
1764       break;
1765     case Date.MINUTE:
1766       d.setMinutes(this.getMinutes() + value);
1767       break;
1768     case Date.HOUR:
1769       d.setHours(this.getHours() + value);
1770       break;
1771     case Date.DAY:
1772       d.setDate(this.getDate() + value);
1773       break;
1774     case Date.MONTH:
1775       var day = this.getDate();
1776       if(day > 28){
1777           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1778       }
1779       d.setDate(day);
1780       d.setMonth(this.getMonth() + value);
1781       break;
1782     case Date.YEAR:
1783       d.setFullYear(this.getFullYear() + value);
1784       break;
1785   }
1786   return d;
1787 };
1788 /*
1789  * Based on:
1790  * Ext JS Library 1.1.1
1791  * Copyright(c) 2006-2007, Ext JS, LLC.
1792  *
1793  * Originally Released Under LGPL - original licence link has changed is not relivant.
1794  *
1795  * Fork - LGPL
1796  * <script type="text/javascript">
1797  */
1798
1799 /**
1800  * @class Roo.lib.Dom
1801  * @static
1802  * 
1803  * Dom utils (from YIU afaik)
1804  * 
1805  **/
1806 Roo.lib.Dom = {
1807     /**
1808      * Get the view width
1809      * @param {Boolean} full True will get the full document, otherwise it's the view width
1810      * @return {Number} The width
1811      */
1812      
1813     getViewWidth : function(full) {
1814         return full ? this.getDocumentWidth() : this.getViewportWidth();
1815     },
1816     /**
1817      * Get the view height
1818      * @param {Boolean} full True will get the full document, otherwise it's the view height
1819      * @return {Number} The height
1820      */
1821     getViewHeight : function(full) {
1822         return full ? this.getDocumentHeight() : this.getViewportHeight();
1823     },
1824
1825     getDocumentHeight: function() {
1826         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1827         return Math.max(scrollHeight, this.getViewportHeight());
1828     },
1829
1830     getDocumentWidth: function() {
1831         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1832         return Math.max(scrollWidth, this.getViewportWidth());
1833     },
1834
1835     getViewportHeight: function() {
1836         var height = self.innerHeight;
1837         var mode = document.compatMode;
1838
1839         if ((mode || Roo.isIE) && !Roo.isOpera) {
1840             height = (mode == "CSS1Compat") ?
1841                      document.documentElement.clientHeight :
1842                      document.body.clientHeight;
1843         }
1844
1845         return height;
1846     },
1847
1848     getViewportWidth: function() {
1849         var width = self.innerWidth;
1850         var mode = document.compatMode;
1851
1852         if (mode || Roo.isIE) {
1853             width = (mode == "CSS1Compat") ?
1854                     document.documentElement.clientWidth :
1855                     document.body.clientWidth;
1856         }
1857         return width;
1858     },
1859
1860     isAncestor : function(p, c) {
1861         p = Roo.getDom(p);
1862         c = Roo.getDom(c);
1863         if (!p || !c) {
1864             return false;
1865         }
1866
1867         if (p.contains && !Roo.isSafari) {
1868             return p.contains(c);
1869         } else if (p.compareDocumentPosition) {
1870             return !!(p.compareDocumentPosition(c) & 16);
1871         } else {
1872             var parent = c.parentNode;
1873             while (parent) {
1874                 if (parent == p) {
1875                     return true;
1876                 }
1877                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1878                     return false;
1879                 }
1880                 parent = parent.parentNode;
1881             }
1882             return false;
1883         }
1884     },
1885
1886     getRegion : function(el) {
1887         return Roo.lib.Region.getRegion(el);
1888     },
1889
1890     getY : function(el) {
1891         return this.getXY(el)[1];
1892     },
1893
1894     getX : function(el) {
1895         return this.getXY(el)[0];
1896     },
1897
1898     getXY : function(el) {
1899         var p, pe, b, scroll, bd = document.body;
1900         el = Roo.getDom(el);
1901         var fly = Roo.lib.AnimBase.fly;
1902         if (el.getBoundingClientRect) {
1903             b = el.getBoundingClientRect();
1904             scroll = fly(document).getScroll();
1905             return [b.left + scroll.left, b.top + scroll.top];
1906         }
1907         var x = 0, y = 0;
1908
1909         p = el;
1910
1911         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1912
1913         while (p) {
1914
1915             x += p.offsetLeft;
1916             y += p.offsetTop;
1917
1918             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1919                 hasAbsolute = true;
1920             }
1921
1922             if (Roo.isGecko) {
1923                 pe = fly(p);
1924
1925                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1926                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1927
1928
1929                 x += bl;
1930                 y += bt;
1931
1932
1933                 if (p != el && pe.getStyle('overflow') != 'visible') {
1934                     x += bl;
1935                     y += bt;
1936                 }
1937             }
1938             p = p.offsetParent;
1939         }
1940
1941         if (Roo.isSafari && hasAbsolute) {
1942             x -= bd.offsetLeft;
1943             y -= bd.offsetTop;
1944         }
1945
1946         if (Roo.isGecko && !hasAbsolute) {
1947             var dbd = fly(bd);
1948             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1949             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1950         }
1951
1952         p = el.parentNode;
1953         while (p && p != bd) {
1954             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1955                 x -= p.scrollLeft;
1956                 y -= p.scrollTop;
1957             }
1958             p = p.parentNode;
1959         }
1960         return [x, y];
1961     },
1962  
1963   
1964
1965
1966     setXY : function(el, xy) {
1967         el = Roo.fly(el, '_setXY');
1968         el.position();
1969         var pts = el.translatePoints(xy);
1970         if (xy[0] !== false) {
1971             el.dom.style.left = pts.left + "px";
1972         }
1973         if (xy[1] !== false) {
1974             el.dom.style.top = pts.top + "px";
1975         }
1976     },
1977
1978     setX : function(el, x) {
1979         this.setXY(el, [x, false]);
1980     },
1981
1982     setY : function(el, y) {
1983         this.setXY(el, [false, y]);
1984     }
1985 };
1986 /*
1987  * Portions of this file are based on pieces of Yahoo User Interface Library
1988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1989  * YUI licensed under the BSD License:
1990  * http://developer.yahoo.net/yui/license.txt
1991  * <script type="text/javascript">
1992  *
1993  */
1994
1995 Roo.lib.Event = function() {
1996     var loadComplete = false;
1997     var listeners = [];
1998     var unloadListeners = [];
1999     var retryCount = 0;
2000     var onAvailStack = [];
2001     var counter = 0;
2002     var lastError = null;
2003
2004     return {
2005         POLL_RETRYS: 200,
2006         POLL_INTERVAL: 20,
2007         EL: 0,
2008         TYPE: 1,
2009         FN: 2,
2010         WFN: 3,
2011         OBJ: 3,
2012         ADJ_SCOPE: 4,
2013         _interval: null,
2014
2015         startInterval: function() {
2016             if (!this._interval) {
2017                 var self = this;
2018                 var callback = function() {
2019                     self._tryPreloadAttach();
2020                 };
2021                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2022
2023             }
2024         },
2025
2026         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2027             onAvailStack.push({ id:         p_id,
2028                 fn:         p_fn,
2029                 obj:        p_obj,
2030                 override:   p_override,
2031                 checkReady: false    });
2032
2033             retryCount = this.POLL_RETRYS;
2034             this.startInterval();
2035         },
2036
2037
2038         addListener: function(el, eventName, fn) {
2039             el = Roo.getDom(el);
2040             if (!el || !fn) {
2041                 return false;
2042             }
2043
2044             if ("unload" == eventName) {
2045                 unloadListeners[unloadListeners.length] =
2046                 [el, eventName, fn];
2047                 return true;
2048             }
2049
2050             var wrappedFn = function(e) {
2051                 return fn(Roo.lib.Event.getEvent(e));
2052             };
2053
2054             var li = [el, eventName, fn, wrappedFn];
2055
2056             var index = listeners.length;
2057             listeners[index] = li;
2058
2059             this.doAdd(el, eventName, wrappedFn, false);
2060             return true;
2061
2062         },
2063
2064
2065         removeListener: function(el, eventName, fn) {
2066             var i, len;
2067
2068             el = Roo.getDom(el);
2069
2070             if(!fn) {
2071                 return this.purgeElement(el, false, eventName);
2072             }
2073
2074
2075             if ("unload" == eventName) {
2076
2077                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2078                     var li = unloadListeners[i];
2079                     if (li &&
2080                         li[0] == el &&
2081                         li[1] == eventName &&
2082                         li[2] == fn) {
2083                         unloadListeners.splice(i, 1);
2084                         return true;
2085                     }
2086                 }
2087
2088                 return false;
2089             }
2090
2091             var cacheItem = null;
2092
2093
2094             var index = arguments[3];
2095
2096             if ("undefined" == typeof index) {
2097                 index = this._getCacheIndex(el, eventName, fn);
2098             }
2099
2100             if (index >= 0) {
2101                 cacheItem = listeners[index];
2102             }
2103
2104             if (!el || !cacheItem) {
2105                 return false;
2106             }
2107
2108             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2109
2110             delete listeners[index][this.WFN];
2111             delete listeners[index][this.FN];
2112             listeners.splice(index, 1);
2113
2114             return true;
2115
2116         },
2117
2118
2119         getTarget: function(ev, resolveTextNode) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var t = ev.target || ev.srcElement;
2123             return this.resolveTextNode(t);
2124         },
2125
2126
2127         resolveTextNode: function(node) {
2128             if (Roo.isSafari && node && 3 == node.nodeType) {
2129                 return node.parentNode;
2130             } else {
2131                 return node;
2132             }
2133         },
2134
2135
2136         getPageX: function(ev) {
2137             ev = ev.browserEvent || ev;
2138             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2139             var x = ev.pageX;
2140             if (!x && 0 !== x) {
2141                 x = ev.clientX || 0;
2142
2143                 if (Roo.isIE) {
2144                     x += this.getScroll()[1];
2145                 }
2146             }
2147
2148             return x;
2149         },
2150
2151
2152         getPageY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             var y = ev.pageY;
2156             if (!y && 0 !== y) {
2157                 y = ev.clientY || 0;
2158
2159                 if (Roo.isIE) {
2160                     y += this.getScroll()[0];
2161                 }
2162             }
2163
2164
2165             return y;
2166         },
2167
2168
2169         getXY: function(ev) {
2170             ev = ev.browserEvent || ev;
2171             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2172             return [this.getPageX(ev), this.getPageY(ev)];
2173         },
2174
2175
2176         getRelatedTarget: function(ev) {
2177             ev = ev.browserEvent || ev;
2178             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2179             var t = ev.relatedTarget;
2180             if (!t) {
2181                 if (ev.type == "mouseout") {
2182                     t = ev.toElement;
2183                 } else if (ev.type == "mouseover") {
2184                     t = ev.fromElement;
2185                 }
2186             }
2187
2188             return this.resolveTextNode(t);
2189         },
2190
2191
2192         getTime: function(ev) {
2193             ev = ev.browserEvent || ev;
2194             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2195             if (!ev.time) {
2196                 var t = new Date().getTime();
2197                 try {
2198                     ev.time = t;
2199                 } catch(ex) {
2200                     this.lastError = ex;
2201                     return t;
2202                 }
2203             }
2204
2205             return ev.time;
2206         },
2207
2208
2209         stopEvent: function(ev) {
2210             this.stopPropagation(ev);
2211             this.preventDefault(ev);
2212         },
2213
2214
2215         stopPropagation: function(ev) {
2216             ev = ev.browserEvent || ev;
2217             if (ev.stopPropagation) {
2218                 ev.stopPropagation();
2219             } else {
2220                 ev.cancelBubble = true;
2221             }
2222         },
2223
2224
2225         preventDefault: function(ev) {
2226             ev = ev.browserEvent || ev;
2227             if(ev.preventDefault) {
2228                 ev.preventDefault();
2229             } else {
2230                 ev.returnValue = false;
2231             }
2232         },
2233
2234
2235         getEvent: function(e) {
2236             var ev = e || window.event;
2237             if (!ev) {
2238                 var c = this.getEvent.caller;
2239                 while (c) {
2240                     ev = c.arguments[0];
2241                     if (ev && Event == ev.constructor) {
2242                         break;
2243                     }
2244                     c = c.caller;
2245                 }
2246             }
2247             return ev;
2248         },
2249
2250
2251         getCharCode: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             return ev.charCode || ev.keyCode || 0;
2254         },
2255
2256
2257         _getCacheIndex: function(el, eventName, fn) {
2258             for (var i = 0,len = listeners.length; i < len; ++i) {
2259                 var li = listeners[i];
2260                 if (li &&
2261                     li[this.FN] == fn &&
2262                     li[this.EL] == el &&
2263                     li[this.TYPE] == eventName) {
2264                     return i;
2265                 }
2266             }
2267
2268             return -1;
2269         },
2270
2271
2272         elCache: {},
2273
2274
2275         getEl: function(id) {
2276             return document.getElementById(id);
2277         },
2278
2279
2280         clearCache: function() {
2281         },
2282
2283
2284         _load: function(e) {
2285             loadComplete = true;
2286             var EU = Roo.lib.Event;
2287
2288
2289             if (Roo.isIE) {
2290                 EU.doRemove(window, "load", EU._load);
2291             }
2292         },
2293
2294
2295         _tryPreloadAttach: function() {
2296
2297             if (this.locked) {
2298                 return false;
2299             }
2300
2301             this.locked = true;
2302
2303
2304             var tryAgain = !loadComplete;
2305             if (!tryAgain) {
2306                 tryAgain = (retryCount > 0);
2307             }
2308
2309
2310             var notAvail = [];
2311             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2312                 var item = onAvailStack[i];
2313                 if (item) {
2314                     var el = this.getEl(item.id);
2315
2316                     if (el) {
2317                         if (!item.checkReady ||
2318                             loadComplete ||
2319                             el.nextSibling ||
2320                             (document && document.body)) {
2321
2322                             var scope = el;
2323                             if (item.override) {
2324                                 if (item.override === true) {
2325                                     scope = item.obj;
2326                                 } else {
2327                                     scope = item.override;
2328                                 }
2329                             }
2330                             item.fn.call(scope, item.obj);
2331                             onAvailStack[i] = null;
2332                         }
2333                     } else {
2334                         notAvail.push(item);
2335                     }
2336                 }
2337             }
2338
2339             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2340
2341             if (tryAgain) {
2342
2343                 this.startInterval();
2344             } else {
2345                 clearInterval(this._interval);
2346                 this._interval = null;
2347             }
2348
2349             this.locked = false;
2350
2351             return true;
2352
2353         },
2354
2355
2356         purgeElement: function(el, recurse, eventName) {
2357             var elListeners = this.getListeners(el, eventName);
2358             if (elListeners) {
2359                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2360                     var l = elListeners[i];
2361                     this.removeListener(el, l.type, l.fn);
2362                 }
2363             }
2364
2365             if (recurse && el && el.childNodes) {
2366                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2367                     this.purgeElement(el.childNodes[i], recurse, eventName);
2368                 }
2369             }
2370         },
2371
2372
2373         getListeners: function(el, eventName) {
2374             var results = [], searchLists;
2375             if (!eventName) {
2376                 searchLists = [listeners, unloadListeners];
2377             } else if (eventName == "unload") {
2378                 searchLists = [unloadListeners];
2379             } else {
2380                 searchLists = [listeners];
2381             }
2382
2383             for (var j = 0; j < searchLists.length; ++j) {
2384                 var searchList = searchLists[j];
2385                 if (searchList && searchList.length > 0) {
2386                     for (var i = 0,len = searchList.length; i < len; ++i) {
2387                         var l = searchList[i];
2388                         if (l && l[this.EL] === el &&
2389                             (!eventName || eventName === l[this.TYPE])) {
2390                             results.push({
2391                                 type:   l[this.TYPE],
2392                                 fn:     l[this.FN],
2393                                 obj:    l[this.OBJ],
2394                                 adjust: l[this.ADJ_SCOPE],
2395                                 index:  i
2396                             });
2397                         }
2398                     }
2399                 }
2400             }
2401
2402             return (results.length) ? results : null;
2403         },
2404
2405
2406         _unload: function(e) {
2407
2408             var EU = Roo.lib.Event, i, j, l, len, index;
2409
2410             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2411                 l = unloadListeners[i];
2412                 if (l) {
2413                     var scope = window;
2414                     if (l[EU.ADJ_SCOPE]) {
2415                         if (l[EU.ADJ_SCOPE] === true) {
2416                             scope = l[EU.OBJ];
2417                         } else {
2418                             scope = l[EU.ADJ_SCOPE];
2419                         }
2420                     }
2421                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2422                     unloadListeners[i] = null;
2423                     l = null;
2424                     scope = null;
2425                 }
2426             }
2427
2428             unloadListeners = null;
2429
2430             if (listeners && listeners.length > 0) {
2431                 j = listeners.length;
2432                 while (j) {
2433                     index = j - 1;
2434                     l = listeners[index];
2435                     if (l) {
2436                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2437                                 l[EU.FN], index);
2438                     }
2439                     j = j - 1;
2440                 }
2441                 l = null;
2442
2443                 EU.clearCache();
2444             }
2445
2446             EU.doRemove(window, "unload", EU._unload);
2447
2448         },
2449
2450
2451         getScroll: function() {
2452             var dd = document.documentElement, db = document.body;
2453             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2454                 return [dd.scrollTop, dd.scrollLeft];
2455             } else if (db) {
2456                 return [db.scrollTop, db.scrollLeft];
2457             } else {
2458                 return [0, 0];
2459             }
2460         },
2461
2462
2463         doAdd: function () {
2464             if (window.addEventListener) {
2465                 return function(el, eventName, fn, capture) {
2466                     el.addEventListener(eventName, fn, (capture));
2467                 };
2468             } else if (window.attachEvent) {
2469                 return function(el, eventName, fn, capture) {
2470                     el.attachEvent("on" + eventName, fn);
2471                 };
2472             } else {
2473                 return function() {
2474                 };
2475             }
2476         }(),
2477
2478
2479         doRemove: function() {
2480             if (window.removeEventListener) {
2481                 return function (el, eventName, fn, capture) {
2482                     el.removeEventListener(eventName, fn, (capture));
2483                 };
2484             } else if (window.detachEvent) {
2485                 return function (el, eventName, fn) {
2486                     el.detachEvent("on" + eventName, fn);
2487                 };
2488             } else {
2489                 return function() {
2490                 };
2491             }
2492         }()
2493     };
2494     
2495 }();
2496 (function() {     
2497    
2498     var E = Roo.lib.Event;
2499     E.on = E.addListener;
2500     E.un = E.removeListener;
2501
2502     if (document && document.body) {
2503         E._load();
2504     } else {
2505         E.doAdd(window, "load", E._load);
2506     }
2507     E.doAdd(window, "unload", E._unload);
2508     E._tryPreloadAttach();
2509 })();
2510
2511 /*
2512  * Portions of this file are based on pieces of Yahoo User Interface Library
2513  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2514  * YUI licensed under the BSD License:
2515  * http://developer.yahoo.net/yui/license.txt
2516  * <script type="text/javascript">
2517  *
2518  */
2519
2520 (function() {
2521     /**
2522      * @class Roo.lib.Ajax
2523      *
2524      */
2525     Roo.lib.Ajax = {
2526         /**
2527          * @static 
2528          */
2529         request : function(method, uri, cb, data, options) {
2530             if(options){
2531                 var hs = options.headers;
2532                 if(hs){
2533                     for(var h in hs){
2534                         if(hs.hasOwnProperty(h)){
2535                             this.initHeader(h, hs[h], false);
2536                         }
2537                     }
2538                 }
2539                 if(options.xmlData){
2540                     this.initHeader('Content-Type', 'text/xml', false);
2541                     method = 'POST';
2542                     data = options.xmlData;
2543                 }
2544             }
2545
2546             return this.asyncRequest(method, uri, cb, data);
2547         },
2548
2549         serializeForm : function(form) {
2550             if(typeof form == 'string') {
2551                 form = (document.getElementById(form) || document.forms[form]);
2552             }
2553
2554             var el, name, val, disabled, data = '', hasSubmit = false;
2555             for (var i = 0; i < form.elements.length; i++) {
2556                 el = form.elements[i];
2557                 disabled = form.elements[i].disabled;
2558                 name = form.elements[i].name;
2559                 val = form.elements[i].value;
2560
2561                 if (!disabled && name){
2562                     switch (el.type)
2563                             {
2564                         case 'select-one':
2565                         case 'select-multiple':
2566                             for (var j = 0; j < el.options.length; j++) {
2567                                 if (el.options[j].selected) {
2568                                     if (Roo.isIE) {
2569                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2570                                     }
2571                                     else {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                 }
2575                             }
2576                             break;
2577                         case 'radio':
2578                         case 'checkbox':
2579                             if (el.checked) {
2580                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2581                             }
2582                             break;
2583                         case 'file':
2584
2585                         case undefined:
2586
2587                         case 'reset':
2588
2589                         case 'button':
2590
2591                             break;
2592                         case 'submit':
2593                             if(hasSubmit == false) {
2594                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2595                                 hasSubmit = true;
2596                             }
2597                             break;
2598                         default:
2599                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2600                             break;
2601                     }
2602                 }
2603             }
2604             data = data.substr(0, data.length - 1);
2605             return data;
2606         },
2607
2608         headers:{},
2609
2610         hasHeaders:false,
2611
2612         useDefaultHeader:true,
2613
2614         defaultPostHeader:'application/x-www-form-urlencoded',
2615
2616         useDefaultXhrHeader:true,
2617
2618         defaultXhrHeader:'XMLHttpRequest',
2619
2620         hasDefaultHeaders:true,
2621
2622         defaultHeaders:{},
2623
2624         poll:{},
2625
2626         timeout:{},
2627
2628         pollInterval:50,
2629
2630         transactionId:0,
2631
2632         setProgId:function(id)
2633         {
2634             this.activeX.unshift(id);
2635         },
2636
2637         setDefaultPostHeader:function(b)
2638         {
2639             this.useDefaultHeader = b;
2640         },
2641
2642         setDefaultXhrHeader:function(b)
2643         {
2644             this.useDefaultXhrHeader = b;
2645         },
2646
2647         setPollingInterval:function(i)
2648         {
2649             if (typeof i == 'number' && isFinite(i)) {
2650                 this.pollInterval = i;
2651             }
2652         },
2653
2654         createXhrObject:function(transactionId)
2655         {
2656             var obj,http;
2657             try
2658             {
2659
2660                 http = new XMLHttpRequest();
2661
2662                 obj = { conn:http, tId:transactionId };
2663             }
2664             catch(e)
2665             {
2666                 for (var i = 0; i < this.activeX.length; ++i) {
2667                     try
2668                     {
2669
2670                         http = new ActiveXObject(this.activeX[i]);
2671
2672                         obj = { conn:http, tId:transactionId };
2673                         break;
2674                     }
2675                     catch(e) {
2676                     }
2677                 }
2678             }
2679             finally
2680             {
2681                 return obj;
2682             }
2683         },
2684
2685         getConnectionObject:function()
2686         {
2687             var o;
2688             var tId = this.transactionId;
2689
2690             try
2691             {
2692                 o = this.createXhrObject(tId);
2693                 if (o) {
2694                     this.transactionId++;
2695                 }
2696             }
2697             catch(e) {
2698             }
2699             finally
2700             {
2701                 return o;
2702             }
2703         },
2704
2705         asyncRequest:function(method, uri, callback, postData)
2706         {
2707             var o = this.getConnectionObject();
2708
2709             if (!o) {
2710                 return null;
2711             }
2712             else {
2713                 o.conn.open(method, uri, true);
2714
2715                 if (this.useDefaultXhrHeader) {
2716                     if (!this.defaultHeaders['X-Requested-With']) {
2717                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2718                     }
2719                 }
2720
2721                 if(postData && this.useDefaultHeader){
2722                     this.initHeader('Content-Type', this.defaultPostHeader);
2723                 }
2724
2725                  if (this.hasDefaultHeaders || this.hasHeaders) {
2726                     this.setHeader(o);
2727                 }
2728
2729                 this.handleReadyState(o, callback);
2730                 o.conn.send(postData || null);
2731
2732                 return o;
2733             }
2734         },
2735
2736         handleReadyState:function(o, callback)
2737         {
2738             var oConn = this;
2739
2740             if (callback && callback.timeout) {
2741                 
2742                 this.timeout[o.tId] = window.setTimeout(function() {
2743                     oConn.abort(o, callback, true);
2744                 }, callback.timeout);
2745             }
2746
2747             this.poll[o.tId] = window.setInterval(
2748                     function() {
2749                         if (o.conn && o.conn.readyState == 4) {
2750                             window.clearInterval(oConn.poll[o.tId]);
2751                             delete oConn.poll[o.tId];
2752
2753                             if(callback && callback.timeout) {
2754                                 window.clearTimeout(oConn.timeout[o.tId]);
2755                                 delete oConn.timeout[o.tId];
2756                             }
2757
2758                             oConn.handleTransactionResponse(o, callback);
2759                         }
2760                     }
2761                     , this.pollInterval);
2762         },
2763
2764         handleTransactionResponse:function(o, callback, isAbort)
2765         {
2766
2767             if (!callback) {
2768                 this.releaseObject(o);
2769                 return;
2770             }
2771
2772             var httpStatus, responseObject;
2773
2774             try
2775             {
2776                 if (o.conn.status !== undefined && o.conn.status != 0) {
2777                     httpStatus = o.conn.status;
2778                 }
2779                 else {
2780                     httpStatus = 13030;
2781                 }
2782             }
2783             catch(e) {
2784
2785
2786                 httpStatus = 13030;
2787             }
2788
2789             if (httpStatus >= 200 && httpStatus < 300) {
2790                 responseObject = this.createResponseObject(o, callback.argument);
2791                 if (callback.success) {
2792                     if (!callback.scope) {
2793                         callback.success(responseObject);
2794                     }
2795                     else {
2796
2797
2798                         callback.success.apply(callback.scope, [responseObject]);
2799                     }
2800                 }
2801             }
2802             else {
2803                 switch (httpStatus) {
2804
2805                     case 12002:
2806                     case 12029:
2807                     case 12030:
2808                     case 12031:
2809                     case 12152:
2810                     case 13030:
2811                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2812                         if (callback.failure) {
2813                             if (!callback.scope) {
2814                                 callback.failure(responseObject);
2815                             }
2816                             else {
2817                                 callback.failure.apply(callback.scope, [responseObject]);
2818                             }
2819                         }
2820                         break;
2821                     default:
2822                         responseObject = this.createResponseObject(o, callback.argument);
2823                         if (callback.failure) {
2824                             if (!callback.scope) {
2825                                 callback.failure(responseObject);
2826                             }
2827                             else {
2828                                 callback.failure.apply(callback.scope, [responseObject]);
2829                             }
2830                         }
2831                 }
2832             }
2833
2834             this.releaseObject(o);
2835             responseObject = null;
2836         },
2837
2838         createResponseObject:function(o, callbackArg)
2839         {
2840             var obj = {};
2841             var headerObj = {};
2842
2843             try
2844             {
2845                 var headerStr = o.conn.getAllResponseHeaders();
2846                 var header = headerStr.split('\n');
2847                 for (var i = 0; i < header.length; i++) {
2848                     var delimitPos = header[i].indexOf(':');
2849                     if (delimitPos != -1) {
2850                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2851                     }
2852                 }
2853             }
2854             catch(e) {
2855             }
2856
2857             obj.tId = o.tId;
2858             obj.status = o.conn.status;
2859             obj.statusText = o.conn.statusText;
2860             obj.getResponseHeader = headerObj;
2861             obj.getAllResponseHeaders = headerStr;
2862             obj.responseText = o.conn.responseText;
2863             obj.responseXML = o.conn.responseXML;
2864
2865             if (typeof callbackArg !== undefined) {
2866                 obj.argument = callbackArg;
2867             }
2868
2869             return obj;
2870         },
2871
2872         createExceptionObject:function(tId, callbackArg, isAbort)
2873         {
2874             var COMM_CODE = 0;
2875             var COMM_ERROR = 'communication failure';
2876             var ABORT_CODE = -1;
2877             var ABORT_ERROR = 'transaction aborted';
2878
2879             var obj = {};
2880
2881             obj.tId = tId;
2882             if (isAbort) {
2883                 obj.status = ABORT_CODE;
2884                 obj.statusText = ABORT_ERROR;
2885             }
2886             else {
2887                 obj.status = COMM_CODE;
2888                 obj.statusText = COMM_ERROR;
2889             }
2890
2891             if (callbackArg) {
2892                 obj.argument = callbackArg;
2893             }
2894
2895             return obj;
2896         },
2897
2898         initHeader:function(label, value, isDefault)
2899         {
2900             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2901
2902             if (headerObj[label] === undefined) {
2903                 headerObj[label] = value;
2904             }
2905             else {
2906
2907
2908                 headerObj[label] = value + "," + headerObj[label];
2909             }
2910
2911             if (isDefault) {
2912                 this.hasDefaultHeaders = true;
2913             }
2914             else {
2915                 this.hasHeaders = true;
2916             }
2917         },
2918
2919
2920         setHeader:function(o)
2921         {
2922             if (this.hasDefaultHeaders) {
2923                 for (var prop in this.defaultHeaders) {
2924                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2925                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2926                     }
2927                 }
2928             }
2929
2930             if (this.hasHeaders) {
2931                 for (var prop in this.headers) {
2932                     if (this.headers.hasOwnProperty(prop)) {
2933                         o.conn.setRequestHeader(prop, this.headers[prop]);
2934                     }
2935                 }
2936                 this.headers = {};
2937                 this.hasHeaders = false;
2938             }
2939         },
2940
2941         resetDefaultHeaders:function() {
2942             delete this.defaultHeaders;
2943             this.defaultHeaders = {};
2944             this.hasDefaultHeaders = false;
2945         },
2946
2947         abort:function(o, callback, isTimeout)
2948         {
2949             if(this.isCallInProgress(o)) {
2950                 o.conn.abort();
2951                 window.clearInterval(this.poll[o.tId]);
2952                 delete this.poll[o.tId];
2953                 if (isTimeout) {
2954                     delete this.timeout[o.tId];
2955                 }
2956
2957                 this.handleTransactionResponse(o, callback, true);
2958
2959                 return true;
2960             }
2961             else {
2962                 return false;
2963             }
2964         },
2965
2966
2967         isCallInProgress:function(o)
2968         {
2969             if (o && o.conn) {
2970                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2971             }
2972             else {
2973
2974                 return false;
2975             }
2976         },
2977
2978
2979         releaseObject:function(o)
2980         {
2981
2982             o.conn = null;
2983
2984             o = null;
2985         },
2986
2987         activeX:[
2988         'MSXML2.XMLHTTP.3.0',
2989         'MSXML2.XMLHTTP',
2990         'Microsoft.XMLHTTP'
2991         ]
2992
2993
2994     };
2995 })();/*
2996  * Portions of this file are based on pieces of Yahoo User Interface Library
2997  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2998  * YUI licensed under the BSD License:
2999  * http://developer.yahoo.net/yui/license.txt
3000  * <script type="text/javascript">
3001  *
3002  */
3003
3004 Roo.lib.Region = function(t, r, b, l) {
3005     this.top = t;
3006     this[1] = t;
3007     this.right = r;
3008     this.bottom = b;
3009     this.left = l;
3010     this[0] = l;
3011 };
3012
3013
3014 Roo.lib.Region.prototype = {
3015     contains : function(region) {
3016         return ( region.left >= this.left &&
3017                  region.right <= this.right &&
3018                  region.top >= this.top &&
3019                  region.bottom <= this.bottom    );
3020
3021     },
3022
3023     getArea : function() {
3024         return ( (this.bottom - this.top) * (this.right - this.left) );
3025     },
3026
3027     intersect : function(region) {
3028         var t = Math.max(this.top, region.top);
3029         var r = Math.min(this.right, region.right);
3030         var b = Math.min(this.bottom, region.bottom);
3031         var l = Math.max(this.left, region.left);
3032
3033         if (b >= t && r >= l) {
3034             return new Roo.lib.Region(t, r, b, l);
3035         } else {
3036             return null;
3037         }
3038     },
3039     union : function(region) {
3040         var t = Math.min(this.top, region.top);
3041         var r = Math.max(this.right, region.right);
3042         var b = Math.max(this.bottom, region.bottom);
3043         var l = Math.min(this.left, region.left);
3044
3045         return new Roo.lib.Region(t, r, b, l);
3046     },
3047
3048     adjust : function(t, l, b, r) {
3049         this.top += t;
3050         this.left += l;
3051         this.right += r;
3052         this.bottom += b;
3053         return this;
3054     }
3055 };
3056
3057 Roo.lib.Region.getRegion = function(el) {
3058     var p = Roo.lib.Dom.getXY(el);
3059
3060     var t = p[1];
3061     var r = p[0] + el.offsetWidth;
3062     var b = p[1] + el.offsetHeight;
3063     var l = p[0];
3064
3065     return new Roo.lib.Region(t, r, b, l);
3066 };
3067 /*
3068  * Portions of this file are based on pieces of Yahoo User Interface Library
3069  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3070  * YUI licensed under the BSD License:
3071  * http://developer.yahoo.net/yui/license.txt
3072  * <script type="text/javascript">
3073  *
3074  */
3075 //@@dep Roo.lib.Region
3076
3077
3078 Roo.lib.Point = function(x, y) {
3079     if (x instanceof Array) {
3080         y = x[1];
3081         x = x[0];
3082     }
3083     this.x = this.right = this.left = this[0] = x;
3084     this.y = this.top = this.bottom = this[1] = y;
3085 };
3086
3087 Roo.lib.Point.prototype = new Roo.lib.Region();
3088 /*
3089  * Portions of this file are based on pieces of Yahoo User Interface Library
3090  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3091  * YUI licensed under the BSD License:
3092  * http://developer.yahoo.net/yui/license.txt
3093  * <script type="text/javascript">
3094  *
3095  */
3096  
3097 (function() {   
3098
3099     Roo.lib.Anim = {
3100         scroll : function(el, args, duration, easing, cb, scope) {
3101             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3102         },
3103
3104         motion : function(el, args, duration, easing, cb, scope) {
3105             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3106         },
3107
3108         color : function(el, args, duration, easing, cb, scope) {
3109             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3110         },
3111
3112         run : function(el, args, duration, easing, cb, scope, type) {
3113             type = type || Roo.lib.AnimBase;
3114             if (typeof easing == "string") {
3115                 easing = Roo.lib.Easing[easing];
3116             }
3117             var anim = new type(el, args, duration, easing);
3118             anim.animateX(function() {
3119                 Roo.callback(cb, scope);
3120             });
3121             return anim;
3122         }
3123     };
3124 })();/*
3125  * Portions of this file are based on pieces of Yahoo User Interface Library
3126  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3127  * YUI licensed under the BSD License:
3128  * http://developer.yahoo.net/yui/license.txt
3129  * <script type="text/javascript">
3130  *
3131  */
3132
3133 (function() {    
3134     var libFlyweight;
3135     
3136     function fly(el) {
3137         if (!libFlyweight) {
3138             libFlyweight = new Roo.Element.Flyweight();
3139         }
3140         libFlyweight.dom = el;
3141         return libFlyweight;
3142     }
3143
3144     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3145     
3146    
3147     
3148     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3149         if (el) {
3150             this.init(el, attributes, duration, method);
3151         }
3152     };
3153
3154     Roo.lib.AnimBase.fly = fly;
3155     
3156     
3157     
3158     Roo.lib.AnimBase.prototype = {
3159
3160         toString: function() {
3161             var el = this.getEl();
3162             var id = el.id || el.tagName;
3163             return ("Anim " + id);
3164         },
3165
3166         patterns: {
3167             noNegatives:        /width|height|opacity|padding/i,
3168             offsetAttribute:  /^((width|height)|(top|left))$/,
3169             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3170             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3171         },
3172
3173
3174         doMethod: function(attr, start, end) {
3175             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3176         },
3177
3178
3179         setAttribute: function(attr, val, unit) {
3180             if (this.patterns.noNegatives.test(attr)) {
3181                 val = (val > 0) ? val : 0;
3182             }
3183
3184             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3185         },
3186
3187
3188         getAttribute: function(attr) {
3189             var el = this.getEl();
3190             var val = fly(el).getStyle(attr);
3191
3192             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3193                 return parseFloat(val);
3194             }
3195
3196             var a = this.patterns.offsetAttribute.exec(attr) || [];
3197             var pos = !!( a[3] );
3198             var box = !!( a[2] );
3199
3200
3201             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3202                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3203             } else {
3204                 val = 0;
3205             }
3206
3207             return val;
3208         },
3209
3210
3211         getDefaultUnit: function(attr) {
3212             if (this.patterns.defaultUnit.test(attr)) {
3213                 return 'px';
3214             }
3215
3216             return '';
3217         },
3218
3219         animateX : function(callback, scope) {
3220             var f = function() {
3221                 this.onComplete.removeListener(f);
3222                 if (typeof callback == "function") {
3223                     callback.call(scope || this, this);
3224                 }
3225             };
3226             this.onComplete.addListener(f, this);
3227             this.animate();
3228         },
3229
3230
3231         setRuntimeAttribute: function(attr) {
3232             var start;
3233             var end;
3234             var attributes = this.attributes;
3235
3236             this.runtimeAttributes[attr] = {};
3237
3238             var isset = function(prop) {
3239                 return (typeof prop !== 'undefined');
3240             };
3241
3242             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3243                 return false;
3244             }
3245
3246             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3247
3248
3249             if (isset(attributes[attr]['to'])) {
3250                 end = attributes[attr]['to'];
3251             } else if (isset(attributes[attr]['by'])) {
3252                 if (start.constructor == Array) {
3253                     end = [];
3254                     for (var i = 0, len = start.length; i < len; ++i) {
3255                         end[i] = start[i] + attributes[attr]['by'][i];
3256                     }
3257                 } else {
3258                     end = start + attributes[attr]['by'];
3259                 }
3260             }
3261
3262             this.runtimeAttributes[attr].start = start;
3263             this.runtimeAttributes[attr].end = end;
3264
3265
3266             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3267         },
3268
3269
3270         init: function(el, attributes, duration, method) {
3271
3272             var isAnimated = false;
3273
3274
3275             var startTime = null;
3276
3277
3278             var actualFrames = 0;
3279
3280
3281             el = Roo.getDom(el);
3282
3283
3284             this.attributes = attributes || {};
3285
3286
3287             this.duration = duration || 1;
3288
3289
3290             this.method = method || Roo.lib.Easing.easeNone;
3291
3292
3293             this.useSeconds = true;
3294
3295
3296             this.currentFrame = 0;
3297
3298
3299             this.totalFrames = Roo.lib.AnimMgr.fps;
3300
3301
3302             this.getEl = function() {
3303                 return el;
3304             };
3305
3306
3307             this.isAnimated = function() {
3308                 return isAnimated;
3309             };
3310
3311
3312             this.getStartTime = function() {
3313                 return startTime;
3314             };
3315
3316             this.runtimeAttributes = {};
3317
3318
3319             this.animate = function() {
3320                 if (this.isAnimated()) {
3321                     return false;
3322                 }
3323
3324                 this.currentFrame = 0;
3325
3326                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3327
3328                 Roo.lib.AnimMgr.registerElement(this);
3329             };
3330
3331
3332             this.stop = function(finish) {
3333                 if (finish) {
3334                     this.currentFrame = this.totalFrames;
3335                     this._onTween.fire();
3336                 }
3337                 Roo.lib.AnimMgr.stop(this);
3338             };
3339
3340             var onStart = function() {
3341                 this.onStart.fire();
3342
3343                 this.runtimeAttributes = {};
3344                 for (var attr in this.attributes) {
3345                     this.setRuntimeAttribute(attr);
3346                 }
3347
3348                 isAnimated = true;
3349                 actualFrames = 0;
3350                 startTime = new Date();
3351             };
3352
3353
3354             var onTween = function() {
3355                 var data = {
3356                     duration: new Date() - this.getStartTime(),
3357                     currentFrame: this.currentFrame
3358                 };
3359
3360                 data.toString = function() {
3361                     return (
3362                             'duration: ' + data.duration +
3363                             ', currentFrame: ' + data.currentFrame
3364                             );
3365                 };
3366
3367                 this.onTween.fire(data);
3368
3369                 var runtimeAttributes = this.runtimeAttributes;
3370
3371                 for (var attr in runtimeAttributes) {
3372                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3373                 }
3374
3375                 actualFrames += 1;
3376             };
3377
3378             var onComplete = function() {
3379                 var actual_duration = (new Date() - startTime) / 1000 ;
3380
3381                 var data = {
3382                     duration: actual_duration,
3383                     frames: actualFrames,
3384                     fps: actualFrames / actual_duration
3385                 };
3386
3387                 data.toString = function() {
3388                     return (
3389                             'duration: ' + data.duration +
3390                             ', frames: ' + data.frames +
3391                             ', fps: ' + data.fps
3392                             );
3393                 };
3394
3395                 isAnimated = false;
3396                 actualFrames = 0;
3397                 this.onComplete.fire(data);
3398             };
3399
3400
3401             this._onStart = new Roo.util.Event(this);
3402             this.onStart = new Roo.util.Event(this);
3403             this.onTween = new Roo.util.Event(this);
3404             this._onTween = new Roo.util.Event(this);
3405             this.onComplete = new Roo.util.Event(this);
3406             this._onComplete = new Roo.util.Event(this);
3407             this._onStart.addListener(onStart);
3408             this._onTween.addListener(onTween);
3409             this._onComplete.addListener(onComplete);
3410         }
3411     };
3412 })();
3413 /*
3414  * Portions of this file are based on pieces of Yahoo User Interface Library
3415  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3416  * YUI licensed under the BSD License:
3417  * http://developer.yahoo.net/yui/license.txt
3418  * <script type="text/javascript">
3419  *
3420  */
3421
3422 Roo.lib.AnimMgr = new function() {
3423
3424     var thread = null;
3425
3426
3427     var queue = [];
3428
3429
3430     var tweenCount = 0;
3431
3432
3433     this.fps = 1000;
3434
3435
3436     this.delay = 1;
3437
3438
3439     this.registerElement = function(tween) {
3440         queue[queue.length] = tween;
3441         tweenCount += 1;
3442         tween._onStart.fire();
3443         this.start();
3444     };
3445
3446
3447     this.unRegister = function(tween, index) {
3448         tween._onComplete.fire();
3449         index = index || getIndex(tween);
3450         if (index != -1) {
3451             queue.splice(index, 1);
3452         }
3453
3454         tweenCount -= 1;
3455         if (tweenCount <= 0) {
3456             this.stop();
3457         }
3458     };
3459
3460
3461     this.start = function() {
3462         if (thread === null) {
3463             thread = setInterval(this.run, this.delay);
3464         }
3465     };
3466
3467
3468     this.stop = function(tween) {
3469         if (!tween) {
3470             clearInterval(thread);
3471
3472             for (var i = 0, len = queue.length; i < len; ++i) {
3473                 if (queue[0].isAnimated()) {
3474                     this.unRegister(queue[0], 0);
3475                 }
3476             }
3477
3478             queue = [];
3479             thread = null;
3480             tweenCount = 0;
3481         }
3482         else {
3483             this.unRegister(tween);
3484         }
3485     };
3486
3487
3488     this.run = function() {
3489         for (var i = 0, len = queue.length; i < len; ++i) {
3490             var tween = queue[i];
3491             if (!tween || !tween.isAnimated()) {
3492                 continue;
3493             }
3494
3495             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3496             {
3497                 tween.currentFrame += 1;
3498
3499                 if (tween.useSeconds) {
3500                     correctFrame(tween);
3501                 }
3502                 tween._onTween.fire();
3503             }
3504             else {
3505                 Roo.lib.AnimMgr.stop(tween, i);
3506             }
3507         }
3508     };
3509
3510     var getIndex = function(anim) {
3511         for (var i = 0, len = queue.length; i < len; ++i) {
3512             if (queue[i] == anim) {
3513                 return i;
3514             }
3515         }
3516         return -1;
3517     };
3518
3519
3520     var correctFrame = function(tween) {
3521         var frames = tween.totalFrames;
3522         var frame = tween.currentFrame;
3523         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3524         var elapsed = (new Date() - tween.getStartTime());
3525         var tweak = 0;
3526
3527         if (elapsed < tween.duration * 1000) {
3528             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3529         } else {
3530             tweak = frames - (frame + 1);
3531         }
3532         if (tweak > 0 && isFinite(tweak)) {
3533             if (tween.currentFrame + tweak >= frames) {
3534                 tweak = frames - (frame + 1);
3535             }
3536
3537             tween.currentFrame += tweak;
3538         }
3539     };
3540 };
3541
3542     /*
3543  * Portions of this file are based on pieces of Yahoo User Interface Library
3544  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3545  * YUI licensed under the BSD License:
3546  * http://developer.yahoo.net/yui/license.txt
3547  * <script type="text/javascript">
3548  *
3549  */
3550 Roo.lib.Bezier = new function() {
3551
3552         this.getPosition = function(points, t) {
3553             var n = points.length;
3554             var tmp = [];
3555
3556             for (var i = 0; i < n; ++i) {
3557                 tmp[i] = [points[i][0], points[i][1]];
3558             }
3559
3560             for (var j = 1; j < n; ++j) {
3561                 for (i = 0; i < n - j; ++i) {
3562                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3563                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3564                 }
3565             }
3566
3567             return [ tmp[0][0], tmp[0][1] ];
3568
3569         };
3570     };/*
3571  * Portions of this file are based on pieces of Yahoo User Interface Library
3572  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3573  * YUI licensed under the BSD License:
3574  * http://developer.yahoo.net/yui/license.txt
3575  * <script type="text/javascript">
3576  *
3577  */
3578 (function() {
3579
3580     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3581         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3582     };
3583
3584     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3585
3586     var fly = Roo.lib.AnimBase.fly;
3587     var Y = Roo.lib;
3588     var superclass = Y.ColorAnim.superclass;
3589     var proto = Y.ColorAnim.prototype;
3590
3591     proto.toString = function() {
3592         var el = this.getEl();
3593         var id = el.id || el.tagName;
3594         return ("ColorAnim " + id);
3595     };
3596
3597     proto.patterns.color = /color$/i;
3598     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3599     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3600     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3601     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3602
3603
3604     proto.parseColor = function(s) {
3605         if (s.length == 3) {
3606             return s;
3607         }
3608
3609         var c = this.patterns.hex.exec(s);
3610         if (c && c.length == 4) {
3611             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3612         }
3613
3614         c = this.patterns.rgb.exec(s);
3615         if (c && c.length == 4) {
3616             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3617         }
3618
3619         c = this.patterns.hex3.exec(s);
3620         if (c && c.length == 4) {
3621             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3622         }
3623
3624         return null;
3625     };
3626     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3627     proto.getAttribute = function(attr) {
3628         var el = this.getEl();
3629         if (this.patterns.color.test(attr)) {
3630             var val = fly(el).getStyle(attr);
3631
3632             if (this.patterns.transparent.test(val)) {
3633                 var parent = el.parentNode;
3634                 val = fly(parent).getStyle(attr);
3635
3636                 while (parent && this.patterns.transparent.test(val)) {
3637                     parent = parent.parentNode;
3638                     val = fly(parent).getStyle(attr);
3639                     if (parent.tagName.toUpperCase() == 'HTML') {
3640                         val = '#fff';
3641                     }
3642                 }
3643             }
3644         } else {
3645             val = superclass.getAttribute.call(this, attr);
3646         }
3647
3648         return val;
3649     };
3650     proto.getAttribute = function(attr) {
3651         var el = this.getEl();
3652         if (this.patterns.color.test(attr)) {
3653             var val = fly(el).getStyle(attr);
3654
3655             if (this.patterns.transparent.test(val)) {
3656                 var parent = el.parentNode;
3657                 val = fly(parent).getStyle(attr);
3658
3659                 while (parent && this.patterns.transparent.test(val)) {
3660                     parent = parent.parentNode;
3661                     val = fly(parent).getStyle(attr);
3662                     if (parent.tagName.toUpperCase() == 'HTML') {
3663                         val = '#fff';
3664                     }
3665                 }
3666             }
3667         } else {
3668             val = superclass.getAttribute.call(this, attr);
3669         }
3670
3671         return val;
3672     };
3673
3674     proto.doMethod = function(attr, start, end) {
3675         var val;
3676
3677         if (this.patterns.color.test(attr)) {
3678             val = [];
3679             for (var i = 0, len = start.length; i < len; ++i) {
3680                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3681             }
3682
3683             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3684         }
3685         else {
3686             val = superclass.doMethod.call(this, attr, start, end);
3687         }
3688
3689         return val;
3690     };
3691
3692     proto.setRuntimeAttribute = function(attr) {
3693         superclass.setRuntimeAttribute.call(this, attr);
3694
3695         if (this.patterns.color.test(attr)) {
3696             var attributes = this.attributes;
3697             var start = this.parseColor(this.runtimeAttributes[attr].start);
3698             var end = this.parseColor(this.runtimeAttributes[attr].end);
3699
3700             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3701                 end = this.parseColor(attributes[attr].by);
3702
3703                 for (var i = 0, len = start.length; i < len; ++i) {
3704                     end[i] = start[i] + end[i];
3705                 }
3706             }
3707
3708             this.runtimeAttributes[attr].start = start;
3709             this.runtimeAttributes[attr].end = end;
3710         }
3711     };
3712 })();
3713
3714 /*
3715  * Portions of this file are based on pieces of Yahoo User Interface Library
3716  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3717  * YUI licensed under the BSD License:
3718  * http://developer.yahoo.net/yui/license.txt
3719  * <script type="text/javascript">
3720  *
3721  */
3722 Roo.lib.Easing = {
3723
3724
3725     easeNone: function (t, b, c, d) {
3726         return c * t / d + b;
3727     },
3728
3729
3730     easeIn: function (t, b, c, d) {
3731         return c * (t /= d) * t + b;
3732     },
3733
3734
3735     easeOut: function (t, b, c, d) {
3736         return -c * (t /= d) * (t - 2) + b;
3737     },
3738
3739
3740     easeBoth: function (t, b, c, d) {
3741         if ((t /= d / 2) < 1) {
3742             return c / 2 * t * t + b;
3743         }
3744
3745         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3746     },
3747
3748
3749     easeInStrong: function (t, b, c, d) {
3750         return c * (t /= d) * t * t * t + b;
3751     },
3752
3753
3754     easeOutStrong: function (t, b, c, d) {
3755         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3756     },
3757
3758
3759     easeBothStrong: function (t, b, c, d) {
3760         if ((t /= d / 2) < 1) {
3761             return c / 2 * t * t * t * t + b;
3762         }
3763
3764         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3765     },
3766
3767
3768
3769     elasticIn: function (t, b, c, d, a, p) {
3770         if (t == 0) {
3771             return b;
3772         }
3773         if ((t /= d) == 1) {
3774             return b + c;
3775         }
3776         if (!p) {
3777             p = d * .3;
3778         }
3779
3780         if (!a || a < Math.abs(c)) {
3781             a = c;
3782             var s = p / 4;
3783         }
3784         else {
3785             var s = p / (2 * Math.PI) * Math.asin(c / a);
3786         }
3787
3788         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789     },
3790
3791
3792     elasticOut: function (t, b, c, d, a, p) {
3793         if (t == 0) {
3794             return b;
3795         }
3796         if ((t /= d) == 1) {
3797             return b + c;
3798         }
3799         if (!p) {
3800             p = d * .3;
3801         }
3802
3803         if (!a || a < Math.abs(c)) {
3804             a = c;
3805             var s = p / 4;
3806         }
3807         else {
3808             var s = p / (2 * Math.PI) * Math.asin(c / a);
3809         }
3810
3811         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3812     },
3813
3814
3815     elasticBoth: function (t, b, c, d, a, p) {
3816         if (t == 0) {
3817             return b;
3818         }
3819
3820         if ((t /= d / 2) == 2) {
3821             return b + c;
3822         }
3823
3824         if (!p) {
3825             p = d * (.3 * 1.5);
3826         }
3827
3828         if (!a || a < Math.abs(c)) {
3829             a = c;
3830             var s = p / 4;
3831         }
3832         else {
3833             var s = p / (2 * Math.PI) * Math.asin(c / a);
3834         }
3835
3836         if (t < 1) {
3837             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3838                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3839         }
3840         return a * Math.pow(2, -10 * (t -= 1)) *
3841                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3842     },
3843
3844
3845
3846     backIn: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3851     },
3852
3853
3854     backOut: function (t, b, c, d, s) {
3855         if (typeof s == 'undefined') {
3856             s = 1.70158;
3857         }
3858         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3859     },
3860
3861
3862     backBoth: function (t, b, c, d, s) {
3863         if (typeof s == 'undefined') {
3864             s = 1.70158;
3865         }
3866
3867         if ((t /= d / 2 ) < 1) {
3868             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3869         }
3870         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3871     },
3872
3873
3874     bounceIn: function (t, b, c, d) {
3875         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3876     },
3877
3878
3879     bounceOut: function (t, b, c, d) {
3880         if ((t /= d) < (1 / 2.75)) {
3881             return c * (7.5625 * t * t) + b;
3882         } else if (t < (2 / 2.75)) {
3883             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3884         } else if (t < (2.5 / 2.75)) {
3885             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3886         }
3887         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3888     },
3889
3890
3891     bounceBoth: function (t, b, c, d) {
3892         if (t < d / 2) {
3893             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3894         }
3895         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3896     }
3897 };/*
3898  * Portions of this file are based on pieces of Yahoo User Interface Library
3899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3900  * YUI licensed under the BSD License:
3901  * http://developer.yahoo.net/yui/license.txt
3902  * <script type="text/javascript">
3903  *
3904  */
3905     (function() {
3906         Roo.lib.Motion = function(el, attributes, duration, method) {
3907             if (el) {
3908                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3909             }
3910         };
3911
3912         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3913
3914
3915         var Y = Roo.lib;
3916         var superclass = Y.Motion.superclass;
3917         var proto = Y.Motion.prototype;
3918
3919         proto.toString = function() {
3920             var el = this.getEl();
3921             var id = el.id || el.tagName;
3922             return ("Motion " + id);
3923         };
3924
3925         proto.patterns.points = /^points$/i;
3926
3927         proto.setAttribute = function(attr, val, unit) {
3928             if (this.patterns.points.test(attr)) {
3929                 unit = unit || 'px';
3930                 superclass.setAttribute.call(this, 'left', val[0], unit);
3931                 superclass.setAttribute.call(this, 'top', val[1], unit);
3932             } else {
3933                 superclass.setAttribute.call(this, attr, val, unit);
3934             }
3935         };
3936
3937         proto.getAttribute = function(attr) {
3938             if (this.patterns.points.test(attr)) {
3939                 var val = [
3940                         superclass.getAttribute.call(this, 'left'),
3941                         superclass.getAttribute.call(this, 'top')
3942                         ];
3943             } else {
3944                 val = superclass.getAttribute.call(this, attr);
3945             }
3946
3947             return val;
3948         };
3949
3950         proto.doMethod = function(attr, start, end) {
3951             var val = null;
3952
3953             if (this.patterns.points.test(attr)) {
3954                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3955                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3956             } else {
3957                 val = superclass.doMethod.call(this, attr, start, end);
3958             }
3959             return val;
3960         };
3961
3962         proto.setRuntimeAttribute = function(attr) {
3963             if (this.patterns.points.test(attr)) {
3964                 var el = this.getEl();
3965                 var attributes = this.attributes;
3966                 var start;
3967                 var control = attributes['points']['control'] || [];
3968                 var end;
3969                 var i, len;
3970
3971                 if (control.length > 0 && !(control[0] instanceof Array)) {
3972                     control = [control];
3973                 } else {
3974                     var tmp = [];
3975                     for (i = 0,len = control.length; i < len; ++i) {
3976                         tmp[i] = control[i];
3977                     }
3978                     control = tmp;
3979                 }
3980
3981                 Roo.fly(el).position();
3982
3983                 if (isset(attributes['points']['from'])) {
3984                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3985                 }
3986                 else {
3987                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3988                 }
3989
3990                 start = this.getAttribute('points');
3991
3992
3993                 if (isset(attributes['points']['to'])) {
3994                     end = translateValues.call(this, attributes['points']['to'], start);
3995
3996                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3997                     for (i = 0,len = control.length; i < len; ++i) {
3998                         control[i] = translateValues.call(this, control[i], start);
3999                     }
4000
4001
4002                 } else if (isset(attributes['points']['by'])) {
4003                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4004
4005                     for (i = 0,len = control.length; i < len; ++i) {
4006                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4007                     }
4008                 }
4009
4010                 this.runtimeAttributes[attr] = [start];
4011
4012                 if (control.length > 0) {
4013                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4014                 }
4015
4016                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4017             }
4018             else {
4019                 superclass.setRuntimeAttribute.call(this, attr);
4020             }
4021         };
4022
4023         var translateValues = function(val, start) {
4024             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4025             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4026
4027             return val;
4028         };
4029
4030         var isset = function(prop) {
4031             return (typeof prop !== 'undefined');
4032         };
4033     })();
4034 /*
4035  * Portions of this file are based on pieces of Yahoo User Interface Library
4036  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4037  * YUI licensed under the BSD License:
4038  * http://developer.yahoo.net/yui/license.txt
4039  * <script type="text/javascript">
4040  *
4041  */
4042     (function() {
4043         Roo.lib.Scroll = function(el, attributes, duration, method) {
4044             if (el) {
4045                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4046             }
4047         };
4048
4049         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4050
4051
4052         var Y = Roo.lib;
4053         var superclass = Y.Scroll.superclass;
4054         var proto = Y.Scroll.prototype;
4055
4056         proto.toString = function() {
4057             var el = this.getEl();
4058             var id = el.id || el.tagName;
4059             return ("Scroll " + id);
4060         };
4061
4062         proto.doMethod = function(attr, start, end) {
4063             var val = null;
4064
4065             if (attr == 'scroll') {
4066                 val = [
4067                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4068                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4069                         ];
4070
4071             } else {
4072                 val = superclass.doMethod.call(this, attr, start, end);
4073             }
4074             return val;
4075         };
4076
4077         proto.getAttribute = function(attr) {
4078             var val = null;
4079             var el = this.getEl();
4080
4081             if (attr == 'scroll') {
4082                 val = [ el.scrollLeft, el.scrollTop ];
4083             } else {
4084                 val = superclass.getAttribute.call(this, attr);
4085             }
4086
4087             return val;
4088         };
4089
4090         proto.setAttribute = function(attr, val, unit) {
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 el.scrollLeft = val[0];
4095                 el.scrollTop = val[1];
4096             } else {
4097                 superclass.setAttribute.call(this, attr, val, unit);
4098             }
4099         };
4100     })();
4101 /*
4102  * Based on:
4103  * Ext JS Library 1.1.1
4104  * Copyright(c) 2006-2007, Ext JS, LLC.
4105  *
4106  * Originally Released Under LGPL - original licence link has changed is not relivant.
4107  *
4108  * Fork - LGPL
4109  * <script type="text/javascript">
4110  */
4111
4112
4113 // nasty IE9 hack - what a pile of crap that is..
4114
4115  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4116     Range.prototype.createContextualFragment = function (html) {
4117         var doc = window.document;
4118         var container = doc.createElement("div");
4119         container.innerHTML = html;
4120         var frag = doc.createDocumentFragment(), n;
4121         while ((n = container.firstChild)) {
4122             frag.appendChild(n);
4123         }
4124         return frag;
4125     };
4126 }
4127
4128 /**
4129  * @class Roo.DomHelper
4130  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4131  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4132  * @singleton
4133  */
4134 Roo.DomHelper = function(){
4135     var tempTableEl = null;
4136     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4137     var tableRe = /^table|tbody|tr|td$/i;
4138     var xmlns = {};
4139     // build as innerHTML where available
4140     /** @ignore */
4141     var createHtml = function(o){
4142         if(typeof o == 'string'){
4143             return o;
4144         }
4145         var b = "";
4146         if(!o.tag){
4147             o.tag = "div";
4148         }
4149         b += "<" + o.tag;
4150         for(var attr in o){
4151             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4152             if(attr == "style"){
4153                 var s = o["style"];
4154                 if(typeof s == "function"){
4155                     s = s.call();
4156                 }
4157                 if(typeof s == "string"){
4158                     b += ' style="' + s + '"';
4159                 }else if(typeof s == "object"){
4160                     b += ' style="';
4161                     for(var key in s){
4162                         if(typeof s[key] != "function"){
4163                             b += key + ":" + s[key] + ";";
4164                         }
4165                     }
4166                     b += '"';
4167                 }
4168             }else{
4169                 if(attr == "cls"){
4170                     b += ' class="' + o["cls"] + '"';
4171                 }else if(attr == "htmlFor"){
4172                     b += ' for="' + o["htmlFor"] + '"';
4173                 }else{
4174                     b += " " + attr + '="' + o[attr] + '"';
4175                 }
4176             }
4177         }
4178         if(emptyTags.test(o.tag)){
4179             b += "/>";
4180         }else{
4181             b += ">";
4182             var cn = o.children || o.cn;
4183             if(cn){
4184                 //http://bugs.kde.org/show_bug.cgi?id=71506
4185                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4186                     for(var i = 0, len = cn.length; i < len; i++) {
4187                         b += createHtml(cn[i], b);
4188                     }
4189                 }else{
4190                     b += createHtml(cn, b);
4191                 }
4192             }
4193             if(o.html){
4194                 b += o.html;
4195             }
4196             b += "</" + o.tag + ">";
4197         }
4198         return b;
4199     };
4200
4201     // build as dom
4202     /** @ignore */
4203     var createDom = function(o, parentNode){
4204          
4205         // defininition craeted..
4206         var ns = false;
4207         if (o.ns && o.ns != 'html') {
4208                
4209             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4210                 xmlns[o.ns] = o.xmlns;
4211                 ns = o.xmlns;
4212             }
4213             if (typeof(xmlns[o.ns]) == 'undefined') {
4214                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4215             }
4216             ns = xmlns[o.ns];
4217         }
4218         
4219         
4220         if (typeof(o) == 'string') {
4221             return parentNode.appendChild(document.createTextNode(o));
4222         }
4223         o.tag = o.tag || div;
4224         if (o.ns && Roo.isIE) {
4225             ns = false;
4226             o.tag = o.ns + ':' + o.tag;
4227             
4228         }
4229         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4230         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4231         for(var attr in o){
4232             
4233             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4234                     attr == "style" || typeof o[attr] == "function") continue;
4235                     
4236             if(attr=="cls" && Roo.isIE){
4237                 el.className = o["cls"];
4238             }else{
4239                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4240                 else el[attr] = o[attr];
4241             }
4242         }
4243         Roo.DomHelper.applyStyles(el, o.style);
4244         var cn = o.children || o.cn;
4245         if(cn){
4246             //http://bugs.kde.org/show_bug.cgi?id=71506
4247              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4248                 for(var i = 0, len = cn.length; i < len; i++) {
4249                     createDom(cn[i], el);
4250                 }
4251             }else{
4252                 createDom(cn, el);
4253             }
4254         }
4255         if(o.html){
4256             el.innerHTML = o.html;
4257         }
4258         if(parentNode){
4259            parentNode.appendChild(el);
4260         }
4261         return el;
4262     };
4263
4264     var ieTable = function(depth, s, h, e){
4265         tempTableEl.innerHTML = [s, h, e].join('');
4266         var i = -1, el = tempTableEl;
4267         while(++i < depth){
4268             el = el.firstChild;
4269         }
4270         return el;
4271     };
4272
4273     // kill repeat to save bytes
4274     var ts = '<table>',
4275         te = '</table>',
4276         tbs = ts+'<tbody>',
4277         tbe = '</tbody>'+te,
4278         trs = tbs + '<tr>',
4279         tre = '</tr>'+tbe;
4280
4281     /**
4282      * @ignore
4283      * Nasty code for IE's broken table implementation
4284      */
4285     var insertIntoTable = function(tag, where, el, html){
4286         if(!tempTableEl){
4287             tempTableEl = document.createElement('div');
4288         }
4289         var node;
4290         var before = null;
4291         if(tag == 'td'){
4292             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4293                 return;
4294             }
4295             if(where == 'beforebegin'){
4296                 before = el;
4297                 el = el.parentNode;
4298             } else{
4299                 before = el.nextSibling;
4300                 el = el.parentNode;
4301             }
4302             node = ieTable(4, trs, html, tre);
4303         }
4304         else if(tag == 'tr'){
4305             if(where == 'beforebegin'){
4306                 before = el;
4307                 el = el.parentNode;
4308                 node = ieTable(3, tbs, html, tbe);
4309             } else if(where == 'afterend'){
4310                 before = el.nextSibling;
4311                 el = el.parentNode;
4312                 node = ieTable(3, tbs, html, tbe);
4313             } else{ // INTO a TR
4314                 if(where == 'afterbegin'){
4315                     before = el.firstChild;
4316                 }
4317                 node = ieTable(4, trs, html, tre);
4318             }
4319         } else if(tag == 'tbody'){
4320             if(where == 'beforebegin'){
4321                 before = el;
4322                 el = el.parentNode;
4323                 node = ieTable(2, ts, html, te);
4324             } else if(where == 'afterend'){
4325                 before = el.nextSibling;
4326                 el = el.parentNode;
4327                 node = ieTable(2, ts, html, te);
4328             } else{
4329                 if(where == 'afterbegin'){
4330                     before = el.firstChild;
4331                 }
4332                 node = ieTable(3, tbs, html, tbe);
4333             }
4334         } else{ // TABLE
4335             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4336                 return;
4337             }
4338             if(where == 'afterbegin'){
4339                 before = el.firstChild;
4340             }
4341             node = ieTable(2, ts, html, te);
4342         }
4343         el.insertBefore(node, before);
4344         return node;
4345     };
4346
4347     return {
4348     /** True to force the use of DOM instead of html fragments @type Boolean */
4349     useDom : false,
4350
4351     /**
4352      * Returns the markup for the passed Element(s) config
4353      * @param {Object} o The Dom object spec (and children)
4354      * @return {String}
4355      */
4356     markup : function(o){
4357         return createHtml(o);
4358     },
4359
4360     /**
4361      * Applies a style specification to an element
4362      * @param {String/HTMLElement} el The element to apply styles to
4363      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4364      * a function which returns such a specification.
4365      */
4366     applyStyles : function(el, styles){
4367         if(styles){
4368            el = Roo.fly(el);
4369            if(typeof styles == "string"){
4370                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4371                var matches;
4372                while ((matches = re.exec(styles)) != null){
4373                    el.setStyle(matches[1], matches[2]);
4374                }
4375            }else if (typeof styles == "object"){
4376                for (var style in styles){
4377                   el.setStyle(style, styles[style]);
4378                }
4379            }else if (typeof styles == "function"){
4380                 Roo.DomHelper.applyStyles(el, styles.call());
4381            }
4382         }
4383     },
4384
4385     /**
4386      * Inserts an HTML fragment into the Dom
4387      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4388      * @param {HTMLElement} el The context element
4389      * @param {String} html The HTML fragmenet
4390      * @return {HTMLElement} The new node
4391      */
4392     insertHtml : function(where, el, html){
4393         where = where.toLowerCase();
4394         if(el.insertAdjacentHTML){
4395             if(tableRe.test(el.tagName)){
4396                 var rs;
4397                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4398                     return rs;
4399                 }
4400             }
4401             switch(where){
4402                 case "beforebegin":
4403                     el.insertAdjacentHTML('BeforeBegin', html);
4404                     return el.previousSibling;
4405                 case "afterbegin":
4406                     el.insertAdjacentHTML('AfterBegin', html);
4407                     return el.firstChild;
4408                 case "beforeend":
4409                     el.insertAdjacentHTML('BeforeEnd', html);
4410                     return el.lastChild;
4411                 case "afterend":
4412                     el.insertAdjacentHTML('AfterEnd', html);
4413                     return el.nextSibling;
4414             }
4415             throw 'Illegal insertion point -> "' + where + '"';
4416         }
4417         var range = el.ownerDocument.createRange();
4418         var frag;
4419         switch(where){
4420              case "beforebegin":
4421                 range.setStartBefore(el);
4422                 frag = range.createContextualFragment(html);
4423                 el.parentNode.insertBefore(frag, el);
4424                 return el.previousSibling;
4425              case "afterbegin":
4426                 if(el.firstChild){
4427                     range.setStartBefore(el.firstChild);
4428                     frag = range.createContextualFragment(html);
4429                     el.insertBefore(frag, el.firstChild);
4430                     return el.firstChild;
4431                 }else{
4432                     el.innerHTML = html;
4433                     return el.firstChild;
4434                 }
4435             case "beforeend":
4436                 if(el.lastChild){
4437                     range.setStartAfter(el.lastChild);
4438                     frag = range.createContextualFragment(html);
4439                     el.appendChild(frag);
4440                     return el.lastChild;
4441                 }else{
4442                     el.innerHTML = html;
4443                     return el.lastChild;
4444                 }
4445             case "afterend":
4446                 range.setStartAfter(el);
4447                 frag = range.createContextualFragment(html);
4448                 el.parentNode.insertBefore(frag, el.nextSibling);
4449                 return el.nextSibling;
4450             }
4451             throw 'Illegal insertion point -> "' + where + '"';
4452     },
4453
4454     /**
4455      * Creates new Dom element(s) and inserts them before el
4456      * @param {String/HTMLElement/Element} el The context element
4457      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4458      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4459      * @return {HTMLElement/Roo.Element} The new node
4460      */
4461     insertBefore : function(el, o, returnElement){
4462         return this.doInsert(el, o, returnElement, "beforeBegin");
4463     },
4464
4465     /**
4466      * Creates new Dom element(s) and inserts them after el
4467      * @param {String/HTMLElement/Element} el The context element
4468      * @param {Object} o The Dom object spec (and children)
4469      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4470      * @return {HTMLElement/Roo.Element} The new node
4471      */
4472     insertAfter : function(el, o, returnElement){
4473         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4474     },
4475
4476     /**
4477      * Creates new Dom element(s) and inserts them as the first child of el
4478      * @param {String/HTMLElement/Element} el The context element
4479      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4480      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4481      * @return {HTMLElement/Roo.Element} The new node
4482      */
4483     insertFirst : function(el, o, returnElement){
4484         return this.doInsert(el, o, returnElement, "afterBegin");
4485     },
4486
4487     // private
4488     doInsert : function(el, o, returnElement, pos, sibling){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml(pos, el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and appends them to el
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     append : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         var newNode;
4511         if(this.useDom || o.ns){
4512             newNode = createDom(o, null);
4513             el.appendChild(newNode);
4514         }else{
4515             var html = createHtml(o);
4516             newNode = this.insertHtml("beforeEnd", el, html);
4517         }
4518         return returnElement ? Roo.get(newNode, true) : newNode;
4519     },
4520
4521     /**
4522      * Creates new Dom element(s) and overwrites the contents of el with them
4523      * @param {String/HTMLElement/Element} el The context element
4524      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4525      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4526      * @return {HTMLElement/Roo.Element} The new node
4527      */
4528     overwrite : function(el, o, returnElement){
4529         el = Roo.getDom(el);
4530         if (o.ns) {
4531           
4532             while (el.childNodes.length) {
4533                 el.removeChild(el.firstChild);
4534             }
4535             createDom(o, el);
4536         } else {
4537             el.innerHTML = createHtml(o);   
4538         }
4539         
4540         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4541     },
4542
4543     /**
4544      * Creates a new Roo.DomHelper.Template from the Dom object spec
4545      * @param {Object} o The Dom object spec (and children)
4546      * @return {Roo.DomHelper.Template} The new template
4547      */
4548     createTemplate : function(o){
4549         var html = createHtml(o);
4550         return new Roo.Template(html);
4551     }
4552     };
4553 }();
4554 /*
4555  * Based on:
4556  * Ext JS Library 1.1.1
4557  * Copyright(c) 2006-2007, Ext JS, LLC.
4558  *
4559  * Originally Released Under LGPL - original licence link has changed is not relivant.
4560  *
4561  * Fork - LGPL
4562  * <script type="text/javascript">
4563  */
4564  
4565 /**
4566 * @class Roo.Template
4567 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4568 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4569 * Usage:
4570 <pre><code>
4571 var t = new Roo.Template({
4572     html :  '&lt;div name="{id}"&gt;' + 
4573         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4574         '&lt;/div&gt;',
4575     myformat: function (value, allValues) {
4576         return 'XX' + value;
4577     }
4578 });
4579 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4580 </code></pre>
4581 * For more information see this blog post with examples:
4582 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4583      - Create Elements using DOM, HTML fragments and Templates</a>. 
4584 * @constructor
4585 * @param {Object} cfg - Configuration object.
4586 */
4587 Roo.Template = function(cfg){
4588     // BC!
4589     if(cfg instanceof Array){
4590         cfg = cfg.join("");
4591     }else if(arguments.length > 1){
4592         cfg = Array.prototype.join.call(arguments, "");
4593     }
4594     
4595     
4596     if (typeof(cfg) == 'object') {
4597         Roo.apply(this,cfg)
4598     } else {
4599         // bc
4600         this.html = cfg;
4601     }
4602     if (this.url) {
4603         this.load();
4604     }
4605     
4606 };
4607 Roo.Template.prototype = {
4608     
4609     /**
4610      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4611      *                    it should be fixed so that template is observable...
4612      */
4613     url : false,
4614     /**
4615      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4616      */
4617     html : '',
4618     /**
4619      * Returns an HTML fragment of this template with the specified values applied.
4620      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4621      * @return {String} The HTML fragment
4622      */
4623     applyTemplate : function(values){
4624         try {
4625            
4626             if(this.compiled){
4627                 return this.compiled(values);
4628             }
4629             var useF = this.disableFormats !== true;
4630             var fm = Roo.util.Format, tpl = this;
4631             var fn = function(m, name, format, args){
4632                 if(format && useF){
4633                     if(format.substr(0, 5) == "this."){
4634                         return tpl.call(format.substr(5), values[name], values);
4635                     }else{
4636                         if(args){
4637                             // quoted values are required for strings in compiled templates, 
4638                             // but for non compiled we need to strip them
4639                             // quoted reversed for jsmin
4640                             var re = /^\s*['"](.*)["']\s*$/;
4641                             args = args.split(',');
4642                             for(var i = 0, len = args.length; i < len; i++){
4643                                 args[i] = args[i].replace(re, "$1");
4644                             }
4645                             args = [values[name]].concat(args);
4646                         }else{
4647                             args = [values[name]];
4648                         }
4649                         return fm[format].apply(fm, args);
4650                     }
4651                 }else{
4652                     return values[name] !== undefined ? values[name] : "";
4653                 }
4654             };
4655             return this.html.replace(this.re, fn);
4656         } catch (e) {
4657             Roo.log(e);
4658             throw e;
4659         }
4660          
4661     },
4662     
4663     loading : false,
4664       
4665     load : function ()
4666     {
4667          
4668         if (this.loading) {
4669             return;
4670         }
4671         var _t = this;
4672         
4673         this.loading = true;
4674         this.compiled = false;
4675         
4676         var cx = new Roo.data.Connection();
4677         cx.request({
4678             url : this.url,
4679             method : 'GET',
4680             success : function (response) {
4681                 _t.loading = false;
4682                 _t.html = response.responseText;
4683                 _t.url = false;
4684                 _t.compile();
4685              },
4686             failure : function(response) {
4687                 Roo.log("Template failed to load from " + _t.url);
4688                 _t.loading = false;
4689             }
4690         });
4691     },
4692
4693     /**
4694      * Sets the HTML used as the template and optionally compiles it.
4695      * @param {String} html
4696      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4697      * @return {Roo.Template} this
4698      */
4699     set : function(html, compile){
4700         this.html = html;
4701         this.compiled = null;
4702         if(compile){
4703             this.compile();
4704         }
4705         return this;
4706     },
4707     
4708     /**
4709      * True to disable format functions (defaults to false)
4710      * @type Boolean
4711      */
4712     disableFormats : false,
4713     
4714     /**
4715     * The regular expression used to match template variables 
4716     * @type RegExp
4717     * @property 
4718     */
4719     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4720     
4721     /**
4722      * Compiles the template into an internal function, eliminating the RegEx overhead.
4723      * @return {Roo.Template} this
4724      */
4725     compile : function(){
4726         var fm = Roo.util.Format;
4727         var useF = this.disableFormats !== true;
4728         var sep = Roo.isGecko ? "+" : ",";
4729         var fn = function(m, name, format, args){
4730             if(format && useF){
4731                 args = args ? ',' + args : "";
4732                 if(format.substr(0, 5) != "this."){
4733                     format = "fm." + format + '(';
4734                 }else{
4735                     format = 'this.call("'+ format.substr(5) + '", ';
4736                     args = ", values";
4737                 }
4738             }else{
4739                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4740             }
4741             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4742         };
4743         var body;
4744         // branched to use + in gecko and [].join() in others
4745         if(Roo.isGecko){
4746             body = "this.compiled = function(values){ return '" +
4747                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4748                     "';};";
4749         }else{
4750             body = ["this.compiled = function(values){ return ['"];
4751             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4752             body.push("'].join('');};");
4753             body = body.join('');
4754         }
4755         /**
4756          * eval:var:values
4757          * eval:var:fm
4758          */
4759         eval(body);
4760         return this;
4761     },
4762     
4763     // private function used to call members
4764     call : function(fnName, value, allValues){
4765         return this[fnName](value, allValues);
4766     },
4767     
4768     /**
4769      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4770      * @param {String/HTMLElement/Roo.Element} el The context element
4771      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4772      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4773      * @return {HTMLElement/Roo.Element} The new node or Element
4774      */
4775     insertFirst: function(el, values, returnElement){
4776         return this.doInsert('afterBegin', el, values, returnElement);
4777     },
4778
4779     /**
4780      * Applies the supplied values to the template and inserts the new node(s) before el.
4781      * @param {String/HTMLElement/Roo.Element} el The context element
4782      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4783      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4784      * @return {HTMLElement/Roo.Element} The new node or Element
4785      */
4786     insertBefore: function(el, values, returnElement){
4787         return this.doInsert('beforeBegin', el, values, returnElement);
4788     },
4789
4790     /**
4791      * Applies the supplied values to the template and inserts the new node(s) after el.
4792      * @param {String/HTMLElement/Roo.Element} el The context element
4793      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4794      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4795      * @return {HTMLElement/Roo.Element} The new node or Element
4796      */
4797     insertAfter : function(el, values, returnElement){
4798         return this.doInsert('afterEnd', el, values, returnElement);
4799     },
4800     
4801     /**
4802      * Applies the supplied values to the template and appends the new node(s) to el.
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     append : function(el, values, returnElement){
4809         return this.doInsert('beforeEnd', el, values, returnElement);
4810     },
4811
4812     doInsert : function(where, el, values, returnEl){
4813         el = Roo.getDom(el);
4814         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4815         return returnEl ? Roo.get(newNode, true) : newNode;
4816     },
4817
4818     /**
4819      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4820      * @param {String/HTMLElement/Roo.Element} el The context element
4821      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4822      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4823      * @return {HTMLElement/Roo.Element} The new node or Element
4824      */
4825     overwrite : function(el, values, returnElement){
4826         el = Roo.getDom(el);
4827         el.innerHTML = this.applyTemplate(values);
4828         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4829     }
4830 };
4831 /**
4832  * Alias for {@link #applyTemplate}
4833  * @method
4834  */
4835 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4836
4837 // backwards compat
4838 Roo.DomHelper.Template = Roo.Template;
4839
4840 /**
4841  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4842  * @param {String/HTMLElement} el A DOM element or its id
4843  * @returns {Roo.Template} The created template
4844  * @static
4845  */
4846 Roo.Template.from = function(el){
4847     el = Roo.getDom(el);
4848     return new Roo.Template(el.value || el.innerHTML);
4849 };/*
4850  * Based on:
4851  * Ext JS Library 1.1.1
4852  * Copyright(c) 2006-2007, Ext JS, LLC.
4853  *
4854  * Originally Released Under LGPL - original licence link has changed is not relivant.
4855  *
4856  * Fork - LGPL
4857  * <script type="text/javascript">
4858  */
4859  
4860
4861 /*
4862  * This is code is also distributed under MIT license for use
4863  * with jQuery and prototype JavaScript libraries.
4864  */
4865 /**
4866  * @class Roo.DomQuery
4867 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4868 <p>
4869 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4870
4871 <p>
4872 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4873 </p>
4874 <h4>Element Selectors:</h4>
4875 <ul class="list">
4876     <li> <b>*</b> any element</li>
4877     <li> <b>E</b> an element with the tag E</li>
4878     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4879     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4880     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4881     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4882 </ul>
4883 <h4>Attribute Selectors:</h4>
4884 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4885 <ul class="list">
4886     <li> <b>E[foo]</b> has an attribute "foo"</li>
4887     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4888     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4889     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4890     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4891     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4892     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4893 </ul>
4894 <h4>Pseudo Classes:</h4>
4895 <ul class="list">
4896     <li> <b>E:first-child</b> E is the first child of its parent</li>
4897     <li> <b>E:last-child</b> E is the last child of its parent</li>
4898     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4899     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4900     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4901     <li> <b>E:only-child</b> E is the only child of its parent</li>
4902     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4903     <li> <b>E:first</b> the first E in the resultset</li>
4904     <li> <b>E:last</b> the last E in the resultset</li>
4905     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4906     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4907     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4908     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4909     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4910     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4911     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4912     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4913     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4914 </ul>
4915 <h4>CSS Value Selectors:</h4>
4916 <ul class="list">
4917     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4918     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4919     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4920     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4921     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4922     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4923 </ul>
4924  * @singleton
4925  */
4926 Roo.DomQuery = function(){
4927     var cache = {}, simpleCache = {}, valueCache = {};
4928     var nonSpace = /\S/;
4929     var trimRe = /^\s+|\s+$/g;
4930     var tplRe = /\{(\d+)\}/g;
4931     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4932     var tagTokenRe = /^(#)?([\w-\*]+)/;
4933     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4934
4935     function child(p, index){
4936         var i = 0;
4937         var n = p.firstChild;
4938         while(n){
4939             if(n.nodeType == 1){
4940                if(++i == index){
4941                    return n;
4942                }
4943             }
4944             n = n.nextSibling;
4945         }
4946         return null;
4947     };
4948
4949     function next(n){
4950         while((n = n.nextSibling) && n.nodeType != 1);
4951         return n;
4952     };
4953
4954     function prev(n){
4955         while((n = n.previousSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function children(d){
4960         var n = d.firstChild, ni = -1;
4961             while(n){
4962                 var nx = n.nextSibling;
4963                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4964                     d.removeChild(n);
4965                 }else{
4966                     n.nodeIndex = ++ni;
4967                 }
4968                 n = nx;
4969             }
4970             return this;
4971         };
4972
4973     function byClassName(c, a, v){
4974         if(!v){
4975             return c;
4976         }
4977         var r = [], ri = -1, cn;
4978         for(var i = 0, ci; ci = c[i]; i++){
4979             if((' '+ci.className+' ').indexOf(v) != -1){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function attrValue(n, attr){
4987         if(!n.tagName && typeof n.length != "undefined"){
4988             n = n[0];
4989         }
4990         if(!n){
4991             return null;
4992         }
4993         if(attr == "for"){
4994             return n.htmlFor;
4995         }
4996         if(attr == "class" || attr == "className"){
4997             return n.className;
4998         }
4999         return n.getAttribute(attr) || n[attr];
5000
5001     };
5002
5003     function getNodes(ns, mode, tagName){
5004         var result = [], ri = -1, cs;
5005         if(!ns){
5006             return result;
5007         }
5008         tagName = tagName || "*";
5009         if(typeof ns.getElementsByTagName != "undefined"){
5010             ns = [ns];
5011         }
5012         if(!mode){
5013             for(var i = 0, ni; ni = ns[i]; i++){
5014                 cs = ni.getElementsByTagName(tagName);
5015                 for(var j = 0, ci; ci = cs[j]; j++){
5016                     result[++ri] = ci;
5017                 }
5018             }
5019         }else if(mode == "/" || mode == ">"){
5020             var utag = tagName.toUpperCase();
5021             for(var i = 0, ni, cn; ni = ns[i]; i++){
5022                 cn = ni.children || ni.childNodes;
5023                 for(var j = 0, cj; cj = cn[j]; j++){
5024                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5025                         result[++ri] = cj;
5026                     }
5027                 }
5028             }
5029         }else if(mode == "+"){
5030             var utag = tagName.toUpperCase();
5031             for(var i = 0, n; n = ns[i]; i++){
5032                 while((n = n.nextSibling) && n.nodeType != 1);
5033                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5034                     result[++ri] = n;
5035                 }
5036             }
5037         }else if(mode == "~"){
5038             for(var i = 0, n; n = ns[i]; i++){
5039                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5040                 if(n){
5041                     result[++ri] = n;
5042                 }
5043             }
5044         }
5045         return result;
5046     };
5047
5048     function concat(a, b){
5049         if(b.slice){
5050             return a.concat(b);
5051         }
5052         for(var i = 0, l = b.length; i < l; i++){
5053             a[a.length] = b[i];
5054         }
5055         return a;
5056     }
5057
5058     function byTag(cs, tagName){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!tagName){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         tagName = tagName.toLowerCase();
5067         for(var i = 0, ci; ci = cs[i]; i++){
5068             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5069                 r[++ri] = ci;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byId(cs, attr, id){
5076         if(cs.tagName || cs == document){
5077             cs = [cs];
5078         }
5079         if(!id){
5080             return cs;
5081         }
5082         var r = [], ri = -1;
5083         for(var i = 0,ci; ci = cs[i]; i++){
5084             if(ci && ci.id == id){
5085                 r[++ri] = ci;
5086                 return r;
5087             }
5088         }
5089         return r;
5090     };
5091
5092     function byAttribute(cs, attr, value, op, custom){
5093         var r = [], ri = -1, st = custom=="{";
5094         var f = Roo.DomQuery.operators[op];
5095         for(var i = 0, ci; ci = cs[i]; i++){
5096             var a;
5097             if(st){
5098                 a = Roo.DomQuery.getStyle(ci, attr);
5099             }
5100             else if(attr == "class" || attr == "className"){
5101                 a = ci.className;
5102             }else if(attr == "for"){
5103                 a = ci.htmlFor;
5104             }else if(attr == "href"){
5105                 a = ci.getAttribute("href", 2);
5106             }else{
5107                 a = ci.getAttribute(attr);
5108             }
5109             if((f && f(a, value)) || (!f && a)){
5110                 r[++ri] = ci;
5111             }
5112         }
5113         return r;
5114     };
5115
5116     function byPseudo(cs, name, value){
5117         return Roo.DomQuery.pseudos[name](cs, value);
5118     };
5119
5120     // This is for IE MSXML which does not support expandos.
5121     // IE runs the same speed using setAttribute, however FF slows way down
5122     // and Safari completely fails so they need to continue to use expandos.
5123     var isIE = window.ActiveXObject ? true : false;
5124
5125     // this eval is stop the compressor from
5126     // renaming the variable to something shorter
5127     
5128     /** eval:var:batch */
5129     var batch = 30803; 
5130
5131     var key = 30803;
5132
5133     function nodupIEXml(cs){
5134         var d = ++key;
5135         cs[0].setAttribute("_nodup", d);
5136         var r = [cs[0]];
5137         for(var i = 1, len = cs.length; i < len; i++){
5138             var c = cs[i];
5139             if(!c.getAttribute("_nodup") != d){
5140                 c.setAttribute("_nodup", d);
5141                 r[r.length] = c;
5142             }
5143         }
5144         for(var i = 0, len = cs.length; i < len; i++){
5145             cs[i].removeAttribute("_nodup");
5146         }
5147         return r;
5148     }
5149
5150     function nodup(cs){
5151         if(!cs){
5152             return [];
5153         }
5154         var len = cs.length, c, i, r = cs, cj, ri = -1;
5155         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5156             return cs;
5157         }
5158         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5159             return nodupIEXml(cs);
5160         }
5161         var d = ++key;
5162         cs[0]._nodup = d;
5163         for(i = 1; c = cs[i]; i++){
5164             if(c._nodup != d){
5165                 c._nodup = d;
5166             }else{
5167                 r = [];
5168                 for(var j = 0; j < i; j++){
5169                     r[++ri] = cs[j];
5170                 }
5171                 for(j = i+1; cj = cs[j]; j++){
5172                     if(cj._nodup != d){
5173                         cj._nodup = d;
5174                         r[++ri] = cj;
5175                     }
5176                 }
5177                 return r;
5178             }
5179         }
5180         return r;
5181     }
5182
5183     function quickDiffIEXml(c1, c2){
5184         var d = ++key;
5185         for(var i = 0, len = c1.length; i < len; i++){
5186             c1[i].setAttribute("_qdiff", d);
5187         }
5188         var r = [];
5189         for(var i = 0, len = c2.length; i < len; i++){
5190             if(c2[i].getAttribute("_qdiff") != d){
5191                 r[r.length] = c2[i];
5192             }
5193         }
5194         for(var i = 0, len = c1.length; i < len; i++){
5195            c1[i].removeAttribute("_qdiff");
5196         }
5197         return r;
5198     }
5199
5200     function quickDiff(c1, c2){
5201         var len1 = c1.length;
5202         if(!len1){
5203             return c2;
5204         }
5205         if(isIE && c1[0].selectSingleNode){
5206             return quickDiffIEXml(c1, c2);
5207         }
5208         var d = ++key;
5209         for(var i = 0; i < len1; i++){
5210             c1[i]._qdiff = d;
5211         }
5212         var r = [];
5213         for(var i = 0, len = c2.length; i < len; i++){
5214             if(c2[i]._qdiff != d){
5215                 r[r.length] = c2[i];
5216             }
5217         }
5218         return r;
5219     }
5220
5221     function quickId(ns, mode, root, id){
5222         if(ns == root){
5223            var d = root.ownerDocument || root;
5224            return d.getElementById(id);
5225         }
5226         ns = getNodes(ns, mode, "*");
5227         return byId(ns, null, id);
5228     }
5229
5230     return {
5231         getStyle : function(el, name){
5232             return Roo.fly(el).getStyle(name);
5233         },
5234         /**
5235          * Compiles a selector/xpath query into a reusable function. The returned function
5236          * takes one parameter "root" (optional), which is the context node from where the query should start.
5237          * @param {String} selector The selector/xpath query
5238          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5239          * @return {Function}
5240          */
5241         compile : function(path, type){
5242             type = type || "select";
5243             
5244             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5245             var q = path, mode, lq;
5246             var tk = Roo.DomQuery.matchers;
5247             var tklen = tk.length;
5248             var mm;
5249
5250             // accept leading mode switch
5251             var lmode = q.match(modeRe);
5252             if(lmode && lmode[1]){
5253                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5254                 q = q.replace(lmode[1], "");
5255             }
5256             // strip leading slashes
5257             while(path.substr(0, 1)=="/"){
5258                 path = path.substr(1);
5259             }
5260
5261             while(q && lq != q){
5262                 lq = q;
5263                 var tm = q.match(tagTokenRe);
5264                 if(type == "select"){
5265                     if(tm){
5266                         if(tm[1] == "#"){
5267                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5268                         }else{
5269                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5270                         }
5271                         q = q.replace(tm[0], "");
5272                     }else if(q.substr(0, 1) != '@'){
5273                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5274                     }
5275                 }else{
5276                     if(tm){
5277                         if(tm[1] == "#"){
5278                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5279                         }else{
5280                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5281                         }
5282                         q = q.replace(tm[0], "");
5283                     }
5284                 }
5285                 while(!(mm = q.match(modeRe))){
5286                     var matched = false;
5287                     for(var j = 0; j < tklen; j++){
5288                         var t = tk[j];
5289                         var m = q.match(t.re);
5290                         if(m){
5291                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5292                                                     return m[i];
5293                                                 });
5294                             q = q.replace(m[0], "");
5295                             matched = true;
5296                             break;
5297                         }
5298                     }
5299                     // prevent infinite loop on bad selector
5300                     if(!matched){
5301                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5302                     }
5303                 }
5304                 if(mm[1]){
5305                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5306                     q = q.replace(mm[1], "");
5307                 }
5308             }
5309             fn[fn.length] = "return nodup(n);\n}";
5310             
5311              /** 
5312               * list of variables that need from compression as they are used by eval.
5313              *  eval:var:batch 
5314              *  eval:var:nodup
5315              *  eval:var:byTag
5316              *  eval:var:ById
5317              *  eval:var:getNodes
5318              *  eval:var:quickId
5319              *  eval:var:mode
5320              *  eval:var:root
5321              *  eval:var:n
5322              *  eval:var:byClassName
5323              *  eval:var:byPseudo
5324              *  eval:var:byAttribute
5325              *  eval:var:attrValue
5326              * 
5327              **/ 
5328             eval(fn.join(""));
5329             return f;
5330         },
5331
5332         /**
5333          * Selects a group of elements.
5334          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5335          * @param {Node} root (optional) The start of the query (defaults to document).
5336          * @return {Array}
5337          */
5338         select : function(path, root, type){
5339             if(!root || root == document){
5340                 root = document;
5341             }
5342             if(typeof root == "string"){
5343                 root = document.getElementById(root);
5344             }
5345             var paths = path.split(",");
5346             var results = [];
5347             for(var i = 0, len = paths.length; i < len; i++){
5348                 var p = paths[i].replace(trimRe, "");
5349                 if(!cache[p]){
5350                     cache[p] = Roo.DomQuery.compile(p);
5351                     if(!cache[p]){
5352                         throw p + " is not a valid selector";
5353                     }
5354                 }
5355                 var result = cache[p](root);
5356                 if(result && result != document){
5357                     results = results.concat(result);
5358                 }
5359             }
5360             if(paths.length > 1){
5361                 return nodup(results);
5362             }
5363             return results;
5364         },
5365
5366         /**
5367          * Selects a single element.
5368          * @param {String} selector The selector/xpath query
5369          * @param {Node} root (optional) The start of the query (defaults to document).
5370          * @return {Element}
5371          */
5372         selectNode : function(path, root){
5373             return Roo.DomQuery.select(path, root)[0];
5374         },
5375
5376         /**
5377          * Selects the value of a node, optionally replacing null with the defaultValue.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {String} defaultValue
5381          */
5382         selectValue : function(path, root, defaultValue){
5383             path = path.replace(trimRe, "");
5384             if(!valueCache[path]){
5385                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5386             }
5387             var n = valueCache[path](root);
5388             n = n[0] ? n[0] : n;
5389             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5390             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5391         },
5392
5393         /**
5394          * Selects the value of a node, parsing integers and floats.
5395          * @param {String} selector The selector/xpath query
5396          * @param {Node} root (optional) The start of the query (defaults to document).
5397          * @param {Number} defaultValue
5398          * @return {Number}
5399          */
5400         selectNumber : function(path, root, defaultValue){
5401             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5402             return parseFloat(v);
5403         },
5404
5405         /**
5406          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5407          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5408          * @param {String} selector The simple selector to test
5409          * @return {Boolean}
5410          */
5411         is : function(el, ss){
5412             if(typeof el == "string"){
5413                 el = document.getElementById(el);
5414             }
5415             var isArray = (el instanceof Array);
5416             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5417             return isArray ? (result.length == el.length) : (result.length > 0);
5418         },
5419
5420         /**
5421          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5422          * @param {Array} el An array of elements to filter
5423          * @param {String} selector The simple selector to test
5424          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5425          * the selector instead of the ones that match
5426          * @return {Array}
5427          */
5428         filter : function(els, ss, nonMatches){
5429             ss = ss.replace(trimRe, "");
5430             if(!simpleCache[ss]){
5431                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5432             }
5433             var result = simpleCache[ss](els);
5434             return nonMatches ? quickDiff(result, els) : result;
5435         },
5436
5437         /**
5438          * Collection of matching regular expressions and code snippets.
5439          */
5440         matchers : [{
5441                 re: /^\.([\w-]+)/,
5442                 select: 'n = byClassName(n, null, " {1} ");'
5443             }, {
5444                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5445                 select: 'n = byPseudo(n, "{1}", "{2}");'
5446             },{
5447                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5448                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5449             }, {
5450                 re: /^#([\w-]+)/,
5451                 select: 'n = byId(n, null, "{1}");'
5452             },{
5453                 re: /^@([\w-]+)/,
5454                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5455             }
5456         ],
5457
5458         /**
5459          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5460          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5461          */
5462         operators : {
5463             "=" : function(a, v){
5464                 return a == v;
5465             },
5466             "!=" : function(a, v){
5467                 return a != v;
5468             },
5469             "^=" : function(a, v){
5470                 return a && a.substr(0, v.length) == v;
5471             },
5472             "$=" : function(a, v){
5473                 return a && a.substr(a.length-v.length) == v;
5474             },
5475             "*=" : function(a, v){
5476                 return a && a.indexOf(v) !== -1;
5477             },
5478             "%=" : function(a, v){
5479                 return (a % v) == 0;
5480             },
5481             "|=" : function(a, v){
5482                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5483             },
5484             "~=" : function(a, v){
5485                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5486             }
5487         },
5488
5489         /**
5490          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5491          * and the argument (if any) supplied in the selector.
5492          */
5493         pseudos : {
5494             "first-child" : function(c){
5495                 var r = [], ri = -1, n;
5496                 for(var i = 0, ci; ci = n = c[i]; i++){
5497                     while((n = n.previousSibling) && n.nodeType != 1);
5498                     if(!n){
5499                         r[++ri] = ci;
5500                     }
5501                 }
5502                 return r;
5503             },
5504
5505             "last-child" : function(c){
5506                 var r = [], ri = -1, n;
5507                 for(var i = 0, ci; ci = n = c[i]; i++){
5508                     while((n = n.nextSibling) && n.nodeType != 1);
5509                     if(!n){
5510                         r[++ri] = ci;
5511                     }
5512                 }
5513                 return r;
5514             },
5515
5516             "nth-child" : function(c, a) {
5517                 var r = [], ri = -1;
5518                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5519                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5520                 for(var i = 0, n; n = c[i]; i++){
5521                     var pn = n.parentNode;
5522                     if (batch != pn._batch) {
5523                         var j = 0;
5524                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5525                             if(cn.nodeType == 1){
5526                                cn.nodeIndex = ++j;
5527                             }
5528                         }
5529                         pn._batch = batch;
5530                     }
5531                     if (f == 1) {
5532                         if (l == 0 || n.nodeIndex == l){
5533                             r[++ri] = n;
5534                         }
5535                     } else if ((n.nodeIndex + l) % f == 0){
5536                         r[++ri] = n;
5537                     }
5538                 }
5539
5540                 return r;
5541             },
5542
5543             "only-child" : function(c){
5544                 var r = [], ri = -1;;
5545                 for(var i = 0, ci; ci = c[i]; i++){
5546                     if(!prev(ci) && !next(ci)){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "empty" : function(c){
5554                 var r = [], ri = -1;
5555                 for(var i = 0, ci; ci = c[i]; i++){
5556                     var cns = ci.childNodes, j = 0, cn, empty = true;
5557                     while(cn = cns[j]){
5558                         ++j;
5559                         if(cn.nodeType == 1 || cn.nodeType == 3){
5560                             empty = false;
5561                             break;
5562                         }
5563                     }
5564                     if(empty){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "contains" : function(c, v){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "nodeValue" : function(c, v){
5582                 var r = [], ri = -1;
5583                 for(var i = 0, ci; ci = c[i]; i++){
5584                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5585                         r[++ri] = ci;
5586                     }
5587                 }
5588                 return r;
5589             },
5590
5591             "checked" : function(c){
5592                 var r = [], ri = -1;
5593                 for(var i = 0, ci; ci = c[i]; i++){
5594                     if(ci.checked == true){
5595                         r[++ri] = ci;
5596                     }
5597                 }
5598                 return r;
5599             },
5600
5601             "not" : function(c, ss){
5602                 return Roo.DomQuery.filter(c, ss, true);
5603             },
5604
5605             "odd" : function(c){
5606                 return this["nth-child"](c, "odd");
5607             },
5608
5609             "even" : function(c){
5610                 return this["nth-child"](c, "even");
5611             },
5612
5613             "nth" : function(c, a){
5614                 return c[a-1] || [];
5615             },
5616
5617             "first" : function(c){
5618                 return c[0] || [];
5619             },
5620
5621             "last" : function(c){
5622                 return c[c.length-1] || [];
5623             },
5624
5625             "has" : function(c, ss){
5626                 var s = Roo.DomQuery.select;
5627                 var r = [], ri = -1;
5628                 for(var i = 0, ci; ci = c[i]; i++){
5629                     if(s(ss, ci).length > 0){
5630                         r[++ri] = ci;
5631                     }
5632                 }
5633                 return r;
5634             },
5635
5636             "next" : function(c, ss){
5637                 var is = Roo.DomQuery.is;
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     var n = next(ci);
5641                     if(n && is(n, ss)){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "prev" : function(c, ss){
5649                 var is = Roo.DomQuery.is;
5650                 var r = [], ri = -1;
5651                 for(var i = 0, ci; ci = c[i]; i++){
5652                     var n = prev(ci);
5653                     if(n && is(n, ss)){
5654                         r[++ri] = ci;
5655                     }
5656                 }
5657                 return r;
5658             }
5659         }
5660     };
5661 }();
5662
5663 /**
5664  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5665  * @param {String} path The selector/xpath query
5666  * @param {Node} root (optional) The start of the query (defaults to document).
5667  * @return {Array}
5668  * @member Roo
5669  * @method query
5670  */
5671 Roo.query = Roo.DomQuery.select;
5672 /*
5673  * Based on:
5674  * Ext JS Library 1.1.1
5675  * Copyright(c) 2006-2007, Ext JS, LLC.
5676  *
5677  * Originally Released Under LGPL - original licence link has changed is not relivant.
5678  *
5679  * Fork - LGPL
5680  * <script type="text/javascript">
5681  */
5682
5683 /**
5684  * @class Roo.util.Observable
5685  * Base class that provides a common interface for publishing events. Subclasses are expected to
5686  * to have a property "events" with all the events defined.<br>
5687  * For example:
5688  * <pre><code>
5689  Employee = function(name){
5690     this.name = name;
5691     this.addEvents({
5692         "fired" : true,
5693         "quit" : true
5694     });
5695  }
5696  Roo.extend(Employee, Roo.util.Observable);
5697 </code></pre>
5698  * @param {Object} config properties to use (incuding events / listeners)
5699  */
5700
5701 Roo.util.Observable = function(cfg){
5702     
5703     cfg = cfg|| {};
5704     this.addEvents(cfg.events || {});
5705     if (cfg.events) {
5706         delete cfg.events; // make sure
5707     }
5708      
5709     Roo.apply(this, cfg);
5710     
5711     if(this.listeners){
5712         this.on(this.listeners);
5713         delete this.listeners;
5714     }
5715 };
5716 Roo.util.Observable.prototype = {
5717     /** 
5718  * @cfg {Object} listeners  list of events and functions to call for this object, 
5719  * For example :
5720  * <pre><code>
5721     listeners :  { 
5722        'click' : function(e) {
5723            ..... 
5724         } ,
5725         .... 
5726     } 
5727   </code></pre>
5728  */
5729     
5730     
5731     /**
5732      * Fires the specified event with the passed parameters (minus the event name).
5733      * @param {String} eventName
5734      * @param {Object...} args Variable number of parameters are passed to handlers
5735      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5736      */
5737     fireEvent : function(){
5738         var ce = this.events[arguments[0].toLowerCase()];
5739         if(typeof ce == "object"){
5740             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5741         }else{
5742             return true;
5743         }
5744     },
5745
5746     // private
5747     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5748
5749     /**
5750      * Appends an event handler to this component
5751      * @param {String}   eventName The type of event to listen for
5752      * @param {Function} handler The method the event invokes
5753      * @param {Object}   scope (optional) The scope in which to execute the handler
5754      * function. The handler function's "this" context.
5755      * @param {Object}   options (optional) An object containing handler configuration
5756      * properties. This may contain any of the following properties:<ul>
5757      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5758      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5759      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5760      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5761      * by the specified number of milliseconds. If the event fires again within that time, the original
5762      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5763      * </ul><br>
5764      * <p>
5765      * <b>Combining Options</b><br>
5766      * Using the options argument, it is possible to combine different types of listeners:<br>
5767      * <br>
5768      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5769                 <pre><code>
5770                 el.on('click', this.onClick, this, {
5771                         single: true,
5772                 delay: 100,
5773                 forumId: 4
5774                 });
5775                 </code></pre>
5776      * <p>
5777      * <b>Attaching multiple handlers in 1 call</b><br>
5778      * The method also allows for a single argument to be passed which is a config object containing properties
5779      * which specify multiple handlers.
5780      * <pre><code>
5781                 el.on({
5782                         'click': {
5783                         fn: this.onClick,
5784                         scope: this,
5785                         delay: 100
5786                 }, 
5787                 'mouseover': {
5788                         fn: this.onMouseOver,
5789                         scope: this
5790                 },
5791                 'mouseout': {
5792                         fn: this.onMouseOut,
5793                         scope: this
5794                 }
5795                 });
5796                 </code></pre>
5797      * <p>
5798      * Or a shorthand syntax which passes the same scope object to all handlers:
5799         <pre><code>
5800                 el.on({
5801                         'click': this.onClick,
5802                 'mouseover': this.onMouseOver,
5803                 'mouseout': this.onMouseOut,
5804                 scope: this
5805                 });
5806                 </code></pre>
5807      */
5808     addListener : function(eventName, fn, scope, o){
5809         if(typeof eventName == "object"){
5810             o = eventName;
5811             for(var e in o){
5812                 if(this.filterOptRe.test(e)){
5813                     continue;
5814                 }
5815                 if(typeof o[e] == "function"){
5816                     // shared options
5817                     this.addListener(e, o[e], o.scope,  o);
5818                 }else{
5819                     // individual options
5820                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5821                 }
5822             }
5823             return;
5824         }
5825         o = (!o || typeof o == "boolean") ? {} : o;
5826         eventName = eventName.toLowerCase();
5827         var ce = this.events[eventName] || true;
5828         if(typeof ce == "boolean"){
5829             ce = new Roo.util.Event(this, eventName);
5830             this.events[eventName] = ce;
5831         }
5832         ce.addListener(fn, scope, o);
5833     },
5834
5835     /**
5836      * Removes a listener
5837      * @param {String}   eventName     The type of event to listen for
5838      * @param {Function} handler        The handler to remove
5839      * @param {Object}   scope  (optional) The scope (this object) for the handler
5840      */
5841     removeListener : function(eventName, fn, scope){
5842         var ce = this.events[eventName.toLowerCase()];
5843         if(typeof ce == "object"){
5844             ce.removeListener(fn, scope);
5845         }
5846     },
5847
5848     /**
5849      * Removes all listeners for this object
5850      */
5851     purgeListeners : function(){
5852         for(var evt in this.events){
5853             if(typeof this.events[evt] == "object"){
5854                  this.events[evt].clearListeners();
5855             }
5856         }
5857     },
5858
5859     relayEvents : function(o, events){
5860         var createHandler = function(ename){
5861             return function(){
5862                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5863             };
5864         };
5865         for(var i = 0, len = events.length; i < len; i++){
5866             var ename = events[i];
5867             if(!this.events[ename]){ this.events[ename] = true; };
5868             o.on(ename, createHandler(ename), this);
5869         }
5870     },
5871
5872     /**
5873      * Used to define events on this Observable
5874      * @param {Object} object The object with the events defined
5875      */
5876     addEvents : function(o){
5877         if(!this.events){
5878             this.events = {};
5879         }
5880         Roo.applyIf(this.events, o);
5881     },
5882
5883     /**
5884      * Checks to see if this object has any listeners for a specified event
5885      * @param {String} eventName The name of the event to check for
5886      * @return {Boolean} True if the event is being listened for, else false
5887      */
5888     hasListener : function(eventName){
5889         var e = this.events[eventName];
5890         return typeof e == "object" && e.listeners.length > 0;
5891     }
5892 };
5893 /**
5894  * Appends an event handler to this element (shorthand for addListener)
5895  * @param {String}   eventName     The type of event to listen for
5896  * @param {Function} handler        The method the event invokes
5897  * @param {Object}   scope (optional) The scope in which to execute the handler
5898  * function. The handler function's "this" context.
5899  * @param {Object}   options  (optional)
5900  * @method
5901  */
5902 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5903 /**
5904  * Removes a listener (shorthand for removeListener)
5905  * @param {String}   eventName     The type of event to listen for
5906  * @param {Function} handler        The handler to remove
5907  * @param {Object}   scope  (optional) The scope (this object) for the handler
5908  * @method
5909  */
5910 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5911
5912 /**
5913  * Starts capture on the specified Observable. All events will be passed
5914  * to the supplied function with the event name + standard signature of the event
5915  * <b>before</b> the event is fired. If the supplied function returns false,
5916  * the event will not fire.
5917  * @param {Observable} o The Observable to capture
5918  * @param {Function} fn The function to call
5919  * @param {Object} scope (optional) The scope (this object) for the fn
5920  * @static
5921  */
5922 Roo.util.Observable.capture = function(o, fn, scope){
5923     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5924 };
5925
5926 /**
5927  * Removes <b>all</b> added captures from the Observable.
5928  * @param {Observable} o The Observable to release
5929  * @static
5930  */
5931 Roo.util.Observable.releaseCapture = function(o){
5932     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5933 };
5934
5935 (function(){
5936
5937     var createBuffered = function(h, o, scope){
5938         var task = new Roo.util.DelayedTask();
5939         return function(){
5940             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5941         };
5942     };
5943
5944     var createSingle = function(h, e, fn, scope){
5945         return function(){
5946             e.removeListener(fn, scope);
5947             return h.apply(scope, arguments);
5948         };
5949     };
5950
5951     var createDelayed = function(h, o, scope){
5952         return function(){
5953             var args = Array.prototype.slice.call(arguments, 0);
5954             setTimeout(function(){
5955                 h.apply(scope, args);
5956             }, o.delay || 10);
5957         };
5958     };
5959
5960     Roo.util.Event = function(obj, name){
5961         this.name = name;
5962         this.obj = obj;
5963         this.listeners = [];
5964     };
5965
5966     Roo.util.Event.prototype = {
5967         addListener : function(fn, scope, options){
5968             var o = options || {};
5969             scope = scope || this.obj;
5970             if(!this.isListening(fn, scope)){
5971                 var l = {fn: fn, scope: scope, options: o};
5972                 var h = fn;
5973                 if(o.delay){
5974                     h = createDelayed(h, o, scope);
5975                 }
5976                 if(o.single){
5977                     h = createSingle(h, this, fn, scope);
5978                 }
5979                 if(o.buffer){
5980                     h = createBuffered(h, o, scope);
5981                 }
5982                 l.fireFn = h;
5983                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5984                     this.listeners.push(l);
5985                 }else{
5986                     this.listeners = this.listeners.slice(0);
5987                     this.listeners.push(l);
5988                 }
5989             }
5990         },
5991
5992         findListener : function(fn, scope){
5993             scope = scope || this.obj;
5994             var ls = this.listeners;
5995             for(var i = 0, len = ls.length; i < len; i++){
5996                 var l = ls[i];
5997                 if(l.fn == fn && l.scope == scope){
5998                     return i;
5999                 }
6000             }
6001             return -1;
6002         },
6003
6004         isListening : function(fn, scope){
6005             return this.findListener(fn, scope) != -1;
6006         },
6007
6008         removeListener : function(fn, scope){
6009             var index;
6010             if((index = this.findListener(fn, scope)) != -1){
6011                 if(!this.firing){
6012                     this.listeners.splice(index, 1);
6013                 }else{
6014                     this.listeners = this.listeners.slice(0);
6015                     this.listeners.splice(index, 1);
6016                 }
6017                 return true;
6018             }
6019             return false;
6020         },
6021
6022         clearListeners : function(){
6023             this.listeners = [];
6024         },
6025
6026         fire : function(){
6027             var ls = this.listeners, scope, len = ls.length;
6028             if(len > 0){
6029                 this.firing = true;
6030                 var args = Array.prototype.slice.call(arguments, 0);
6031                 for(var i = 0; i < len; i++){
6032                     var l = ls[i];
6033                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6034                         this.firing = false;
6035                         return false;
6036                     }
6037                 }
6038                 this.firing = false;
6039             }
6040             return true;
6041         }
6042     };
6043 })();/*
6044  * Based on:
6045  * Ext JS Library 1.1.1
6046  * Copyright(c) 2006-2007, Ext JS, LLC.
6047  *
6048  * Originally Released Under LGPL - original licence link has changed is not relivant.
6049  *
6050  * Fork - LGPL
6051  * <script type="text/javascript">
6052  */
6053
6054 /**
6055  * @class Roo.EventManager
6056  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6057  * several useful events directly.
6058  * See {@link Roo.EventObject} for more details on normalized event objects.
6059  * @singleton
6060  */
6061 Roo.EventManager = function(){
6062     var docReadyEvent, docReadyProcId, docReadyState = false;
6063     var resizeEvent, resizeTask, textEvent, textSize;
6064     var E = Roo.lib.Event;
6065     var D = Roo.lib.Dom;
6066
6067     
6068     
6069
6070     var fireDocReady = function(){
6071         if(!docReadyState){
6072             docReadyState = true;
6073             Roo.isReady = true;
6074             if(docReadyProcId){
6075                 clearInterval(docReadyProcId);
6076             }
6077             if(Roo.isGecko || Roo.isOpera) {
6078                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6079             }
6080             if(Roo.isIE){
6081                 var defer = document.getElementById("ie-deferred-loader");
6082                 if(defer){
6083                     defer.onreadystatechange = null;
6084                     defer.parentNode.removeChild(defer);
6085                 }
6086             }
6087             if(docReadyEvent){
6088                 docReadyEvent.fire();
6089                 docReadyEvent.clearListeners();
6090             }
6091         }
6092     };
6093     
6094     var initDocReady = function(){
6095         docReadyEvent = new Roo.util.Event();
6096         if(Roo.isGecko || Roo.isOpera) {
6097             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6098         }else if(Roo.isIE){
6099             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6100             var defer = document.getElementById("ie-deferred-loader");
6101             defer.onreadystatechange = function(){
6102                 if(this.readyState == "complete"){
6103                     fireDocReady();
6104                 }
6105             };
6106         }else if(Roo.isSafari){ 
6107             docReadyProcId = setInterval(function(){
6108                 var rs = document.readyState;
6109                 if(rs == "complete") {
6110                     fireDocReady();     
6111                  }
6112             }, 10);
6113         }
6114         // no matter what, make sure it fires on load
6115         E.on(window, "load", fireDocReady);
6116     };
6117
6118     var createBuffered = function(h, o){
6119         var task = new Roo.util.DelayedTask(h);
6120         return function(e){
6121             // create new event object impl so new events don't wipe out properties
6122             e = new Roo.EventObjectImpl(e);
6123             task.delay(o.buffer, h, null, [e]);
6124         };
6125     };
6126
6127     var createSingle = function(h, el, ename, fn){
6128         return function(e){
6129             Roo.EventManager.removeListener(el, ename, fn);
6130             h(e);
6131         };
6132     };
6133
6134     var createDelayed = function(h, o){
6135         return function(e){
6136             // create new event object impl so new events don't wipe out properties
6137             e = new Roo.EventObjectImpl(e);
6138             setTimeout(function(){
6139                 h(e);
6140             }, o.delay || 10);
6141         };
6142     };
6143     var transitionEndVal = false;
6144     
6145     var transitionEnd = function()
6146     {
6147         if (transitionEndVal) {
6148             return transitionEndVal;
6149         }
6150         var el = document.createElement('div');
6151
6152         var transEndEventNames = {
6153             WebkitTransition : 'webkitTransitionEnd',
6154             MozTransition    : 'transitionend',
6155             OTransition      : 'oTransitionEnd otransitionend',
6156             transition       : 'transitionend'
6157         };
6158     
6159         for (var name in transEndEventNames) {
6160             if (el.style[name] !== undefined) {
6161                 transitionEndVal = transEndEventNames[name];
6162                 return  transitionEndVal ;
6163             }
6164         }
6165     }
6166     
6167
6168     var listen = function(element, ename, opt, fn, scope){
6169         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6170         fn = fn || o.fn; scope = scope || o.scope;
6171         var el = Roo.getDom(element);
6172         
6173         
6174         if(!el){
6175             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6176         }
6177         
6178         if (ename == 'transitionend') {
6179             ename = transitionEnd();
6180         }
6181         var h = function(e){
6182             e = Roo.EventObject.setEvent(e);
6183             var t;
6184             if(o.delegate){
6185                 t = e.getTarget(o.delegate, el);
6186                 if(!t){
6187                     return;
6188                 }
6189             }else{
6190                 t = e.target;
6191             }
6192             if(o.stopEvent === true){
6193                 e.stopEvent();
6194             }
6195             if(o.preventDefault === true){
6196                e.preventDefault();
6197             }
6198             if(o.stopPropagation === true){
6199                 e.stopPropagation();
6200             }
6201
6202             if(o.normalized === false){
6203                 e = e.browserEvent;
6204             }
6205
6206             fn.call(scope || el, e, t, o);
6207         };
6208         if(o.delay){
6209             h = createDelayed(h, o);
6210         }
6211         if(o.single){
6212             h = createSingle(h, el, ename, fn);
6213         }
6214         if(o.buffer){
6215             h = createBuffered(h, o);
6216         }
6217         fn._handlers = fn._handlers || [];
6218         
6219         
6220         fn._handlers.push([Roo.id(el), ename, h]);
6221         
6222         
6223          
6224         E.on(el, ename, h);
6225         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6226             el.addEventListener("DOMMouseScroll", h, false);
6227             E.on(window, 'unload', function(){
6228                 el.removeEventListener("DOMMouseScroll", h, false);
6229             });
6230         }
6231         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6232             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6233         }
6234         return h;
6235     };
6236
6237     var stopListening = function(el, ename, fn){
6238         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6239         if(hds){
6240             for(var i = 0, len = hds.length; i < len; i++){
6241                 var h = hds[i];
6242                 if(h[0] == id && h[1] == ename){
6243                     hd = h[2];
6244                     hds.splice(i, 1);
6245                     break;
6246                 }
6247             }
6248         }
6249         E.un(el, ename, hd);
6250         el = Roo.getDom(el);
6251         if(ename == "mousewheel" && el.addEventListener){
6252             el.removeEventListener("DOMMouseScroll", hd, false);
6253         }
6254         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6255             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6256         }
6257     };
6258
6259     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6260     
6261     var pub = {
6262         
6263         
6264         /** 
6265          * Fix for doc tools
6266          * @scope Roo.EventManager
6267          */
6268         
6269         
6270         /** 
6271          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6272          * object with a Roo.EventObject
6273          * @param {Function} fn        The method the event invokes
6274          * @param {Object}   scope    An object that becomes the scope of the handler
6275          * @param {boolean}  override If true, the obj passed in becomes
6276          *                             the execution scope of the listener
6277          * @return {Function} The wrapped function
6278          * @deprecated
6279          */
6280         wrap : function(fn, scope, override){
6281             return function(e){
6282                 Roo.EventObject.setEvent(e);
6283                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6284             };
6285         },
6286         
6287         /**
6288      * Appends an event handler to an element (shorthand for addListener)
6289      * @param {String/HTMLElement}   element        The html element or id to assign the
6290      * @param {String}   eventName The type of event to listen for
6291      * @param {Function} handler The method the event invokes
6292      * @param {Object}   scope (optional) The scope in which to execute the handler
6293      * function. The handler function's "this" context.
6294      * @param {Object}   options (optional) An object containing handler configuration
6295      * properties. This may contain any of the following properties:<ul>
6296      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6297      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6298      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6299      * <li>preventDefault {Boolean} True to prevent the default action</li>
6300      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6301      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6302      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6303      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6304      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6305      * by the specified number of milliseconds. If the event fires again within that time, the original
6306      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6307      * </ul><br>
6308      * <p>
6309      * <b>Combining Options</b><br>
6310      * Using the options argument, it is possible to combine different types of listeners:<br>
6311      * <br>
6312      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6313      * Code:<pre><code>
6314 el.on('click', this.onClick, this, {
6315     single: true,
6316     delay: 100,
6317     stopEvent : true,
6318     forumId: 4
6319 });</code></pre>
6320      * <p>
6321      * <b>Attaching multiple handlers in 1 call</b><br>
6322       * The method also allows for a single argument to be passed which is a config object containing properties
6323      * which specify multiple handlers.
6324      * <p>
6325      * Code:<pre><code>
6326 el.on({
6327     'click' : {
6328         fn: this.onClick
6329         scope: this,
6330         delay: 100
6331     },
6332     'mouseover' : {
6333         fn: this.onMouseOver
6334         scope: this
6335     },
6336     'mouseout' : {
6337         fn: this.onMouseOut
6338         scope: this
6339     }
6340 });</code></pre>
6341      * <p>
6342      * Or a shorthand syntax:<br>
6343      * Code:<pre><code>
6344 el.on({
6345     'click' : this.onClick,
6346     'mouseover' : this.onMouseOver,
6347     'mouseout' : this.onMouseOut
6348     scope: this
6349 });</code></pre>
6350      */
6351         addListener : function(element, eventName, fn, scope, options){
6352             if(typeof eventName == "object"){
6353                 var o = eventName;
6354                 for(var e in o){
6355                     if(propRe.test(e)){
6356                         continue;
6357                     }
6358                     if(typeof o[e] == "function"){
6359                         // shared options
6360                         listen(element, e, o, o[e], o.scope);
6361                     }else{
6362                         // individual options
6363                         listen(element, e, o[e]);
6364                     }
6365                 }
6366                 return;
6367             }
6368             return listen(element, eventName, options, fn, scope);
6369         },
6370         
6371         /**
6372          * Removes an event handler
6373          *
6374          * @param {String/HTMLElement}   element        The id or html element to remove the 
6375          *                             event from
6376          * @param {String}   eventName     The type of event
6377          * @param {Function} fn
6378          * @return {Boolean} True if a listener was actually removed
6379          */
6380         removeListener : function(element, eventName, fn){
6381             return stopListening(element, eventName, fn);
6382         },
6383         
6384         /**
6385          * Fires when the document is ready (before onload and before images are loaded). Can be 
6386          * accessed shorthanded Roo.onReady().
6387          * @param {Function} fn        The method the event invokes
6388          * @param {Object}   scope    An  object that becomes the scope of the handler
6389          * @param {boolean}  options
6390          */
6391         onDocumentReady : function(fn, scope, options){
6392             if(docReadyState){ // if it already fired
6393                 docReadyEvent.addListener(fn, scope, options);
6394                 docReadyEvent.fire();
6395                 docReadyEvent.clearListeners();
6396                 return;
6397             }
6398             if(!docReadyEvent){
6399                 initDocReady();
6400             }
6401             docReadyEvent.addListener(fn, scope, options);
6402         },
6403         
6404         /**
6405          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6406          * @param {Function} fn        The method the event invokes
6407          * @param {Object}   scope    An object that becomes the scope of the handler
6408          * @param {boolean}  options
6409          */
6410         onWindowResize : function(fn, scope, options){
6411             if(!resizeEvent){
6412                 resizeEvent = new Roo.util.Event();
6413                 resizeTask = new Roo.util.DelayedTask(function(){
6414                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6415                 });
6416                 E.on(window, "resize", function(){
6417                     if(Roo.isIE){
6418                         resizeTask.delay(50);
6419                     }else{
6420                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6421                     }
6422                 });
6423             }
6424             resizeEvent.addListener(fn, scope, options);
6425         },
6426
6427         /**
6428          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6429          * @param {Function} fn        The method the event invokes
6430          * @param {Object}   scope    An object that becomes the scope of the handler
6431          * @param {boolean}  options
6432          */
6433         onTextResize : function(fn, scope, options){
6434             if(!textEvent){
6435                 textEvent = new Roo.util.Event();
6436                 var textEl = new Roo.Element(document.createElement('div'));
6437                 textEl.dom.className = 'x-text-resize';
6438                 textEl.dom.innerHTML = 'X';
6439                 textEl.appendTo(document.body);
6440                 textSize = textEl.dom.offsetHeight;
6441                 setInterval(function(){
6442                     if(textEl.dom.offsetHeight != textSize){
6443                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6444                     }
6445                 }, this.textResizeInterval);
6446             }
6447             textEvent.addListener(fn, scope, options);
6448         },
6449
6450         /**
6451          * Removes the passed window resize listener.
6452          * @param {Function} fn        The method the event invokes
6453          * @param {Object}   scope    The scope of handler
6454          */
6455         removeResizeListener : function(fn, scope){
6456             if(resizeEvent){
6457                 resizeEvent.removeListener(fn, scope);
6458             }
6459         },
6460
6461         // private
6462         fireResize : function(){
6463             if(resizeEvent){
6464                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6465             }   
6466         },
6467         /**
6468          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6469          */
6470         ieDeferSrc : false,
6471         /**
6472          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6473          */
6474         textResizeInterval : 50
6475     };
6476     
6477     /**
6478      * Fix for doc tools
6479      * @scopeAlias pub=Roo.EventManager
6480      */
6481     
6482      /**
6483      * Appends an event handler to an element (shorthand for addListener)
6484      * @param {String/HTMLElement}   element        The html element or id to assign the
6485      * @param {String}   eventName The type of event to listen for
6486      * @param {Function} handler The method the event invokes
6487      * @param {Object}   scope (optional) The scope in which to execute the handler
6488      * function. The handler function's "this" context.
6489      * @param {Object}   options (optional) An object containing handler configuration
6490      * properties. This may contain any of the following properties:<ul>
6491      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6492      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6493      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6494      * <li>preventDefault {Boolean} True to prevent the default action</li>
6495      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6496      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6497      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6498      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6499      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6500      * by the specified number of milliseconds. If the event fires again within that time, the original
6501      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6502      * </ul><br>
6503      * <p>
6504      * <b>Combining Options</b><br>
6505      * Using the options argument, it is possible to combine different types of listeners:<br>
6506      * <br>
6507      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6508      * Code:<pre><code>
6509 el.on('click', this.onClick, this, {
6510     single: true,
6511     delay: 100,
6512     stopEvent : true,
6513     forumId: 4
6514 });</code></pre>
6515      * <p>
6516      * <b>Attaching multiple handlers in 1 call</b><br>
6517       * The method also allows for a single argument to be passed which is a config object containing properties
6518      * which specify multiple handlers.
6519      * <p>
6520      * Code:<pre><code>
6521 el.on({
6522     'click' : {
6523         fn: this.onClick
6524         scope: this,
6525         delay: 100
6526     },
6527     'mouseover' : {
6528         fn: this.onMouseOver
6529         scope: this
6530     },
6531     'mouseout' : {
6532         fn: this.onMouseOut
6533         scope: this
6534     }
6535 });</code></pre>
6536      * <p>
6537      * Or a shorthand syntax:<br>
6538      * Code:<pre><code>
6539 el.on({
6540     'click' : this.onClick,
6541     'mouseover' : this.onMouseOver,
6542     'mouseout' : this.onMouseOut
6543     scope: this
6544 });</code></pre>
6545      */
6546     pub.on = pub.addListener;
6547     pub.un = pub.removeListener;
6548
6549     pub.stoppedMouseDownEvent = new Roo.util.Event();
6550     return pub;
6551 }();
6552 /**
6553   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6554   * @param {Function} fn        The method the event invokes
6555   * @param {Object}   scope    An  object that becomes the scope of the handler
6556   * @param {boolean}  override If true, the obj passed in becomes
6557   *                             the execution scope of the listener
6558   * @member Roo
6559   * @method onReady
6560  */
6561 Roo.onReady = Roo.EventManager.onDocumentReady;
6562
6563 Roo.onReady(function(){
6564     var bd = Roo.get(document.body);
6565     if(!bd){ return; }
6566
6567     var cls = [
6568             Roo.isIE ? "roo-ie"
6569             : Roo.isGecko ? "roo-gecko"
6570             : Roo.isOpera ? "roo-opera"
6571             : Roo.isSafari ? "roo-safari" : ""];
6572
6573     if(Roo.isMac){
6574         cls.push("roo-mac");
6575     }
6576     if(Roo.isLinux){
6577         cls.push("roo-linux");
6578     }
6579     if(Roo.isBorderBox){
6580         cls.push('roo-border-box');
6581     }
6582     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6583         var p = bd.dom.parentNode;
6584         if(p){
6585             p.className += ' roo-strict';
6586         }
6587     }
6588     bd.addClass(cls.join(' '));
6589 });
6590
6591 /**
6592  * @class Roo.EventObject
6593  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6594  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6595  * Example:
6596  * <pre><code>
6597  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6598     e.preventDefault();
6599     var target = e.getTarget();
6600     ...
6601  }
6602  var myDiv = Roo.get("myDiv");
6603  myDiv.on("click", handleClick);
6604  //or
6605  Roo.EventManager.on("myDiv", 'click', handleClick);
6606  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6607  </code></pre>
6608  * @singleton
6609  */
6610 Roo.EventObject = function(){
6611     
6612     var E = Roo.lib.Event;
6613     
6614     // safari keypress events for special keys return bad keycodes
6615     var safariKeys = {
6616         63234 : 37, // left
6617         63235 : 39, // right
6618         63232 : 38, // up
6619         63233 : 40, // down
6620         63276 : 33, // page up
6621         63277 : 34, // page down
6622         63272 : 46, // delete
6623         63273 : 36, // home
6624         63275 : 35  // end
6625     };
6626
6627     // normalize button clicks
6628     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6629                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6630
6631     Roo.EventObjectImpl = function(e){
6632         if(e){
6633             this.setEvent(e.browserEvent || e);
6634         }
6635     };
6636     Roo.EventObjectImpl.prototype = {
6637         /**
6638          * Used to fix doc tools.
6639          * @scope Roo.EventObject.prototype
6640          */
6641             
6642
6643         
6644         
6645         /** The normal browser event */
6646         browserEvent : null,
6647         /** The button pressed in a mouse event */
6648         button : -1,
6649         /** True if the shift key was down during the event */
6650         shiftKey : false,
6651         /** True if the control key was down during the event */
6652         ctrlKey : false,
6653         /** True if the alt key was down during the event */
6654         altKey : false,
6655
6656         /** Key constant 
6657         * @type Number */
6658         BACKSPACE : 8,
6659         /** Key constant 
6660         * @type Number */
6661         TAB : 9,
6662         /** Key constant 
6663         * @type Number */
6664         RETURN : 13,
6665         /** Key constant 
6666         * @type Number */
6667         ENTER : 13,
6668         /** Key constant 
6669         * @type Number */
6670         SHIFT : 16,
6671         /** Key constant 
6672         * @type Number */
6673         CONTROL : 17,
6674         /** Key constant 
6675         * @type Number */
6676         ESC : 27,
6677         /** Key constant 
6678         * @type Number */
6679         SPACE : 32,
6680         /** Key constant 
6681         * @type Number */
6682         PAGEUP : 33,
6683         /** Key constant 
6684         * @type Number */
6685         PAGEDOWN : 34,
6686         /** Key constant 
6687         * @type Number */
6688         END : 35,
6689         /** Key constant 
6690         * @type Number */
6691         HOME : 36,
6692         /** Key constant 
6693         * @type Number */
6694         LEFT : 37,
6695         /** Key constant 
6696         * @type Number */
6697         UP : 38,
6698         /** Key constant 
6699         * @type Number */
6700         RIGHT : 39,
6701         /** Key constant 
6702         * @type Number */
6703         DOWN : 40,
6704         /** Key constant 
6705         * @type Number */
6706         DELETE : 46,
6707         /** Key constant 
6708         * @type Number */
6709         F5 : 116,
6710
6711            /** @private */
6712         setEvent : function(e){
6713             if(e == this || (e && e.browserEvent)){ // already wrapped
6714                 return e;
6715             }
6716             this.browserEvent = e;
6717             if(e){
6718                 // normalize buttons
6719                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6720                 if(e.type == 'click' && this.button == -1){
6721                     this.button = 0;
6722                 }
6723                 this.type = e.type;
6724                 this.shiftKey = e.shiftKey;
6725                 // mac metaKey behaves like ctrlKey
6726                 this.ctrlKey = e.ctrlKey || e.metaKey;
6727                 this.altKey = e.altKey;
6728                 // in getKey these will be normalized for the mac
6729                 this.keyCode = e.keyCode;
6730                 // keyup warnings on firefox.
6731                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6732                 // cache the target for the delayed and or buffered events
6733                 this.target = E.getTarget(e);
6734                 // same for XY
6735                 this.xy = E.getXY(e);
6736             }else{
6737                 this.button = -1;
6738                 this.shiftKey = false;
6739                 this.ctrlKey = false;
6740                 this.altKey = false;
6741                 this.keyCode = 0;
6742                 this.charCode =0;
6743                 this.target = null;
6744                 this.xy = [0, 0];
6745             }
6746             return this;
6747         },
6748
6749         /**
6750          * Stop the event (preventDefault and stopPropagation)
6751          */
6752         stopEvent : function(){
6753             if(this.browserEvent){
6754                 if(this.browserEvent.type == 'mousedown'){
6755                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6756                 }
6757                 E.stopEvent(this.browserEvent);
6758             }
6759         },
6760
6761         /**
6762          * Prevents the browsers default handling of the event.
6763          */
6764         preventDefault : function(){
6765             if(this.browserEvent){
6766                 E.preventDefault(this.browserEvent);
6767             }
6768         },
6769
6770         /** @private */
6771         isNavKeyPress : function(){
6772             var k = this.keyCode;
6773             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6774             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6775         },
6776
6777         isSpecialKey : function(){
6778             var k = this.keyCode;
6779             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6780             (k == 16) || (k == 17) ||
6781             (k >= 18 && k <= 20) ||
6782             (k >= 33 && k <= 35) ||
6783             (k >= 36 && k <= 39) ||
6784             (k >= 44 && k <= 45);
6785         },
6786         /**
6787          * Cancels bubbling of the event.
6788          */
6789         stopPropagation : function(){
6790             if(this.browserEvent){
6791                 if(this.type == 'mousedown'){
6792                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6793                 }
6794                 E.stopPropagation(this.browserEvent);
6795             }
6796         },
6797
6798         /**
6799          * Gets the key code for the event.
6800          * @return {Number}
6801          */
6802         getCharCode : function(){
6803             return this.charCode || this.keyCode;
6804         },
6805
6806         /**
6807          * Returns a normalized keyCode for the event.
6808          * @return {Number} The key code
6809          */
6810         getKey : function(){
6811             var k = this.keyCode || this.charCode;
6812             return Roo.isSafari ? (safariKeys[k] || k) : k;
6813         },
6814
6815         /**
6816          * Gets the x coordinate of the event.
6817          * @return {Number}
6818          */
6819         getPageX : function(){
6820             return this.xy[0];
6821         },
6822
6823         /**
6824          * Gets the y coordinate of the event.
6825          * @return {Number}
6826          */
6827         getPageY : function(){
6828             return this.xy[1];
6829         },
6830
6831         /**
6832          * Gets the time of the event.
6833          * @return {Number}
6834          */
6835         getTime : function(){
6836             if(this.browserEvent){
6837                 return E.getTime(this.browserEvent);
6838             }
6839             return null;
6840         },
6841
6842         /**
6843          * Gets the page coordinates of the event.
6844          * @return {Array} The xy values like [x, y]
6845          */
6846         getXY : function(){
6847             return this.xy;
6848         },
6849
6850         /**
6851          * Gets the target for the event.
6852          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6853          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6854                 search as a number or element (defaults to 10 || document.body)
6855          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6856          * @return {HTMLelement}
6857          */
6858         getTarget : function(selector, maxDepth, returnEl){
6859             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6860         },
6861         /**
6862          * Gets the related target.
6863          * @return {HTMLElement}
6864          */
6865         getRelatedTarget : function(){
6866             if(this.browserEvent){
6867                 return E.getRelatedTarget(this.browserEvent);
6868             }
6869             return null;
6870         },
6871
6872         /**
6873          * Normalizes mouse wheel delta across browsers
6874          * @return {Number} The delta
6875          */
6876         getWheelDelta : function(){
6877             var e = this.browserEvent;
6878             var delta = 0;
6879             if(e.wheelDelta){ /* IE/Opera. */
6880                 delta = e.wheelDelta/120;
6881             }else if(e.detail){ /* Mozilla case. */
6882                 delta = -e.detail/3;
6883             }
6884             return delta;
6885         },
6886
6887         /**
6888          * Returns true if the control, meta, shift or alt key was pressed during this event.
6889          * @return {Boolean}
6890          */
6891         hasModifier : function(){
6892             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6893         },
6894
6895         /**
6896          * Returns true if the target of this event equals el or is a child of el
6897          * @param {String/HTMLElement/Element} el
6898          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6899          * @return {Boolean}
6900          */
6901         within : function(el, related){
6902             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6903             return t && Roo.fly(el).contains(t);
6904         },
6905
6906         getPoint : function(){
6907             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6908         }
6909     };
6910
6911     return new Roo.EventObjectImpl();
6912 }();
6913             
6914     /*
6915  * Based on:
6916  * Ext JS Library 1.1.1
6917  * Copyright(c) 2006-2007, Ext JS, LLC.
6918  *
6919  * Originally Released Under LGPL - original licence link has changed is not relivant.
6920  *
6921  * Fork - LGPL
6922  * <script type="text/javascript">
6923  */
6924
6925  
6926 // was in Composite Element!??!?!
6927  
6928 (function(){
6929     var D = Roo.lib.Dom;
6930     var E = Roo.lib.Event;
6931     var A = Roo.lib.Anim;
6932
6933     // local style camelizing for speed
6934     var propCache = {};
6935     var camelRe = /(-[a-z])/gi;
6936     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6937     var view = document.defaultView;
6938
6939 /**
6940  * @class Roo.Element
6941  * Represents an Element in the DOM.<br><br>
6942  * Usage:<br>
6943 <pre><code>
6944 var el = Roo.get("my-div");
6945
6946 // or with getEl
6947 var el = getEl("my-div");
6948
6949 // or with a DOM element
6950 var el = Roo.get(myDivElement);
6951 </code></pre>
6952  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6953  * each call instead of constructing a new one.<br><br>
6954  * <b>Animations</b><br />
6955  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6956  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6957 <pre>
6958 Option    Default   Description
6959 --------- --------  ---------------------------------------------
6960 duration  .35       The duration of the animation in seconds
6961 easing    easeOut   The YUI easing method
6962 callback  none      A function to execute when the anim completes
6963 scope     this      The scope (this) of the callback function
6964 </pre>
6965 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6966 * manipulate the animation. Here's an example:
6967 <pre><code>
6968 var el = Roo.get("my-div");
6969
6970 // no animation
6971 el.setWidth(100);
6972
6973 // default animation
6974 el.setWidth(100, true);
6975
6976 // animation with some options set
6977 el.setWidth(100, {
6978     duration: 1,
6979     callback: this.foo,
6980     scope: this
6981 });
6982
6983 // using the "anim" property to get the Anim object
6984 var opt = {
6985     duration: 1,
6986     callback: this.foo,
6987     scope: this
6988 };
6989 el.setWidth(100, opt);
6990 ...
6991 if(opt.anim.isAnimated()){
6992     opt.anim.stop();
6993 }
6994 </code></pre>
6995 * <b> Composite (Collections of) Elements</b><br />
6996  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6997  * @constructor Create a new Element directly.
6998  * @param {String/HTMLElement} element
6999  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7000  */
7001     Roo.Element = function(element, forceNew){
7002         var dom = typeof element == "string" ?
7003                 document.getElementById(element) : element;
7004         if(!dom){ // invalid id/element
7005             return null;
7006         }
7007         var id = dom.id;
7008         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7009             return Roo.Element.cache[id];
7010         }
7011
7012         /**
7013          * The DOM element
7014          * @type HTMLElement
7015          */
7016         this.dom = dom;
7017
7018         /**
7019          * The DOM element ID
7020          * @type String
7021          */
7022         this.id = id || Roo.id(dom);
7023     };
7024
7025     var El = Roo.Element;
7026
7027     El.prototype = {
7028         /**
7029          * The element's default display mode  (defaults to "")
7030          * @type String
7031          */
7032         originalDisplay : "",
7033
7034         visibilityMode : 1,
7035         /**
7036          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7037          * @type String
7038          */
7039         defaultUnit : "px",
7040         /**
7041          * Sets the element's visibility mode. When setVisible() is called it
7042          * will use this to determine whether to set the visibility or the display property.
7043          * @param visMode Element.VISIBILITY or Element.DISPLAY
7044          * @return {Roo.Element} this
7045          */
7046         setVisibilityMode : function(visMode){
7047             this.visibilityMode = visMode;
7048             return this;
7049         },
7050         /**
7051          * Convenience method for setVisibilityMode(Element.DISPLAY)
7052          * @param {String} display (optional) What to set display to when visible
7053          * @return {Roo.Element} this
7054          */
7055         enableDisplayMode : function(display){
7056             this.setVisibilityMode(El.DISPLAY);
7057             if(typeof display != "undefined") this.originalDisplay = display;
7058             return this;
7059         },
7060
7061         /**
7062          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7063          * @param {String} selector The simple selector to test
7064          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7065                 search as a number or element (defaults to 10 || document.body)
7066          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7067          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7068          */
7069         findParent : function(simpleSelector, maxDepth, returnEl){
7070             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7071             maxDepth = maxDepth || 50;
7072             if(typeof maxDepth != "number"){
7073                 stopEl = Roo.getDom(maxDepth);
7074                 maxDepth = 10;
7075             }
7076             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7077                 if(dq.is(p, simpleSelector)){
7078                     return returnEl ? Roo.get(p) : p;
7079                 }
7080                 depth++;
7081                 p = p.parentNode;
7082             }
7083             return null;
7084         },
7085
7086
7087         /**
7088          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7089          * @param {String} selector The simple selector to test
7090          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7091                 search as a number or element (defaults to 10 || document.body)
7092          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7093          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7094          */
7095         findParentNode : function(simpleSelector, maxDepth, returnEl){
7096             var p = Roo.fly(this.dom.parentNode, '_internal');
7097             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7098         },
7099
7100         /**
7101          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7102          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7103          * @param {String} selector The simple selector to test
7104          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7105                 search as a number or element (defaults to 10 || document.body)
7106          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7107          */
7108         up : function(simpleSelector, maxDepth){
7109             return this.findParentNode(simpleSelector, maxDepth, true);
7110         },
7111
7112
7113
7114         /**
7115          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7116          * @param {String} selector The simple selector to test
7117          * @return {Boolean} True if this element matches the selector, else false
7118          */
7119         is : function(simpleSelector){
7120             return Roo.DomQuery.is(this.dom, simpleSelector);
7121         },
7122
7123         /**
7124          * Perform animation on this element.
7125          * @param {Object} args The YUI animation control args
7126          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7127          * @param {Function} onComplete (optional) Function to call when animation completes
7128          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7129          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7130          * @return {Roo.Element} this
7131          */
7132         animate : function(args, duration, onComplete, easing, animType){
7133             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7134             return this;
7135         },
7136
7137         /*
7138          * @private Internal animation call
7139          */
7140         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7141             animType = animType || 'run';
7142             opt = opt || {};
7143             var anim = Roo.lib.Anim[animType](
7144                 this.dom, args,
7145                 (opt.duration || defaultDur) || .35,
7146                 (opt.easing || defaultEase) || 'easeOut',
7147                 function(){
7148                     Roo.callback(cb, this);
7149                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7150                 },
7151                 this
7152             );
7153             opt.anim = anim;
7154             return anim;
7155         },
7156
7157         // private legacy anim prep
7158         preanim : function(a, i){
7159             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7160         },
7161
7162         /**
7163          * Removes worthless text nodes
7164          * @param {Boolean} forceReclean (optional) By default the element
7165          * keeps track if it has been cleaned already so
7166          * you can call this over and over. However, if you update the element and
7167          * need to force a reclean, you can pass true.
7168          */
7169         clean : function(forceReclean){
7170             if(this.isCleaned && forceReclean !== true){
7171                 return this;
7172             }
7173             var ns = /\S/;
7174             var d = this.dom, n = d.firstChild, ni = -1;
7175             while(n){
7176                 var nx = n.nextSibling;
7177                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7178                     d.removeChild(n);
7179                 }else{
7180                     n.nodeIndex = ++ni;
7181                 }
7182                 n = nx;
7183             }
7184             this.isCleaned = true;
7185             return this;
7186         },
7187
7188         // private
7189         calcOffsetsTo : function(el){
7190             el = Roo.get(el);
7191             var d = el.dom;
7192             var restorePos = false;
7193             if(el.getStyle('position') == 'static'){
7194                 el.position('relative');
7195                 restorePos = true;
7196             }
7197             var x = 0, y =0;
7198             var op = this.dom;
7199             while(op && op != d && op.tagName != 'HTML'){
7200                 x+= op.offsetLeft;
7201                 y+= op.offsetTop;
7202                 op = op.offsetParent;
7203             }
7204             if(restorePos){
7205                 el.position('static');
7206             }
7207             return [x, y];
7208         },
7209
7210         /**
7211          * Scrolls this element into view within the passed container.
7212          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7213          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7214          * @return {Roo.Element} this
7215          */
7216         scrollIntoView : function(container, hscroll){
7217             var c = Roo.getDom(container) || document.body;
7218             var el = this.dom;
7219
7220             var o = this.calcOffsetsTo(c),
7221                 l = o[0],
7222                 t = o[1],
7223                 b = t+el.offsetHeight,
7224                 r = l+el.offsetWidth;
7225
7226             var ch = c.clientHeight;
7227             var ct = parseInt(c.scrollTop, 10);
7228             var cl = parseInt(c.scrollLeft, 10);
7229             var cb = ct + ch;
7230             var cr = cl + c.clientWidth;
7231
7232             if(t < ct){
7233                 c.scrollTop = t;
7234             }else if(b > cb){
7235                 c.scrollTop = b-ch;
7236             }
7237
7238             if(hscroll !== false){
7239                 if(l < cl){
7240                     c.scrollLeft = l;
7241                 }else if(r > cr){
7242                     c.scrollLeft = r-c.clientWidth;
7243                 }
7244             }
7245             return this;
7246         },
7247
7248         // private
7249         scrollChildIntoView : function(child, hscroll){
7250             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7251         },
7252
7253         /**
7254          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7255          * the new height may not be available immediately.
7256          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7257          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7258          * @param {Function} onComplete (optional) Function to call when animation completes
7259          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7260          * @return {Roo.Element} this
7261          */
7262         autoHeight : function(animate, duration, onComplete, easing){
7263             var oldHeight = this.getHeight();
7264             this.clip();
7265             this.setHeight(1); // force clipping
7266             setTimeout(function(){
7267                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7268                 if(!animate){
7269                     this.setHeight(height);
7270                     this.unclip();
7271                     if(typeof onComplete == "function"){
7272                         onComplete();
7273                     }
7274                 }else{
7275                     this.setHeight(oldHeight); // restore original height
7276                     this.setHeight(height, animate, duration, function(){
7277                         this.unclip();
7278                         if(typeof onComplete == "function") onComplete();
7279                     }.createDelegate(this), easing);
7280                 }
7281             }.createDelegate(this), 0);
7282             return this;
7283         },
7284
7285         /**
7286          * Returns true if this element is an ancestor of the passed element
7287          * @param {HTMLElement/String} el The element to check
7288          * @return {Boolean} True if this element is an ancestor of el, else false
7289          */
7290         contains : function(el){
7291             if(!el){return false;}
7292             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7293         },
7294
7295         /**
7296          * Checks whether the element is currently visible using both visibility and display properties.
7297          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7298          * @return {Boolean} True if the element is currently visible, else false
7299          */
7300         isVisible : function(deep) {
7301             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7302             if(deep !== true || !vis){
7303                 return vis;
7304             }
7305             var p = this.dom.parentNode;
7306             while(p && p.tagName.toLowerCase() != "body"){
7307                 if(!Roo.fly(p, '_isVisible').isVisible()){
7308                     return false;
7309                 }
7310                 p = p.parentNode;
7311             }
7312             return true;
7313         },
7314
7315         /**
7316          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7317          * @param {String} selector The CSS selector
7318          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7319          * @return {CompositeElement/CompositeElementLite} The composite element
7320          */
7321         select : function(selector, unique){
7322             return El.select(selector, unique, this.dom);
7323         },
7324
7325         /**
7326          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7327          * @param {String} selector The CSS selector
7328          * @return {Array} An array of the matched nodes
7329          */
7330         query : function(selector, unique){
7331             return Roo.DomQuery.select(selector, this.dom);
7332         },
7333
7334         /**
7335          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7336          * @param {String} selector The CSS selector
7337          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7338          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7339          */
7340         child : function(selector, returnDom){
7341             var n = Roo.DomQuery.selectNode(selector, this.dom);
7342             return returnDom ? n : Roo.get(n);
7343         },
7344
7345         /**
7346          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7347          * @param {String} selector The CSS selector
7348          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7349          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7350          */
7351         down : function(selector, returnDom){
7352             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7353             return returnDom ? n : Roo.get(n);
7354         },
7355
7356         /**
7357          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7358          * @param {String} group The group the DD object is member of
7359          * @param {Object} config The DD config object
7360          * @param {Object} overrides An object containing methods to override/implement on the DD object
7361          * @return {Roo.dd.DD} The DD object
7362          */
7363         initDD : function(group, config, overrides){
7364             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7365             return Roo.apply(dd, overrides);
7366         },
7367
7368         /**
7369          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7370          * @param {String} group The group the DDProxy object is member of
7371          * @param {Object} config The DDProxy config object
7372          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7373          * @return {Roo.dd.DDProxy} The DDProxy object
7374          */
7375         initDDProxy : function(group, config, overrides){
7376             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7377             return Roo.apply(dd, overrides);
7378         },
7379
7380         /**
7381          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7382          * @param {String} group The group the DDTarget object is member of
7383          * @param {Object} config The DDTarget config object
7384          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7385          * @return {Roo.dd.DDTarget} The DDTarget object
7386          */
7387         initDDTarget : function(group, config, overrides){
7388             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7389             return Roo.apply(dd, overrides);
7390         },
7391
7392         /**
7393          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7394          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7395          * @param {Boolean} visible Whether the element is visible
7396          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7397          * @return {Roo.Element} this
7398          */
7399          setVisible : function(visible, animate){
7400             if(!animate || !A){
7401                 if(this.visibilityMode == El.DISPLAY){
7402                     this.setDisplayed(visible);
7403                 }else{
7404                     this.fixDisplay();
7405                     this.dom.style.visibility = visible ? "visible" : "hidden";
7406                 }
7407             }else{
7408                 // closure for composites
7409                 var dom = this.dom;
7410                 var visMode = this.visibilityMode;
7411                 if(visible){
7412                     this.setOpacity(.01);
7413                     this.setVisible(true);
7414                 }
7415                 this.anim({opacity: { to: (visible?1:0) }},
7416                       this.preanim(arguments, 1),
7417                       null, .35, 'easeIn', function(){
7418                          if(!visible){
7419                              if(visMode == El.DISPLAY){
7420                                  dom.style.display = "none";
7421                              }else{
7422                                  dom.style.visibility = "hidden";
7423                              }
7424                              Roo.get(dom).setOpacity(1);
7425                          }
7426                      });
7427             }
7428             return this;
7429         },
7430
7431         /**
7432          * Returns true if display is not "none"
7433          * @return {Boolean}
7434          */
7435         isDisplayed : function() {
7436             return this.getStyle("display") != "none";
7437         },
7438
7439         /**
7440          * Toggles the element's visibility or display, depending on visibility mode.
7441          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7442          * @return {Roo.Element} this
7443          */
7444         toggle : function(animate){
7445             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7446             return this;
7447         },
7448
7449         /**
7450          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7451          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7452          * @return {Roo.Element} this
7453          */
7454         setDisplayed : function(value) {
7455             if(typeof value == "boolean"){
7456                value = value ? this.originalDisplay : "none";
7457             }
7458             this.setStyle("display", value);
7459             return this;
7460         },
7461
7462         /**
7463          * Tries to focus the element. Any exceptions are caught and ignored.
7464          * @return {Roo.Element} this
7465          */
7466         focus : function() {
7467             try{
7468                 this.dom.focus();
7469             }catch(e){}
7470             return this;
7471         },
7472
7473         /**
7474          * Tries to blur the element. Any exceptions are caught and ignored.
7475          * @return {Roo.Element} this
7476          */
7477         blur : function() {
7478             try{
7479                 this.dom.blur();
7480             }catch(e){}
7481             return this;
7482         },
7483
7484         /**
7485          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7486          * @param {String/Array} className The CSS class to add, or an array of classes
7487          * @return {Roo.Element} this
7488          */
7489         addClass : function(className){
7490             if(className instanceof Array){
7491                 for(var i = 0, len = className.length; i < len; i++) {
7492                     this.addClass(className[i]);
7493                 }
7494             }else{
7495                 if(className && !this.hasClass(className)){
7496                     this.dom.className = this.dom.className + " " + className;
7497                 }
7498             }
7499             return this;
7500         },
7501
7502         /**
7503          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7504          * @param {String/Array} className The CSS class to add, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         radioClass : function(className){
7508             var siblings = this.dom.parentNode.childNodes;
7509             for(var i = 0; i < siblings.length; i++) {
7510                 var s = siblings[i];
7511                 if(s.nodeType == 1){
7512                     Roo.get(s).removeClass(className);
7513                 }
7514             }
7515             this.addClass(className);
7516             return this;
7517         },
7518
7519         /**
7520          * Removes one or more CSS classes from the element.
7521          * @param {String/Array} className The CSS class to remove, or an array of classes
7522          * @return {Roo.Element} this
7523          */
7524         removeClass : function(className){
7525             if(!className || !this.dom.className){
7526                 return this;
7527             }
7528             if(className instanceof Array){
7529                 for(var i = 0, len = className.length; i < len; i++) {
7530                     this.removeClass(className[i]);
7531                 }
7532             }else{
7533                 if(this.hasClass(className)){
7534                     var re = this.classReCache[className];
7535                     if (!re) {
7536                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7537                        this.classReCache[className] = re;
7538                     }
7539                     this.dom.className =
7540                         this.dom.className.replace(re, " ");
7541                 }
7542             }
7543             return this;
7544         },
7545
7546         // private
7547         classReCache: {},
7548
7549         /**
7550          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7551          * @param {String} className The CSS class to toggle
7552          * @return {Roo.Element} this
7553          */
7554         toggleClass : function(className){
7555             if(this.hasClass(className)){
7556                 this.removeClass(className);
7557             }else{
7558                 this.addClass(className);
7559             }
7560             return this;
7561         },
7562
7563         /**
7564          * Checks if the specified CSS class exists on this element's DOM node.
7565          * @param {String} className The CSS class to check for
7566          * @return {Boolean} True if the class exists, else false
7567          */
7568         hasClass : function(className){
7569             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7570         },
7571
7572         /**
7573          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7574          * @param {String} oldClassName The CSS class to replace
7575          * @param {String} newClassName The replacement CSS class
7576          * @return {Roo.Element} this
7577          */
7578         replaceClass : function(oldClassName, newClassName){
7579             this.removeClass(oldClassName);
7580             this.addClass(newClassName);
7581             return this;
7582         },
7583
7584         /**
7585          * Returns an object with properties matching the styles requested.
7586          * For example, el.getStyles('color', 'font-size', 'width') might return
7587          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7588          * @param {String} style1 A style name
7589          * @param {String} style2 A style name
7590          * @param {String} etc.
7591          * @return {Object} The style object
7592          */
7593         getStyles : function(){
7594             var a = arguments, len = a.length, r = {};
7595             for(var i = 0; i < len; i++){
7596                 r[a[i]] = this.getStyle(a[i]);
7597             }
7598             return r;
7599         },
7600
7601         /**
7602          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7603          * @param {String} property The style property whose value is returned.
7604          * @return {String} The current value of the style property for this element.
7605          */
7606         getStyle : function(){
7607             return view && view.getComputedStyle ?
7608                 function(prop){
7609                     var el = this.dom, v, cs, camel;
7610                     if(prop == 'float'){
7611                         prop = "cssFloat";
7612                     }
7613                     if(el.style && (v = el.style[prop])){
7614                         return v;
7615                     }
7616                     if(cs = view.getComputedStyle(el, "")){
7617                         if(!(camel = propCache[prop])){
7618                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7619                         }
7620                         return cs[camel];
7621                     }
7622                     return null;
7623                 } :
7624                 function(prop){
7625                     var el = this.dom, v, cs, camel;
7626                     if(prop == 'opacity'){
7627                         if(typeof el.style.filter == 'string'){
7628                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7629                             if(m){
7630                                 var fv = parseFloat(m[1]);
7631                                 if(!isNaN(fv)){
7632                                     return fv ? fv / 100 : 0;
7633                                 }
7634                             }
7635                         }
7636                         return 1;
7637                     }else if(prop == 'float'){
7638                         prop = "styleFloat";
7639                     }
7640                     if(!(camel = propCache[prop])){
7641                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7642                     }
7643                     if(v = el.style[camel]){
7644                         return v;
7645                     }
7646                     if(cs = el.currentStyle){
7647                         return cs[camel];
7648                     }
7649                     return null;
7650                 };
7651         }(),
7652
7653         /**
7654          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7655          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7656          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7657          * @return {Roo.Element} this
7658          */
7659         setStyle : function(prop, value){
7660             if(typeof prop == "string"){
7661                 
7662                 if (prop == 'float') {
7663                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7664                     return this;
7665                 }
7666                 
7667                 var camel;
7668                 if(!(camel = propCache[prop])){
7669                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7670                 }
7671                 
7672                 if(camel == 'opacity') {
7673                     this.setOpacity(value);
7674                 }else{
7675                     this.dom.style[camel] = value;
7676                 }
7677             }else{
7678                 for(var style in prop){
7679                     if(typeof prop[style] != "function"){
7680                        this.setStyle(style, prop[style]);
7681                     }
7682                 }
7683             }
7684             return this;
7685         },
7686
7687         /**
7688          * More flexible version of {@link #setStyle} for setting style properties.
7689          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7690          * a function which returns such a specification.
7691          * @return {Roo.Element} this
7692          */
7693         applyStyles : function(style){
7694             Roo.DomHelper.applyStyles(this.dom, style);
7695             return this;
7696         },
7697
7698         /**
7699           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7700           * @return {Number} The X position of the element
7701           */
7702         getX : function(){
7703             return D.getX(this.dom);
7704         },
7705
7706         /**
7707           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7708           * @return {Number} The Y position of the element
7709           */
7710         getY : function(){
7711             return D.getY(this.dom);
7712         },
7713
7714         /**
7715           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7716           * @return {Array} The XY position of the element
7717           */
7718         getXY : function(){
7719             return D.getXY(this.dom);
7720         },
7721
7722         /**
7723          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7724          * @param {Number} The X position of the element
7725          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7726          * @return {Roo.Element} this
7727          */
7728         setX : function(x, animate){
7729             if(!animate || !A){
7730                 D.setX(this.dom, x);
7731             }else{
7732                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7733             }
7734             return this;
7735         },
7736
7737         /**
7738          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Number} The Y position of the element
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setY : function(y, animate){
7744             if(!animate || !A){
7745                 D.setY(this.dom, y);
7746             }else{
7747                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7754          * @param {String} left The left CSS property value
7755          * @return {Roo.Element} this
7756          */
7757         setLeft : function(left){
7758             this.setStyle("left", this.addUnits(left));
7759             return this;
7760         },
7761
7762         /**
7763          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7764          * @param {String} top The top CSS property value
7765          * @return {Roo.Element} this
7766          */
7767         setTop : function(top){
7768             this.setStyle("top", this.addUnits(top));
7769             return this;
7770         },
7771
7772         /**
7773          * Sets the element's CSS right style.
7774          * @param {String} right The right CSS property value
7775          * @return {Roo.Element} this
7776          */
7777         setRight : function(right){
7778             this.setStyle("right", this.addUnits(right));
7779             return this;
7780         },
7781
7782         /**
7783          * Sets the element's CSS bottom style.
7784          * @param {String} bottom The bottom CSS property value
7785          * @return {Roo.Element} this
7786          */
7787         setBottom : function(bottom){
7788             this.setStyle("bottom", this.addUnits(bottom));
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7794          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7795          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setXY : function(pos, animate){
7800             if(!animate || !A){
7801                 D.setXY(this.dom, pos);
7802             }else{
7803                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7804             }
7805             return this;
7806         },
7807
7808         /**
7809          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7810          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7811          * @param {Number} x X value for new position (coordinates are page-based)
7812          * @param {Number} y Y value for new position (coordinates are page-based)
7813          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7814          * @return {Roo.Element} this
7815          */
7816         setLocation : function(x, y, animate){
7817             this.setXY([x, y], this.preanim(arguments, 2));
7818             return this;
7819         },
7820
7821         /**
7822          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7823          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7824          * @param {Number} x X value for new position (coordinates are page-based)
7825          * @param {Number} y Y value for new position (coordinates are page-based)
7826          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7827          * @return {Roo.Element} this
7828          */
7829         moveTo : function(x, y, animate){
7830             this.setXY([x, y], this.preanim(arguments, 2));
7831             return this;
7832         },
7833
7834         /**
7835          * Returns the region of the given element.
7836          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7837          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7838          */
7839         getRegion : function(){
7840             return D.getRegion(this.dom);
7841         },
7842
7843         /**
7844          * Returns the offset height of the element
7845          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7846          * @return {Number} The element's height
7847          */
7848         getHeight : function(contentHeight){
7849             var h = this.dom.offsetHeight || 0;
7850             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7851         },
7852
7853         /**
7854          * Returns the offset width of the element
7855          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7856          * @return {Number} The element's width
7857          */
7858         getWidth : function(contentWidth){
7859             var w = this.dom.offsetWidth || 0;
7860             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7861         },
7862
7863         /**
7864          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7865          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7866          * if a height has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedHeight : function(){
7870             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7871             if(!h){
7872                 h = parseInt(this.getStyle('height'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     h += this.getFrameWidth('tb');
7875                 }
7876             }
7877             return h;
7878         },
7879
7880         /**
7881          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7882          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7883          * if a width has not been set using CSS.
7884          * @return {Number}
7885          */
7886         getComputedWidth : function(){
7887             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7888             if(!w){
7889                 w = parseInt(this.getStyle('width'), 10) || 0;
7890                 if(!this.isBorderBox()){
7891                     w += this.getFrameWidth('lr');
7892                 }
7893             }
7894             return w;
7895         },
7896
7897         /**
7898          * Returns the size of the element.
7899          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7900          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7901          */
7902         getSize : function(contentSize){
7903             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7904         },
7905
7906         /**
7907          * Returns the width and height of the viewport.
7908          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7909          */
7910         getViewSize : function(){
7911             var d = this.dom, doc = document, aw = 0, ah = 0;
7912             if(d == doc || d == doc.body){
7913                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7914             }else{
7915                 return {
7916                     width : d.clientWidth,
7917                     height: d.clientHeight
7918                 };
7919             }
7920         },
7921
7922         /**
7923          * Returns the value of the "value" attribute
7924          * @param {Boolean} asNumber true to parse the value as a number
7925          * @return {String/Number}
7926          */
7927         getValue : function(asNumber){
7928             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7929         },
7930
7931         // private
7932         adjustWidth : function(width){
7933             if(typeof width == "number"){
7934                 if(this.autoBoxAdjust && !this.isBorderBox()){
7935                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7936                 }
7937                 if(width < 0){
7938                     width = 0;
7939                 }
7940             }
7941             return width;
7942         },
7943
7944         // private
7945         adjustHeight : function(height){
7946             if(typeof height == "number"){
7947                if(this.autoBoxAdjust && !this.isBorderBox()){
7948                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7949                }
7950                if(height < 0){
7951                    height = 0;
7952                }
7953             }
7954             return height;
7955         },
7956
7957         /**
7958          * Set the width of the element
7959          * @param {Number} width The new width
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setWidth : function(width, animate){
7964             width = this.adjustWidth(width);
7965             if(!animate || !A){
7966                 this.dom.style.width = this.addUnits(width);
7967             }else{
7968                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Set the height of the element
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setHeight : function(height, animate){
7980             height = this.adjustHeight(height);
7981             if(!animate || !A){
7982                 this.dom.style.height = this.addUnits(height);
7983             }else{
7984                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7985             }
7986             return this;
7987         },
7988
7989         /**
7990          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7991          * @param {Number} width The new width
7992          * @param {Number} height The new height
7993          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7994          * @return {Roo.Element} this
7995          */
7996          setSize : function(width, height, animate){
7997             if(typeof width == "object"){ // in case of object from getSize()
7998                 height = width.height; width = width.width;
7999             }
8000             width = this.adjustWidth(width); height = this.adjustHeight(height);
8001             if(!animate || !A){
8002                 this.dom.style.width = this.addUnits(width);
8003                 this.dom.style.height = this.addUnits(height);
8004             }else{
8005                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8006             }
8007             return this;
8008         },
8009
8010         /**
8011          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8012          * @param {Number} x X value for new position (coordinates are page-based)
8013          * @param {Number} y Y value for new position (coordinates are page-based)
8014          * @param {Number} width The new width
8015          * @param {Number} height The new height
8016          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8017          * @return {Roo.Element} this
8018          */
8019         setBounds : function(x, y, width, height, animate){
8020             if(!animate || !A){
8021                 this.setSize(width, height);
8022                 this.setLocation(x, y);
8023             }else{
8024                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8025                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8026                               this.preanim(arguments, 4), 'motion');
8027             }
8028             return this;
8029         },
8030
8031         /**
8032          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8033          * @param {Roo.lib.Region} region The region to fill
8034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8035          * @return {Roo.Element} this
8036          */
8037         setRegion : function(region, animate){
8038             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8039             return this;
8040         },
8041
8042         /**
8043          * Appends an event handler
8044          *
8045          * @param {String}   eventName     The type of event to append
8046          * @param {Function} fn        The method the event invokes
8047          * @param {Object} scope       (optional) The scope (this object) of the fn
8048          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8049          */
8050         addListener : function(eventName, fn, scope, options){
8051             if (this.dom) {
8052                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8053             }
8054         },
8055
8056         /**
8057          * Removes an event handler from this element
8058          * @param {String} eventName the type of event to remove
8059          * @param {Function} fn the method the event invokes
8060          * @return {Roo.Element} this
8061          */
8062         removeListener : function(eventName, fn){
8063             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8064             return this;
8065         },
8066
8067         /**
8068          * Removes all previous added listeners from this element
8069          * @return {Roo.Element} this
8070          */
8071         removeAllListeners : function(){
8072             E.purgeElement(this.dom);
8073             return this;
8074         },
8075
8076         relayEvent : function(eventName, observable){
8077             this.on(eventName, function(e){
8078                 observable.fireEvent(eventName, e);
8079             });
8080         },
8081
8082         /**
8083          * Set the opacity of the element
8084          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8085          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8086          * @return {Roo.Element} this
8087          */
8088          setOpacity : function(opacity, animate){
8089             if(!animate || !A){
8090                 var s = this.dom.style;
8091                 if(Roo.isIE){
8092                     s.zoom = 1;
8093                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8094                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8095                 }else{
8096                     s.opacity = opacity;
8097                 }
8098             }else{
8099                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8100             }
8101             return this;
8102         },
8103
8104         /**
8105          * Gets the left X coordinate
8106          * @param {Boolean} local True to get the local css position instead of page coordinate
8107          * @return {Number}
8108          */
8109         getLeft : function(local){
8110             if(!local){
8111                 return this.getX();
8112             }else{
8113                 return parseInt(this.getStyle("left"), 10) || 0;
8114             }
8115         },
8116
8117         /**
8118          * Gets the right X coordinate of the element (element X position + element width)
8119          * @param {Boolean} local True to get the local css position instead of page coordinate
8120          * @return {Number}
8121          */
8122         getRight : function(local){
8123             if(!local){
8124                 return this.getX() + this.getWidth();
8125             }else{
8126                 return (this.getLeft(true) + this.getWidth()) || 0;
8127             }
8128         },
8129
8130         /**
8131          * Gets the top Y coordinate
8132          * @param {Boolean} local True to get the local css position instead of page coordinate
8133          * @return {Number}
8134          */
8135         getTop : function(local) {
8136             if(!local){
8137                 return this.getY();
8138             }else{
8139                 return parseInt(this.getStyle("top"), 10) || 0;
8140             }
8141         },
8142
8143         /**
8144          * Gets the bottom Y coordinate of the element (element Y position + element height)
8145          * @param {Boolean} local True to get the local css position instead of page coordinate
8146          * @return {Number}
8147          */
8148         getBottom : function(local){
8149             if(!local){
8150                 return this.getY() + this.getHeight();
8151             }else{
8152                 return (this.getTop(true) + this.getHeight()) || 0;
8153             }
8154         },
8155
8156         /**
8157         * Initializes positioning on this element. If a desired position is not passed, it will make the
8158         * the element positioned relative IF it is not already positioned.
8159         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8160         * @param {Number} zIndex (optional) The zIndex to apply
8161         * @param {Number} x (optional) Set the page X position
8162         * @param {Number} y (optional) Set the page Y position
8163         */
8164         position : function(pos, zIndex, x, y){
8165             if(!pos){
8166                if(this.getStyle('position') == 'static'){
8167                    this.setStyle('position', 'relative');
8168                }
8169             }else{
8170                 this.setStyle("position", pos);
8171             }
8172             if(zIndex){
8173                 this.setStyle("z-index", zIndex);
8174             }
8175             if(x !== undefined && y !== undefined){
8176                 this.setXY([x, y]);
8177             }else if(x !== undefined){
8178                 this.setX(x);
8179             }else if(y !== undefined){
8180                 this.setY(y);
8181             }
8182         },
8183
8184         /**
8185         * Clear positioning back to the default when the document was loaded
8186         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8187         * @return {Roo.Element} this
8188          */
8189         clearPositioning : function(value){
8190             value = value ||'';
8191             this.setStyle({
8192                 "left": value,
8193                 "right": value,
8194                 "top": value,
8195                 "bottom": value,
8196                 "z-index": "",
8197                 "position" : "static"
8198             });
8199             return this;
8200         },
8201
8202         /**
8203         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8204         * snapshot before performing an update and then restoring the element.
8205         * @return {Object}
8206         */
8207         getPositioning : function(){
8208             var l = this.getStyle("left");
8209             var t = this.getStyle("top");
8210             return {
8211                 "position" : this.getStyle("position"),
8212                 "left" : l,
8213                 "right" : l ? "" : this.getStyle("right"),
8214                 "top" : t,
8215                 "bottom" : t ? "" : this.getStyle("bottom"),
8216                 "z-index" : this.getStyle("z-index")
8217             };
8218         },
8219
8220         /**
8221          * Gets the width of the border(s) for the specified side(s)
8222          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8223          * passing lr would get the border (l)eft width + the border (r)ight width.
8224          * @return {Number} The width of the sides passed added together
8225          */
8226         getBorderWidth : function(side){
8227             return this.addStyles(side, El.borders);
8228         },
8229
8230         /**
8231          * Gets the width of the padding(s) for the specified side(s)
8232          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8233          * passing lr would get the padding (l)eft + the padding (r)ight.
8234          * @return {Number} The padding of the sides passed added together
8235          */
8236         getPadding : function(side){
8237             return this.addStyles(side, El.paddings);
8238         },
8239
8240         /**
8241         * Set positioning with an object returned by getPositioning().
8242         * @param {Object} posCfg
8243         * @return {Roo.Element} this
8244          */
8245         setPositioning : function(pc){
8246             this.applyStyles(pc);
8247             if(pc.right == "auto"){
8248                 this.dom.style.right = "";
8249             }
8250             if(pc.bottom == "auto"){
8251                 this.dom.style.bottom = "";
8252             }
8253             return this;
8254         },
8255
8256         // private
8257         fixDisplay : function(){
8258             if(this.getStyle("display") == "none"){
8259                 this.setStyle("visibility", "hidden");
8260                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8261                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8262                     this.setStyle("display", "block");
8263                 }
8264             }
8265         },
8266
8267         /**
8268          * Quick set left and top adding default units
8269          * @param {String} left The left CSS property value
8270          * @param {String} top The top CSS property value
8271          * @return {Roo.Element} this
8272          */
8273          setLeftTop : function(left, top){
8274             this.dom.style.left = this.addUnits(left);
8275             this.dom.style.top = this.addUnits(top);
8276             return this;
8277         },
8278
8279         /**
8280          * Move this element relative to its current position.
8281          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8282          * @param {Number} distance How far to move the element in pixels
8283          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8284          * @return {Roo.Element} this
8285          */
8286          move : function(direction, distance, animate){
8287             var xy = this.getXY();
8288             direction = direction.toLowerCase();
8289             switch(direction){
8290                 case "l":
8291                 case "left":
8292                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8293                     break;
8294                case "r":
8295                case "right":
8296                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8297                     break;
8298                case "t":
8299                case "top":
8300                case "up":
8301                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8302                     break;
8303                case "b":
8304                case "bottom":
8305                case "down":
8306                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8307                     break;
8308             }
8309             return this;
8310         },
8311
8312         /**
8313          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8314          * @return {Roo.Element} this
8315          */
8316         clip : function(){
8317             if(!this.isClipped){
8318                this.isClipped = true;
8319                this.originalClip = {
8320                    "o": this.getStyle("overflow"),
8321                    "x": this.getStyle("overflow-x"),
8322                    "y": this.getStyle("overflow-y")
8323                };
8324                this.setStyle("overflow", "hidden");
8325                this.setStyle("overflow-x", "hidden");
8326                this.setStyle("overflow-y", "hidden");
8327             }
8328             return this;
8329         },
8330
8331         /**
8332          *  Return clipping (overflow) to original clipping before clip() was called
8333          * @return {Roo.Element} this
8334          */
8335         unclip : function(){
8336             if(this.isClipped){
8337                 this.isClipped = false;
8338                 var o = this.originalClip;
8339                 if(o.o){this.setStyle("overflow", o.o);}
8340                 if(o.x){this.setStyle("overflow-x", o.x);}
8341                 if(o.y){this.setStyle("overflow-y", o.y);}
8342             }
8343             return this;
8344         },
8345
8346
8347         /**
8348          * Gets the x,y coordinates specified by the anchor position on the element.
8349          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8350          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8351          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8352          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8353          * @return {Array} [x, y] An array containing the element's x and y coordinates
8354          */
8355         getAnchorXY : function(anchor, local, s){
8356             //Passing a different size is useful for pre-calculating anchors,
8357             //especially for anchored animations that change the el size.
8358
8359             var w, h, vp = false;
8360             if(!s){
8361                 var d = this.dom;
8362                 if(d == document.body || d == document){
8363                     vp = true;
8364                     w = D.getViewWidth(); h = D.getViewHeight();
8365                 }else{
8366                     w = this.getWidth(); h = this.getHeight();
8367                 }
8368             }else{
8369                 w = s.width;  h = s.height;
8370             }
8371             var x = 0, y = 0, r = Math.round;
8372             switch((anchor || "tl").toLowerCase()){
8373                 case "c":
8374                     x = r(w*.5);
8375                     y = r(h*.5);
8376                 break;
8377                 case "t":
8378                     x = r(w*.5);
8379                     y = 0;
8380                 break;
8381                 case "l":
8382                     x = 0;
8383                     y = r(h*.5);
8384                 break;
8385                 case "r":
8386                     x = w;
8387                     y = r(h*.5);
8388                 break;
8389                 case "b":
8390                     x = r(w*.5);
8391                     y = h;
8392                 break;
8393                 case "tl":
8394                     x = 0;
8395                     y = 0;
8396                 break;
8397                 case "bl":
8398                     x = 0;
8399                     y = h;
8400                 break;
8401                 case "br":
8402                     x = w;
8403                     y = h;
8404                 break;
8405                 case "tr":
8406                     x = w;
8407                     y = 0;
8408                 break;
8409             }
8410             if(local === true){
8411                 return [x, y];
8412             }
8413             if(vp){
8414                 var sc = this.getScroll();
8415                 return [x + sc.left, y + sc.top];
8416             }
8417             //Add the element's offset xy
8418             var o = this.getXY();
8419             return [x+o[0], y+o[1]];
8420         },
8421
8422         /**
8423          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8424          * supported position values.
8425          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8426          * @param {String} position The position to align to.
8427          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8428          * @return {Array} [x, y]
8429          */
8430         getAlignToXY : function(el, p, o){
8431             el = Roo.get(el);
8432             var d = this.dom;
8433             if(!el.dom){
8434                 throw "Element.alignTo with an element that doesn't exist";
8435             }
8436             var c = false; //constrain to viewport
8437             var p1 = "", p2 = "";
8438             o = o || [0,0];
8439
8440             if(!p){
8441                 p = "tl-bl";
8442             }else if(p == "?"){
8443                 p = "tl-bl?";
8444             }else if(p.indexOf("-") == -1){
8445                 p = "tl-" + p;
8446             }
8447             p = p.toLowerCase();
8448             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8449             if(!m){
8450                throw "Element.alignTo with an invalid alignment " + p;
8451             }
8452             p1 = m[1]; p2 = m[2]; c = !!m[3];
8453
8454             //Subtract the aligned el's internal xy from the target's offset xy
8455             //plus custom offset to get the aligned el's new offset xy
8456             var a1 = this.getAnchorXY(p1, true);
8457             var a2 = el.getAnchorXY(p2, false);
8458             var x = a2[0] - a1[0] + o[0];
8459             var y = a2[1] - a1[1] + o[1];
8460             if(c){
8461                 //constrain the aligned el to viewport if necessary
8462                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8463                 // 5px of margin for ie
8464                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8465
8466                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8467                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8468                 //otherwise swap the aligned el to the opposite border of the target.
8469                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8470                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8471                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8472                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8473
8474                var doc = document;
8475                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8476                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8477
8478                if((x+w) > dw + scrollX){
8479                     x = swapX ? r.left-w : dw+scrollX-w;
8480                 }
8481                if(x < scrollX){
8482                    x = swapX ? r.right : scrollX;
8483                }
8484                if((y+h) > dh + scrollY){
8485                     y = swapY ? r.top-h : dh+scrollY-h;
8486                 }
8487                if (y < scrollY){
8488                    y = swapY ? r.bottom : scrollY;
8489                }
8490             }
8491             return [x,y];
8492         },
8493
8494         // private
8495         getConstrainToXY : function(){
8496             var os = {top:0, left:0, bottom:0, right: 0};
8497
8498             return function(el, local, offsets, proposedXY){
8499                 el = Roo.get(el);
8500                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8501
8502                 var vw, vh, vx = 0, vy = 0;
8503                 if(el.dom == document.body || el.dom == document){
8504                     vw = Roo.lib.Dom.getViewWidth();
8505                     vh = Roo.lib.Dom.getViewHeight();
8506                 }else{
8507                     vw = el.dom.clientWidth;
8508                     vh = el.dom.clientHeight;
8509                     if(!local){
8510                         var vxy = el.getXY();
8511                         vx = vxy[0];
8512                         vy = vxy[1];
8513                     }
8514                 }
8515
8516                 var s = el.getScroll();
8517
8518                 vx += offsets.left + s.left;
8519                 vy += offsets.top + s.top;
8520
8521                 vw -= offsets.right;
8522                 vh -= offsets.bottom;
8523
8524                 var vr = vx+vw;
8525                 var vb = vy+vh;
8526
8527                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8528                 var x = xy[0], y = xy[1];
8529                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8530
8531                 // only move it if it needs it
8532                 var moved = false;
8533
8534                 // first validate right/bottom
8535                 if((x + w) > vr){
8536                     x = vr - w;
8537                     moved = true;
8538                 }
8539                 if((y + h) > vb){
8540                     y = vb - h;
8541                     moved = true;
8542                 }
8543                 // then make sure top/left isn't negative
8544                 if(x < vx){
8545                     x = vx;
8546                     moved = true;
8547                 }
8548                 if(y < vy){
8549                     y = vy;
8550                     moved = true;
8551                 }
8552                 return moved ? [x, y] : false;
8553             };
8554         }(),
8555
8556         // private
8557         adjustForConstraints : function(xy, parent, offsets){
8558             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8559         },
8560
8561         /**
8562          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8563          * document it aligns it to the viewport.
8564          * The position parameter is optional, and can be specified in any one of the following formats:
8565          * <ul>
8566          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8567          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8568          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8569          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8570          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8571          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8572          * </ul>
8573          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8574          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8575          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8576          * that specified in order to enforce the viewport constraints.
8577          * Following are all of the supported anchor positions:
8578     <pre>
8579     Value  Description
8580     -----  -----------------------------
8581     tl     The top left corner (default)
8582     t      The center of the top edge
8583     tr     The top right corner
8584     l      The center of the left edge
8585     c      In the center of the element
8586     r      The center of the right edge
8587     bl     The bottom left corner
8588     b      The center of the bottom edge
8589     br     The bottom right corner
8590     </pre>
8591     Example Usage:
8592     <pre><code>
8593     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8594     el.alignTo("other-el");
8595
8596     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8597     el.alignTo("other-el", "tr?");
8598
8599     // align the bottom right corner of el with the center left edge of other-el
8600     el.alignTo("other-el", "br-l?");
8601
8602     // align the center of el with the bottom left corner of other-el and
8603     // adjust the x position by -6 pixels (and the y position by 0)
8604     el.alignTo("other-el", "c-bl", [-6, 0]);
8605     </code></pre>
8606          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8607          * @param {String} position The position to align to.
8608          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8610          * @return {Roo.Element} this
8611          */
8612         alignTo : function(element, position, offsets, animate){
8613             var xy = this.getAlignToXY(element, position, offsets);
8614             this.setXY(xy, this.preanim(arguments, 3));
8615             return this;
8616         },
8617
8618         /**
8619          * Anchors an element to another element and realigns it when the window is resized.
8620          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8621          * @param {String} position The position to align to.
8622          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8623          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8624          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8625          * is a number, it is used as the buffer delay (defaults to 50ms).
8626          * @param {Function} callback The function to call after the animation finishes
8627          * @return {Roo.Element} this
8628          */
8629         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8630             var action = function(){
8631                 this.alignTo(el, alignment, offsets, animate);
8632                 Roo.callback(callback, this);
8633             };
8634             Roo.EventManager.onWindowResize(action, this);
8635             var tm = typeof monitorScroll;
8636             if(tm != 'undefined'){
8637                 Roo.EventManager.on(window, 'scroll', action, this,
8638                     {buffer: tm == 'number' ? monitorScroll : 50});
8639             }
8640             action.call(this); // align immediately
8641             return this;
8642         },
8643         /**
8644          * Clears any opacity settings from this element. Required in some cases for IE.
8645          * @return {Roo.Element} this
8646          */
8647         clearOpacity : function(){
8648             if (window.ActiveXObject) {
8649                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8650                     this.dom.style.filter = "";
8651                 }
8652             } else {
8653                 this.dom.style.opacity = "";
8654                 this.dom.style["-moz-opacity"] = "";
8655                 this.dom.style["-khtml-opacity"] = "";
8656             }
8657             return this;
8658         },
8659
8660         /**
8661          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8662          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8663          * @return {Roo.Element} this
8664          */
8665         hide : function(animate){
8666             this.setVisible(false, this.preanim(arguments, 0));
8667             return this;
8668         },
8669
8670         /**
8671         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8672         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8673          * @return {Roo.Element} this
8674          */
8675         show : function(animate){
8676             this.setVisible(true, this.preanim(arguments, 0));
8677             return this;
8678         },
8679
8680         /**
8681          * @private Test if size has a unit, otherwise appends the default
8682          */
8683         addUnits : function(size){
8684             return Roo.Element.addUnits(size, this.defaultUnit);
8685         },
8686
8687         /**
8688          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8689          * @return {Roo.Element} this
8690          */
8691         beginMeasure : function(){
8692             var el = this.dom;
8693             if(el.offsetWidth || el.offsetHeight){
8694                 return this; // offsets work already
8695             }
8696             var changed = [];
8697             var p = this.dom, b = document.body; // start with this element
8698             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8699                 var pe = Roo.get(p);
8700                 if(pe.getStyle('display') == 'none'){
8701                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8702                     p.style.visibility = "hidden";
8703                     p.style.display = "block";
8704                 }
8705                 p = p.parentNode;
8706             }
8707             this._measureChanged = changed;
8708             return this;
8709
8710         },
8711
8712         /**
8713          * Restores displays to before beginMeasure was called
8714          * @return {Roo.Element} this
8715          */
8716         endMeasure : function(){
8717             var changed = this._measureChanged;
8718             if(changed){
8719                 for(var i = 0, len = changed.length; i < len; i++) {
8720                     var r = changed[i];
8721                     r.el.style.visibility = r.visibility;
8722                     r.el.style.display = "none";
8723                 }
8724                 this._measureChanged = null;
8725             }
8726             return this;
8727         },
8728
8729         /**
8730         * Update the innerHTML of this element, optionally searching for and processing scripts
8731         * @param {String} html The new HTML
8732         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8733         * @param {Function} callback For async script loading you can be noticed when the update completes
8734         * @return {Roo.Element} this
8735          */
8736         update : function(html, loadScripts, callback){
8737             if(typeof html == "undefined"){
8738                 html = "";
8739             }
8740             if(loadScripts !== true){
8741                 this.dom.innerHTML = html;
8742                 if(typeof callback == "function"){
8743                     callback();
8744                 }
8745                 return this;
8746             }
8747             var id = Roo.id();
8748             var dom = this.dom;
8749
8750             html += '<span id="' + id + '"></span>';
8751
8752             E.onAvailable(id, function(){
8753                 var hd = document.getElementsByTagName("head")[0];
8754                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8755                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8756                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8757
8758                 var match;
8759                 while(match = re.exec(html)){
8760                     var attrs = match[1];
8761                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8762                     if(srcMatch && srcMatch[2]){
8763                        var s = document.createElement("script");
8764                        s.src = srcMatch[2];
8765                        var typeMatch = attrs.match(typeRe);
8766                        if(typeMatch && typeMatch[2]){
8767                            s.type = typeMatch[2];
8768                        }
8769                        hd.appendChild(s);
8770                     }else if(match[2] && match[2].length > 0){
8771                         if(window.execScript) {
8772                            window.execScript(match[2]);
8773                         } else {
8774                             /**
8775                              * eval:var:id
8776                              * eval:var:dom
8777                              * eval:var:html
8778                              * 
8779                              */
8780                            window.eval(match[2]);
8781                         }
8782                     }
8783                 }
8784                 var el = document.getElementById(id);
8785                 if(el){el.parentNode.removeChild(el);}
8786                 if(typeof callback == "function"){
8787                     callback();
8788                 }
8789             });
8790             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8791             return this;
8792         },
8793
8794         /**
8795          * Direct access to the UpdateManager update() method (takes the same parameters).
8796          * @param {String/Function} url The url for this request or a function to call to get the url
8797          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8798          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8799          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8800          * @return {Roo.Element} this
8801          */
8802         load : function(){
8803             var um = this.getUpdateManager();
8804             um.update.apply(um, arguments);
8805             return this;
8806         },
8807
8808         /**
8809         * Gets this element's UpdateManager
8810         * @return {Roo.UpdateManager} The UpdateManager
8811         */
8812         getUpdateManager : function(){
8813             if(!this.updateManager){
8814                 this.updateManager = new Roo.UpdateManager(this);
8815             }
8816             return this.updateManager;
8817         },
8818
8819         /**
8820          * Disables text selection for this element (normalized across browsers)
8821          * @return {Roo.Element} this
8822          */
8823         unselectable : function(){
8824             this.dom.unselectable = "on";
8825             this.swallowEvent("selectstart", true);
8826             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8827             this.addClass("x-unselectable");
8828             return this;
8829         },
8830
8831         /**
8832         * Calculates the x, y to center this element on the screen
8833         * @return {Array} The x, y values [x, y]
8834         */
8835         getCenterXY : function(){
8836             return this.getAlignToXY(document, 'c-c');
8837         },
8838
8839         /**
8840         * Centers the Element in either the viewport, or another Element.
8841         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8842         */
8843         center : function(centerIn){
8844             this.alignTo(centerIn || document, 'c-c');
8845             return this;
8846         },
8847
8848         /**
8849          * Tests various css rules/browsers to determine if this element uses a border box
8850          * @return {Boolean}
8851          */
8852         isBorderBox : function(){
8853             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8854         },
8855
8856         /**
8857          * Return a box {x, y, width, height} that can be used to set another elements
8858          * size/location to match this element.
8859          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8860          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8861          * @return {Object} box An object in the format {x, y, width, height}
8862          */
8863         getBox : function(contentBox, local){
8864             var xy;
8865             if(!local){
8866                 xy = this.getXY();
8867             }else{
8868                 var left = parseInt(this.getStyle("left"), 10) || 0;
8869                 var top = parseInt(this.getStyle("top"), 10) || 0;
8870                 xy = [left, top];
8871             }
8872             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8873             if(!contentBox){
8874                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8875             }else{
8876                 var l = this.getBorderWidth("l")+this.getPadding("l");
8877                 var r = this.getBorderWidth("r")+this.getPadding("r");
8878                 var t = this.getBorderWidth("t")+this.getPadding("t");
8879                 var b = this.getBorderWidth("b")+this.getPadding("b");
8880                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8881             }
8882             bx.right = bx.x + bx.width;
8883             bx.bottom = bx.y + bx.height;
8884             return bx;
8885         },
8886
8887         /**
8888          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8889          for more information about the sides.
8890          * @param {String} sides
8891          * @return {Number}
8892          */
8893         getFrameWidth : function(sides, onlyContentBox){
8894             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8895         },
8896
8897         /**
8898          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8899          * @param {Object} box The box to fill {x, y, width, height}
8900          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8901          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8902          * @return {Roo.Element} this
8903          */
8904         setBox : function(box, adjust, animate){
8905             var w = box.width, h = box.height;
8906             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8907                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8908                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8909             }
8910             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8911             return this;
8912         },
8913
8914         /**
8915          * Forces the browser to repaint this element
8916          * @return {Roo.Element} this
8917          */
8918          repaint : function(){
8919             var dom = this.dom;
8920             this.addClass("x-repaint");
8921             setTimeout(function(){
8922                 Roo.get(dom).removeClass("x-repaint");
8923             }, 1);
8924             return this;
8925         },
8926
8927         /**
8928          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8929          * then it returns the calculated width of the sides (see getPadding)
8930          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8931          * @return {Object/Number}
8932          */
8933         getMargins : function(side){
8934             if(!side){
8935                 return {
8936                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8937                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8938                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8939                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8940                 };
8941             }else{
8942                 return this.addStyles(side, El.margins);
8943              }
8944         },
8945
8946         // private
8947         addStyles : function(sides, styles){
8948             var val = 0, v, w;
8949             for(var i = 0, len = sides.length; i < len; i++){
8950                 v = this.getStyle(styles[sides.charAt(i)]);
8951                 if(v){
8952                      w = parseInt(v, 10);
8953                      if(w){ val += w; }
8954                 }
8955             }
8956             return val;
8957         },
8958
8959         /**
8960          * Creates a proxy element of this element
8961          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8962          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8963          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8964          * @return {Roo.Element} The new proxy element
8965          */
8966         createProxy : function(config, renderTo, matchBox){
8967             if(renderTo){
8968                 renderTo = Roo.getDom(renderTo);
8969             }else{
8970                 renderTo = document.body;
8971             }
8972             config = typeof config == "object" ?
8973                 config : {tag : "div", cls: config};
8974             var proxy = Roo.DomHelper.append(renderTo, config, true);
8975             if(matchBox){
8976                proxy.setBox(this.getBox());
8977             }
8978             return proxy;
8979         },
8980
8981         /**
8982          * Puts a mask over this element to disable user interaction. Requires core.css.
8983          * This method can only be applied to elements which accept child nodes.
8984          * @param {String} msg (optional) A message to display in the mask
8985          * @param {String} msgCls (optional) A css class to apply to the msg element
8986          * @return {Element} The mask  element
8987          */
8988         mask : function(msg, msgCls)
8989         {
8990             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8991                 this.setStyle("position", "relative");
8992             }
8993             if(!this._mask){
8994                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8995             }
8996             this.addClass("x-masked");
8997             this._mask.setDisplayed(true);
8998             
8999             // we wander
9000             var z = 0;
9001             var dom = this.dom
9002             while (dom && dom.style) {
9003                 if (!isNaN(parseInt(dom.style.zIndex))) {
9004                     z = Math.max(z, parseInt(dom.style.zIndex));
9005                 }
9006                 dom = dom.parentNode;
9007             }
9008             // if we are masking the body - then it hides everything..
9009             if (this.dom == document.body) {
9010                 z = 1000000;
9011                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9012                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9013             }
9014            
9015             if(typeof msg == 'string'){
9016                 if(!this._maskMsg){
9017                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9018                 }
9019                 var mm = this._maskMsg;
9020                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9021                 if (mm.dom.firstChild) { // weird IE issue?
9022                     mm.dom.firstChild.innerHTML = msg;
9023                 }
9024                 mm.setDisplayed(true);
9025                 mm.center(this);
9026                 mm.setStyle('z-index', z + 102);
9027             }
9028             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9029                 this._mask.setHeight(this.getHeight());
9030             }
9031             this._mask.setStyle('z-index', z + 100);
9032             
9033             return this._mask;
9034         },
9035
9036         /**
9037          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9038          * it is cached for reuse.
9039          */
9040         unmask : function(removeEl){
9041             if(this._mask){
9042                 if(removeEl === true){
9043                     this._mask.remove();
9044                     delete this._mask;
9045                     if(this._maskMsg){
9046                         this._maskMsg.remove();
9047                         delete this._maskMsg;
9048                     }
9049                 }else{
9050                     this._mask.setDisplayed(false);
9051                     if(this._maskMsg){
9052                         this._maskMsg.setDisplayed(false);
9053                     }
9054                 }
9055             }
9056             this.removeClass("x-masked");
9057         },
9058
9059         /**
9060          * Returns true if this element is masked
9061          * @return {Boolean}
9062          */
9063         isMasked : function(){
9064             return this._mask && this._mask.isVisible();
9065         },
9066
9067         /**
9068          * Creates an iframe shim for this element to keep selects and other windowed objects from
9069          * showing through.
9070          * @return {Roo.Element} The new shim element
9071          */
9072         createShim : function(){
9073             var el = document.createElement('iframe');
9074             el.frameBorder = 'no';
9075             el.className = 'roo-shim';
9076             if(Roo.isIE && Roo.isSecure){
9077                 el.src = Roo.SSL_SECURE_URL;
9078             }
9079             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9080             shim.autoBoxAdjust = false;
9081             return shim;
9082         },
9083
9084         /**
9085          * Removes this element from the DOM and deletes it from the cache
9086          */
9087         remove : function(){
9088             if(this.dom.parentNode){
9089                 this.dom.parentNode.removeChild(this.dom);
9090             }
9091             delete El.cache[this.dom.id];
9092         },
9093
9094         /**
9095          * Sets up event handlers to add and remove a css class when the mouse is over this element
9096          * @param {String} className
9097          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9098          * mouseout events for children elements
9099          * @return {Roo.Element} this
9100          */
9101         addClassOnOver : function(className, preventFlicker){
9102             this.on("mouseover", function(){
9103                 Roo.fly(this, '_internal').addClass(className);
9104             }, this.dom);
9105             var removeFn = function(e){
9106                 if(preventFlicker !== true || !e.within(this, true)){
9107                     Roo.fly(this, '_internal').removeClass(className);
9108                 }
9109             };
9110             this.on("mouseout", removeFn, this.dom);
9111             return this;
9112         },
9113
9114         /**
9115          * Sets up event handlers to add and remove a css class when this element has the focus
9116          * @param {String} className
9117          * @return {Roo.Element} this
9118          */
9119         addClassOnFocus : function(className){
9120             this.on("focus", function(){
9121                 Roo.fly(this, '_internal').addClass(className);
9122             }, this.dom);
9123             this.on("blur", function(){
9124                 Roo.fly(this, '_internal').removeClass(className);
9125             }, this.dom);
9126             return this;
9127         },
9128         /**
9129          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9130          * @param {String} className
9131          * @return {Roo.Element} this
9132          */
9133         addClassOnClick : function(className){
9134             var dom = this.dom;
9135             this.on("mousedown", function(){
9136                 Roo.fly(dom, '_internal').addClass(className);
9137                 var d = Roo.get(document);
9138                 var fn = function(){
9139                     Roo.fly(dom, '_internal').removeClass(className);
9140                     d.removeListener("mouseup", fn);
9141                 };
9142                 d.on("mouseup", fn);
9143             });
9144             return this;
9145         },
9146
9147         /**
9148          * Stops the specified event from bubbling and optionally prevents the default action
9149          * @param {String} eventName
9150          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9151          * @return {Roo.Element} this
9152          */
9153         swallowEvent : function(eventName, preventDefault){
9154             var fn = function(e){
9155                 e.stopPropagation();
9156                 if(preventDefault){
9157                     e.preventDefault();
9158                 }
9159             };
9160             if(eventName instanceof Array){
9161                 for(var i = 0, len = eventName.length; i < len; i++){
9162                      this.on(eventName[i], fn);
9163                 }
9164                 return this;
9165             }
9166             this.on(eventName, fn);
9167             return this;
9168         },
9169
9170         /**
9171          * @private
9172          */
9173       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9174
9175         /**
9176          * Sizes this element to its parent element's dimensions performing
9177          * neccessary box adjustments.
9178          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9179          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9180          * @return {Roo.Element} this
9181          */
9182         fitToParent : function(monitorResize, targetParent) {
9183           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9184           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9185           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9186             return;
9187           }
9188           var p = Roo.get(targetParent || this.dom.parentNode);
9189           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9190           if (monitorResize === true) {
9191             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9192             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9193           }
9194           return this;
9195         },
9196
9197         /**
9198          * Gets the next sibling, skipping text nodes
9199          * @return {HTMLElement} The next sibling or null
9200          */
9201         getNextSibling : function(){
9202             var n = this.dom.nextSibling;
9203             while(n && n.nodeType != 1){
9204                 n = n.nextSibling;
9205             }
9206             return n;
9207         },
9208
9209         /**
9210          * Gets the previous sibling, skipping text nodes
9211          * @return {HTMLElement} The previous sibling or null
9212          */
9213         getPrevSibling : function(){
9214             var n = this.dom.previousSibling;
9215             while(n && n.nodeType != 1){
9216                 n = n.previousSibling;
9217             }
9218             return n;
9219         },
9220
9221
9222         /**
9223          * Appends the passed element(s) to this element
9224          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9225          * @return {Roo.Element} this
9226          */
9227         appendChild: function(el){
9228             el = Roo.get(el);
9229             el.appendTo(this);
9230             return this;
9231         },
9232
9233         /**
9234          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9235          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9236          * automatically generated with the specified attributes.
9237          * @param {HTMLElement} insertBefore (optional) a child element of this element
9238          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9239          * @return {Roo.Element} The new child element
9240          */
9241         createChild: function(config, insertBefore, returnDom){
9242             config = config || {tag:'div'};
9243             if(insertBefore){
9244                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9245             }
9246             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9247         },
9248
9249         /**
9250          * Appends this element to the passed element
9251          * @param {String/HTMLElement/Element} el The new parent element
9252          * @return {Roo.Element} this
9253          */
9254         appendTo: function(el){
9255             el = Roo.getDom(el);
9256             el.appendChild(this.dom);
9257             return this;
9258         },
9259
9260         /**
9261          * Inserts this element before the passed element in the DOM
9262          * @param {String/HTMLElement/Element} el The element to insert before
9263          * @return {Roo.Element} this
9264          */
9265         insertBefore: function(el){
9266             el = Roo.getDom(el);
9267             el.parentNode.insertBefore(this.dom, el);
9268             return this;
9269         },
9270
9271         /**
9272          * Inserts this element after the passed element in the DOM
9273          * @param {String/HTMLElement/Element} el The element to insert after
9274          * @return {Roo.Element} this
9275          */
9276         insertAfter: function(el){
9277             el = Roo.getDom(el);
9278             el.parentNode.insertBefore(this.dom, el.nextSibling);
9279             return this;
9280         },
9281
9282         /**
9283          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9284          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9285          * @return {Roo.Element} The new child
9286          */
9287         insertFirst: function(el, returnDom){
9288             el = el || {};
9289             if(typeof el == 'object' && !el.nodeType){ // dh config
9290                 return this.createChild(el, this.dom.firstChild, returnDom);
9291             }else{
9292                 el = Roo.getDom(el);
9293                 this.dom.insertBefore(el, this.dom.firstChild);
9294                 return !returnDom ? Roo.get(el) : el;
9295             }
9296         },
9297
9298         /**
9299          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9300          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9301          * @param {String} where (optional) 'before' or 'after' defaults to before
9302          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9303          * @return {Roo.Element} the inserted Element
9304          */
9305         insertSibling: function(el, where, returnDom){
9306             where = where ? where.toLowerCase() : 'before';
9307             el = el || {};
9308             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9309
9310             if(typeof el == 'object' && !el.nodeType){ // dh config
9311                 if(where == 'after' && !this.dom.nextSibling){
9312                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9313                 }else{
9314                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9315                 }
9316
9317             }else{
9318                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9319                             where == 'before' ? this.dom : this.dom.nextSibling);
9320                 if(!returnDom){
9321                     rt = Roo.get(rt);
9322                 }
9323             }
9324             return rt;
9325         },
9326
9327         /**
9328          * Creates and wraps this element with another element
9329          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9330          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9331          * @return {HTMLElement/Element} The newly created wrapper element
9332          */
9333         wrap: function(config, returnDom){
9334             if(!config){
9335                 config = {tag: "div"};
9336             }
9337             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9338             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9339             return newEl;
9340         },
9341
9342         /**
9343          * Replaces the passed element with this element
9344          * @param {String/HTMLElement/Element} el The element to replace
9345          * @return {Roo.Element} this
9346          */
9347         replace: function(el){
9348             el = Roo.get(el);
9349             this.insertBefore(el);
9350             el.remove();
9351             return this;
9352         },
9353
9354         /**
9355          * Inserts an html fragment into this element
9356          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9357          * @param {String} html The HTML fragment
9358          * @param {Boolean} returnEl True to return an Roo.Element
9359          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9360          */
9361         insertHtml : function(where, html, returnEl){
9362             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9363             return returnEl ? Roo.get(el) : el;
9364         },
9365
9366         /**
9367          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9368          * @param {Object} o The object with the attributes
9369          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9370          * @return {Roo.Element} this
9371          */
9372         set : function(o, useSet){
9373             var el = this.dom;
9374             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9375             for(var attr in o){
9376                 if(attr == "style" || typeof o[attr] == "function") continue;
9377                 if(attr=="cls"){
9378                     el.className = o["cls"];
9379                 }else{
9380                     if(useSet) el.setAttribute(attr, o[attr]);
9381                     else el[attr] = o[attr];
9382                 }
9383             }
9384             if(o.style){
9385                 Roo.DomHelper.applyStyles(el, o.style);
9386             }
9387             return this;
9388         },
9389
9390         /**
9391          * Convenience method for constructing a KeyMap
9392          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9393          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9394          * @param {Function} fn The function to call
9395          * @param {Object} scope (optional) The scope of the function
9396          * @return {Roo.KeyMap} The KeyMap created
9397          */
9398         addKeyListener : function(key, fn, scope){
9399             var config;
9400             if(typeof key != "object" || key instanceof Array){
9401                 config = {
9402                     key: key,
9403                     fn: fn,
9404                     scope: scope
9405                 };
9406             }else{
9407                 config = {
9408                     key : key.key,
9409                     shift : key.shift,
9410                     ctrl : key.ctrl,
9411                     alt : key.alt,
9412                     fn: fn,
9413                     scope: scope
9414                 };
9415             }
9416             return new Roo.KeyMap(this, config);
9417         },
9418
9419         /**
9420          * Creates a KeyMap for this element
9421          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9422          * @return {Roo.KeyMap} The KeyMap created
9423          */
9424         addKeyMap : function(config){
9425             return new Roo.KeyMap(this, config);
9426         },
9427
9428         /**
9429          * Returns true if this element is scrollable.
9430          * @return {Boolean}
9431          */
9432          isScrollable : function(){
9433             var dom = this.dom;
9434             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9435         },
9436
9437         /**
9438          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9439          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9440          * @param {Number} value The new scroll value
9441          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9442          * @return {Element} this
9443          */
9444
9445         scrollTo : function(side, value, animate){
9446             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9447             if(!animate || !A){
9448                 this.dom[prop] = value;
9449             }else{
9450                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9451                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9452             }
9453             return this;
9454         },
9455
9456         /**
9457          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9458          * within this element's scrollable range.
9459          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9460          * @param {Number} distance How far to scroll the element in pixels
9461          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9462          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9463          * was scrolled as far as it could go.
9464          */
9465          scroll : function(direction, distance, animate){
9466              if(!this.isScrollable()){
9467                  return;
9468              }
9469              var el = this.dom;
9470              var l = el.scrollLeft, t = el.scrollTop;
9471              var w = el.scrollWidth, h = el.scrollHeight;
9472              var cw = el.clientWidth, ch = el.clientHeight;
9473              direction = direction.toLowerCase();
9474              var scrolled = false;
9475              var a = this.preanim(arguments, 2);
9476              switch(direction){
9477                  case "l":
9478                  case "left":
9479                      if(w - l > cw){
9480                          var v = Math.min(l + distance, w-cw);
9481                          this.scrollTo("left", v, a);
9482                          scrolled = true;
9483                      }
9484                      break;
9485                 case "r":
9486                 case "right":
9487                      if(l > 0){
9488                          var v = Math.max(l - distance, 0);
9489                          this.scrollTo("left", v, a);
9490                          scrolled = true;
9491                      }
9492                      break;
9493                 case "t":
9494                 case "top":
9495                 case "up":
9496                      if(t > 0){
9497                          var v = Math.max(t - distance, 0);
9498                          this.scrollTo("top", v, a);
9499                          scrolled = true;
9500                      }
9501                      break;
9502                 case "b":
9503                 case "bottom":
9504                 case "down":
9505                      if(h - t > ch){
9506                          var v = Math.min(t + distance, h-ch);
9507                          this.scrollTo("top", v, a);
9508                          scrolled = true;
9509                      }
9510                      break;
9511              }
9512              return scrolled;
9513         },
9514
9515         /**
9516          * Translates the passed page coordinates into left/top css values for this element
9517          * @param {Number/Array} x The page x or an array containing [x, y]
9518          * @param {Number} y The page y
9519          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9520          */
9521         translatePoints : function(x, y){
9522             if(typeof x == 'object' || x instanceof Array){
9523                 y = x[1]; x = x[0];
9524             }
9525             var p = this.getStyle('position');
9526             var o = this.getXY();
9527
9528             var l = parseInt(this.getStyle('left'), 10);
9529             var t = parseInt(this.getStyle('top'), 10);
9530
9531             if(isNaN(l)){
9532                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9533             }
9534             if(isNaN(t)){
9535                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9536             }
9537
9538             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9539         },
9540
9541         /**
9542          * Returns the current scroll position of the element.
9543          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9544          */
9545         getScroll : function(){
9546             var d = this.dom, doc = document;
9547             if(d == doc || d == doc.body){
9548                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9549                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9550                 return {left: l, top: t};
9551             }else{
9552                 return {left: d.scrollLeft, top: d.scrollTop};
9553             }
9554         },
9555
9556         /**
9557          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9558          * are convert to standard 6 digit hex color.
9559          * @param {String} attr The css attribute
9560          * @param {String} defaultValue The default value to use when a valid color isn't found
9561          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9562          * YUI color anims.
9563          */
9564         getColor : function(attr, defaultValue, prefix){
9565             var v = this.getStyle(attr);
9566             if(!v || v == "transparent" || v == "inherit") {
9567                 return defaultValue;
9568             }
9569             var color = typeof prefix == "undefined" ? "#" : prefix;
9570             if(v.substr(0, 4) == "rgb("){
9571                 var rvs = v.slice(4, v.length -1).split(",");
9572                 for(var i = 0; i < 3; i++){
9573                     var h = parseInt(rvs[i]).toString(16);
9574                     if(h < 16){
9575                         h = "0" + h;
9576                     }
9577                     color += h;
9578                 }
9579             } else {
9580                 if(v.substr(0, 1) == "#"){
9581                     if(v.length == 4) {
9582                         for(var i = 1; i < 4; i++){
9583                             var c = v.charAt(i);
9584                             color +=  c + c;
9585                         }
9586                     }else if(v.length == 7){
9587                         color += v.substr(1);
9588                     }
9589                 }
9590             }
9591             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9592         },
9593
9594         /**
9595          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9596          * gradient background, rounded corners and a 4-way shadow.
9597          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9598          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9599          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9600          * @return {Roo.Element} this
9601          */
9602         boxWrap : function(cls){
9603             cls = cls || 'x-box';
9604             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9605             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9606             return el;
9607         },
9608
9609         /**
9610          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9611          * @param {String} namespace The namespace in which to look for the attribute
9612          * @param {String} name The attribute name
9613          * @return {String} The attribute value
9614          */
9615         getAttributeNS : Roo.isIE ? function(ns, name){
9616             var d = this.dom;
9617             var type = typeof d[ns+":"+name];
9618             if(type != 'undefined' && type != 'unknown'){
9619                 return d[ns+":"+name];
9620             }
9621             return d[name];
9622         } : function(ns, name){
9623             var d = this.dom;
9624             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9625         },
9626         
9627         
9628         /**
9629          * Sets or Returns the value the dom attribute value
9630          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9631          * @param {String} value (optional) The value to set the attribute to
9632          * @return {String} The attribute value
9633          */
9634         attr : function(name){
9635             if (arguments.length > 1) {
9636                 this.dom.setAttribute(name, arguments[1]);
9637                 return arguments[1];
9638             }
9639             if (typeof(name) == 'object') {
9640                 for(var i in name) {
9641                     this.attr(i, name[i]);
9642                 }
9643                 return name;
9644             }
9645             
9646             
9647             if (!this.dom.hasAttribute(name)) {
9648                 return undefined;
9649             }
9650             return this.dom.getAttribute(name);
9651         }
9652         
9653         
9654         
9655     };
9656
9657     var ep = El.prototype;
9658
9659     /**
9660      * Appends an event handler (Shorthand for addListener)
9661      * @param {String}   eventName     The type of event to append
9662      * @param {Function} fn        The method the event invokes
9663      * @param {Object} scope       (optional) The scope (this object) of the fn
9664      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9665      * @method
9666      */
9667     ep.on = ep.addListener;
9668         // backwards compat
9669     ep.mon = ep.addListener;
9670
9671     /**
9672      * Removes an event handler from this element (shorthand for removeListener)
9673      * @param {String} eventName the type of event to remove
9674      * @param {Function} fn the method the event invokes
9675      * @return {Roo.Element} this
9676      * @method
9677      */
9678     ep.un = ep.removeListener;
9679
9680     /**
9681      * true to automatically adjust width and height settings for box-model issues (default to true)
9682      */
9683     ep.autoBoxAdjust = true;
9684
9685     // private
9686     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9687
9688     // private
9689     El.addUnits = function(v, defaultUnit){
9690         if(v === "" || v == "auto"){
9691             return v;
9692         }
9693         if(v === undefined){
9694             return '';
9695         }
9696         if(typeof v == "number" || !El.unitPattern.test(v)){
9697             return v + (defaultUnit || 'px');
9698         }
9699         return v;
9700     };
9701
9702     // special markup used throughout Roo when box wrapping elements
9703     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9704     /**
9705      * Visibility mode constant - Use visibility to hide element
9706      * @static
9707      * @type Number
9708      */
9709     El.VISIBILITY = 1;
9710     /**
9711      * Visibility mode constant - Use display to hide element
9712      * @static
9713      * @type Number
9714      */
9715     El.DISPLAY = 2;
9716
9717     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9718     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9719     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9720
9721
9722
9723     /**
9724      * @private
9725      */
9726     El.cache = {};
9727
9728     var docEl;
9729
9730     /**
9731      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9732      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9733      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9734      * @return {Element} The Element object
9735      * @static
9736      */
9737     El.get = function(el){
9738         var ex, elm, id;
9739         if(!el){ return null; }
9740         if(typeof el == "string"){ // element id
9741             if(!(elm = document.getElementById(el))){
9742                 return null;
9743             }
9744             if(ex = El.cache[el]){
9745                 ex.dom = elm;
9746             }else{
9747                 ex = El.cache[el] = new El(elm);
9748             }
9749             return ex;
9750         }else if(el.tagName){ // dom element
9751             if(!(id = el.id)){
9752                 id = Roo.id(el);
9753             }
9754             if(ex = El.cache[id]){
9755                 ex.dom = el;
9756             }else{
9757                 ex = El.cache[id] = new El(el);
9758             }
9759             return ex;
9760         }else if(el instanceof El){
9761             if(el != docEl){
9762                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9763                                                               // catch case where it hasn't been appended
9764                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9765             }
9766             return el;
9767         }else if(el.isComposite){
9768             return el;
9769         }else if(el instanceof Array){
9770             return El.select(el);
9771         }else if(el == document){
9772             // create a bogus element object representing the document object
9773             if(!docEl){
9774                 var f = function(){};
9775                 f.prototype = El.prototype;
9776                 docEl = new f();
9777                 docEl.dom = document;
9778             }
9779             return docEl;
9780         }
9781         return null;
9782     };
9783
9784     // private
9785     El.uncache = function(el){
9786         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9787             if(a[i]){
9788                 delete El.cache[a[i].id || a[i]];
9789             }
9790         }
9791     };
9792
9793     // private
9794     // Garbage collection - uncache elements/purge listeners on orphaned elements
9795     // so we don't hold a reference and cause the browser to retain them
9796     El.garbageCollect = function(){
9797         if(!Roo.enableGarbageCollector){
9798             clearInterval(El.collectorThread);
9799             return;
9800         }
9801         for(var eid in El.cache){
9802             var el = El.cache[eid], d = el.dom;
9803             // -------------------------------------------------------
9804             // Determining what is garbage:
9805             // -------------------------------------------------------
9806             // !d
9807             // dom node is null, definitely garbage
9808             // -------------------------------------------------------
9809             // !d.parentNode
9810             // no parentNode == direct orphan, definitely garbage
9811             // -------------------------------------------------------
9812             // !d.offsetParent && !document.getElementById(eid)
9813             // display none elements have no offsetParent so we will
9814             // also try to look it up by it's id. However, check
9815             // offsetParent first so we don't do unneeded lookups.
9816             // This enables collection of elements that are not orphans
9817             // directly, but somewhere up the line they have an orphan
9818             // parent.
9819             // -------------------------------------------------------
9820             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9821                 delete El.cache[eid];
9822                 if(d && Roo.enableListenerCollection){
9823                     E.purgeElement(d);
9824                 }
9825             }
9826         }
9827     }
9828     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9829
9830
9831     // dom is optional
9832     El.Flyweight = function(dom){
9833         this.dom = dom;
9834     };
9835     El.Flyweight.prototype = El.prototype;
9836
9837     El._flyweights = {};
9838     /**
9839      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9840      * the dom node can be overwritten by other code.
9841      * @param {String/HTMLElement} el The dom node or id
9842      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9843      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9844      * @static
9845      * @return {Element} The shared Element object
9846      */
9847     El.fly = function(el, named){
9848         named = named || '_global';
9849         el = Roo.getDom(el);
9850         if(!el){
9851             return null;
9852         }
9853         if(!El._flyweights[named]){
9854             El._flyweights[named] = new El.Flyweight();
9855         }
9856         El._flyweights[named].dom = el;
9857         return El._flyweights[named];
9858     };
9859
9860     /**
9861      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9862      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9863      * Shorthand of {@link Roo.Element#get}
9864      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9865      * @return {Element} The Element object
9866      * @member Roo
9867      * @method get
9868      */
9869     Roo.get = El.get;
9870     /**
9871      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9872      * the dom node can be overwritten by other code.
9873      * Shorthand of {@link Roo.Element#fly}
9874      * @param {String/HTMLElement} el The dom node or id
9875      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9876      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9877      * @static
9878      * @return {Element} The shared Element object
9879      * @member Roo
9880      * @method fly
9881      */
9882     Roo.fly = El.fly;
9883
9884     // speedy lookup for elements never to box adjust
9885     var noBoxAdjust = Roo.isStrict ? {
9886         select:1
9887     } : {
9888         input:1, select:1, textarea:1
9889     };
9890     if(Roo.isIE || Roo.isGecko){
9891         noBoxAdjust['button'] = 1;
9892     }
9893
9894
9895     Roo.EventManager.on(window, 'unload', function(){
9896         delete El.cache;
9897         delete El._flyweights;
9898     });
9899 })();
9900
9901
9902
9903
9904 if(Roo.DomQuery){
9905     Roo.Element.selectorFunction = Roo.DomQuery.select;
9906 }
9907
9908 Roo.Element.select = function(selector, unique, root){
9909     var els;
9910     if(typeof selector == "string"){
9911         els = Roo.Element.selectorFunction(selector, root);
9912     }else if(selector.length !== undefined){
9913         els = selector;
9914     }else{
9915         throw "Invalid selector";
9916     }
9917     if(unique === true){
9918         return new Roo.CompositeElement(els);
9919     }else{
9920         return new Roo.CompositeElementLite(els);
9921     }
9922 };
9923 /**
9924  * Selects elements based on the passed CSS selector to enable working on them as 1.
9925  * @param {String/Array} selector The CSS selector or an array of elements
9926  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9927  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9928  * @return {CompositeElementLite/CompositeElement}
9929  * @member Roo
9930  * @method select
9931  */
9932 Roo.select = Roo.Element.select;
9933
9934
9935
9936
9937
9938
9939
9940
9941
9942
9943
9944
9945
9946
9947 /*
9948  * Based on:
9949  * Ext JS Library 1.1.1
9950  * Copyright(c) 2006-2007, Ext JS, LLC.
9951  *
9952  * Originally Released Under LGPL - original licence link has changed is not relivant.
9953  *
9954  * Fork - LGPL
9955  * <script type="text/javascript">
9956  */
9957
9958
9959
9960 //Notifies Element that fx methods are available
9961 Roo.enableFx = true;
9962
9963 /**
9964  * @class Roo.Fx
9965  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9966  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9967  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9968  * Element effects to work.</p><br/>
9969  *
9970  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9971  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9972  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9973  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9974  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9975  * expected results and should be done with care.</p><br/>
9976  *
9977  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9978  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9979 <pre>
9980 Value  Description
9981 -----  -----------------------------
9982 tl     The top left corner
9983 t      The center of the top edge
9984 tr     The top right corner
9985 l      The center of the left edge
9986 r      The center of the right edge
9987 bl     The bottom left corner
9988 b      The center of the bottom edge
9989 br     The bottom right corner
9990 </pre>
9991  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9992  * below are common options that can be passed to any Fx method.</b>
9993  * @cfg {Function} callback A function called when the effect is finished
9994  * @cfg {Object} scope The scope of the effect function
9995  * @cfg {String} easing A valid Easing value for the effect
9996  * @cfg {String} afterCls A css class to apply after the effect
9997  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9998  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9999  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10000  * effects that end with the element being visually hidden, ignored otherwise)
10001  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10002  * a function which returns such a specification that will be applied to the Element after the effect finishes
10003  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10004  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10005  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10006  */
10007 Roo.Fx = {
10008         /**
10009          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10010          * origin for the slide effect.  This function automatically handles wrapping the element with
10011          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10012          * Usage:
10013          *<pre><code>
10014 // default: slide the element in from the top
10015 el.slideIn();
10016
10017 // custom: slide the element in from the right with a 2-second duration
10018 el.slideIn('r', { duration: 2 });
10019
10020 // common config options shown with default values
10021 el.slideIn('t', {
10022     easing: 'easeOut',
10023     duration: .5
10024 });
10025 </code></pre>
10026          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10027          * @param {Object} options (optional) Object literal with any of the Fx config options
10028          * @return {Roo.Element} The Element
10029          */
10030     slideIn : function(anchor, o){
10031         var el = this.getFxEl();
10032         o = o || {};
10033
10034         el.queueFx(o, function(){
10035
10036             anchor = anchor || "t";
10037
10038             // fix display to visibility
10039             this.fixDisplay();
10040
10041             // restore values after effect
10042             var r = this.getFxRestore();
10043             var b = this.getBox();
10044             // fixed size for slide
10045             this.setSize(b);
10046
10047             // wrap if needed
10048             var wrap = this.fxWrap(r.pos, o, "hidden");
10049
10050             var st = this.dom.style;
10051             st.visibility = "visible";
10052             st.position = "absolute";
10053
10054             // clear out temp styles after slide and unwrap
10055             var after = function(){
10056                 el.fxUnwrap(wrap, r.pos, o);
10057                 st.width = r.width;
10058                 st.height = r.height;
10059                 el.afterFx(o);
10060             };
10061             // time to calc the positions
10062             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10063
10064             switch(anchor.toLowerCase()){
10065                 case "t":
10066                     wrap.setSize(b.width, 0);
10067                     st.left = st.bottom = "0";
10068                     a = {height: bh};
10069                 break;
10070                 case "l":
10071                     wrap.setSize(0, b.height);
10072                     st.right = st.top = "0";
10073                     a = {width: bw};
10074                 break;
10075                 case "r":
10076                     wrap.setSize(0, b.height);
10077                     wrap.setX(b.right);
10078                     st.left = st.top = "0";
10079                     a = {width: bw, points: pt};
10080                 break;
10081                 case "b":
10082                     wrap.setSize(b.width, 0);
10083                     wrap.setY(b.bottom);
10084                     st.left = st.top = "0";
10085                     a = {height: bh, points: pt};
10086                 break;
10087                 case "tl":
10088                     wrap.setSize(0, 0);
10089                     st.right = st.bottom = "0";
10090                     a = {width: bw, height: bh};
10091                 break;
10092                 case "bl":
10093                     wrap.setSize(0, 0);
10094                     wrap.setY(b.y+b.height);
10095                     st.right = st.top = "0";
10096                     a = {width: bw, height: bh, points: pt};
10097                 break;
10098                 case "br":
10099                     wrap.setSize(0, 0);
10100                     wrap.setXY([b.right, b.bottom]);
10101                     st.left = st.top = "0";
10102                     a = {width: bw, height: bh, points: pt};
10103                 break;
10104                 case "tr":
10105                     wrap.setSize(0, 0);
10106                     wrap.setX(b.x+b.width);
10107                     st.left = st.bottom = "0";
10108                     a = {width: bw, height: bh, points: pt};
10109                 break;
10110             }
10111             this.dom.style.visibility = "visible";
10112             wrap.show();
10113
10114             arguments.callee.anim = wrap.fxanim(a,
10115                 o,
10116                 'motion',
10117                 .5,
10118                 'easeOut', after);
10119         });
10120         return this;
10121     },
10122     
10123         /**
10124          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10125          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10126          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10127          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10128          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10129          * Usage:
10130          *<pre><code>
10131 // default: slide the element out to the top
10132 el.slideOut();
10133
10134 // custom: slide the element out to the right with a 2-second duration
10135 el.slideOut('r', { duration: 2 });
10136
10137 // common config options shown with default values
10138 el.slideOut('t', {
10139     easing: 'easeOut',
10140     duration: .5,
10141     remove: false,
10142     useDisplay: false
10143 });
10144 </code></pre>
10145          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10146          * @param {Object} options (optional) Object literal with any of the Fx config options
10147          * @return {Roo.Element} The Element
10148          */
10149     slideOut : function(anchor, o){
10150         var el = this.getFxEl();
10151         o = o || {};
10152
10153         el.queueFx(o, function(){
10154
10155             anchor = anchor || "t";
10156
10157             // restore values after effect
10158             var r = this.getFxRestore();
10159             
10160             var b = this.getBox();
10161             // fixed size for slide
10162             this.setSize(b);
10163
10164             // wrap if needed
10165             var wrap = this.fxWrap(r.pos, o, "visible");
10166
10167             var st = this.dom.style;
10168             st.visibility = "visible";
10169             st.position = "absolute";
10170
10171             wrap.setSize(b);
10172
10173             var after = function(){
10174                 if(o.useDisplay){
10175                     el.setDisplayed(false);
10176                 }else{
10177                     el.hide();
10178                 }
10179
10180                 el.fxUnwrap(wrap, r.pos, o);
10181
10182                 st.width = r.width;
10183                 st.height = r.height;
10184
10185                 el.afterFx(o);
10186             };
10187
10188             var a, zero = {to: 0};
10189             switch(anchor.toLowerCase()){
10190                 case "t":
10191                     st.left = st.bottom = "0";
10192                     a = {height: zero};
10193                 break;
10194                 case "l":
10195                     st.right = st.top = "0";
10196                     a = {width: zero};
10197                 break;
10198                 case "r":
10199                     st.left = st.top = "0";
10200                     a = {width: zero, points: {to:[b.right, b.y]}};
10201                 break;
10202                 case "b":
10203                     st.left = st.top = "0";
10204                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10205                 break;
10206                 case "tl":
10207                     st.right = st.bottom = "0";
10208                     a = {width: zero, height: zero};
10209                 break;
10210                 case "bl":
10211                     st.right = st.top = "0";
10212                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10213                 break;
10214                 case "br":
10215                     st.left = st.top = "0";
10216                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10217                 break;
10218                 case "tr":
10219                     st.left = st.bottom = "0";
10220                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10221                 break;
10222             }
10223
10224             arguments.callee.anim = wrap.fxanim(a,
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10235          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10236          * The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.puff();
10241
10242 // common config options shown with default values
10243 el.puff({
10244     easing: 'easeOut',
10245     duration: .5,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     puff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.show();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273
10274                 el.setPositioning(r.pos);
10275                 st.width = r.width;
10276                 st.height = r.height;
10277                 st.fontSize = '';
10278                 el.afterFx(o);
10279             };
10280
10281             var width = this.getWidth();
10282             var height = this.getHeight();
10283
10284             arguments.callee.anim = this.fxanim({
10285                     width : {to: this.adjustWidth(width * 2)},
10286                     height : {to: this.adjustHeight(height * 2)},
10287                     points : {by: [-(width * .5), -(height * .5)]},
10288                     opacity : {to: 0},
10289                     fontSize: {to:200, unit: "%"}
10290                 },
10291                 o,
10292                 'motion',
10293                 .5,
10294                 "easeOut", after);
10295         });
10296         return this;
10297     },
10298
10299         /**
10300          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10301          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10302          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10303          * Usage:
10304          *<pre><code>
10305 // default
10306 el.switchOff();
10307
10308 // all config options shown with default values
10309 el.switchOff({
10310     easing: 'easeIn',
10311     duration: .3,
10312     remove: false,
10313     useDisplay: false
10314 });
10315 </code></pre>
10316          * @param {Object} options (optional) Object literal with any of the Fx config options
10317          * @return {Roo.Element} The Element
10318          */
10319     switchOff : function(o){
10320         var el = this.getFxEl();
10321         o = o || {};
10322
10323         el.queueFx(o, function(){
10324             this.clearOpacity();
10325             this.clip();
10326
10327             // restore values after effect
10328             var r = this.getFxRestore();
10329             var st = this.dom.style;
10330
10331             var after = function(){
10332                 if(o.useDisplay){
10333                     el.setDisplayed(false);
10334                 }else{
10335                     el.hide();
10336                 }
10337
10338                 el.clearOpacity();
10339                 el.setPositioning(r.pos);
10340                 st.width = r.width;
10341                 st.height = r.height;
10342
10343                 el.afterFx(o);
10344             };
10345
10346             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10347                 this.clearOpacity();
10348                 (function(){
10349                     this.fxanim({
10350                         height:{to:1},
10351                         points:{by:[0, this.getHeight() * .5]}
10352                     }, o, 'motion', 0.3, 'easeIn', after);
10353                 }).defer(100, this);
10354             });
10355         });
10356         return this;
10357     },
10358
10359     /**
10360      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10361      * changed using the "attr" config option) and then fading back to the original color. If no original
10362      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10363      * Usage:
10364 <pre><code>
10365 // default: highlight background to yellow
10366 el.highlight();
10367
10368 // custom: highlight foreground text to blue for 2 seconds
10369 el.highlight("0000ff", { attr: 'color', duration: 2 });
10370
10371 // common config options shown with default values
10372 el.highlight("ffff9c", {
10373     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10374     endColor: (current color) or "ffffff",
10375     easing: 'easeIn',
10376     duration: 1
10377 });
10378 </code></pre>
10379      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10380      * @param {Object} options (optional) Object literal with any of the Fx config options
10381      * @return {Roo.Element} The Element
10382      */ 
10383     highlight : function(color, o){
10384         var el = this.getFxEl();
10385         o = o || {};
10386
10387         el.queueFx(o, function(){
10388             color = color || "ffff9c";
10389             attr = o.attr || "backgroundColor";
10390
10391             this.clearOpacity();
10392             this.show();
10393
10394             var origColor = this.getColor(attr);
10395             var restoreColor = this.dom.style[attr];
10396             endColor = (o.endColor || origColor) || "ffffff";
10397
10398             var after = function(){
10399                 el.dom.style[attr] = restoreColor;
10400                 el.afterFx(o);
10401             };
10402
10403             var a = {};
10404             a[attr] = {from: color, to: endColor};
10405             arguments.callee.anim = this.fxanim(a,
10406                 o,
10407                 'color',
10408                 1,
10409                 'easeIn', after);
10410         });
10411         return this;
10412     },
10413
10414    /**
10415     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10416     * Usage:
10417 <pre><code>
10418 // default: a single light blue ripple
10419 el.frame();
10420
10421 // custom: 3 red ripples lasting 3 seconds total
10422 el.frame("ff0000", 3, { duration: 3 });
10423
10424 // common config options shown with default values
10425 el.frame("C3DAF9", 1, {
10426     duration: 1 //duration of entire animation (not each individual ripple)
10427     // Note: Easing is not configurable and will be ignored if included
10428 });
10429 </code></pre>
10430     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10431     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10432     * @param {Object} options (optional) Object literal with any of the Fx config options
10433     * @return {Roo.Element} The Element
10434     */
10435     frame : function(color, count, o){
10436         var el = this.getFxEl();
10437         o = o || {};
10438
10439         el.queueFx(o, function(){
10440             color = color || "#C3DAF9";
10441             if(color.length == 6){
10442                 color = "#" + color;
10443             }
10444             count = count || 1;
10445             duration = o.duration || 1;
10446             this.show();
10447
10448             var b = this.getBox();
10449             var animFn = function(){
10450                 var proxy = this.createProxy({
10451
10452                      style:{
10453                         visbility:"hidden",
10454                         position:"absolute",
10455                         "z-index":"35000", // yee haw
10456                         border:"0px solid " + color
10457                      }
10458                   });
10459                 var scale = Roo.isBorderBox ? 2 : 1;
10460                 proxy.animate({
10461                     top:{from:b.y, to:b.y - 20},
10462                     left:{from:b.x, to:b.x - 20},
10463                     borderWidth:{from:0, to:10},
10464                     opacity:{from:1, to:0},
10465                     height:{from:b.height, to:(b.height + (20*scale))},
10466                     width:{from:b.width, to:(b.width + (20*scale))}
10467                 }, duration, function(){
10468                     proxy.remove();
10469                 });
10470                 if(--count > 0){
10471                      animFn.defer((duration/2)*1000, this);
10472                 }else{
10473                     el.afterFx(o);
10474                 }
10475             };
10476             animFn.call(this);
10477         });
10478         return this;
10479     },
10480
10481    /**
10482     * Creates a pause before any subsequent queued effects begin.  If there are
10483     * no effects queued after the pause it will have no effect.
10484     * Usage:
10485 <pre><code>
10486 el.pause(1);
10487 </code></pre>
10488     * @param {Number} seconds The length of time to pause (in seconds)
10489     * @return {Roo.Element} The Element
10490     */
10491     pause : function(seconds){
10492         var el = this.getFxEl();
10493         var o = {};
10494
10495         el.queueFx(o, function(){
10496             setTimeout(function(){
10497                 el.afterFx(o);
10498             }, seconds * 1000);
10499         });
10500         return this;
10501     },
10502
10503    /**
10504     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10505     * using the "endOpacity" config option.
10506     * Usage:
10507 <pre><code>
10508 // default: fade in from opacity 0 to 100%
10509 el.fadeIn();
10510
10511 // custom: fade in from opacity 0 to 75% over 2 seconds
10512 el.fadeIn({ endOpacity: .75, duration: 2});
10513
10514 // common config options shown with default values
10515 el.fadeIn({
10516     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10517     easing: 'easeOut',
10518     duration: .5
10519 });
10520 </code></pre>
10521     * @param {Object} options (optional) Object literal with any of the Fx config options
10522     * @return {Roo.Element} The Element
10523     */
10524     fadeIn : function(o){
10525         var el = this.getFxEl();
10526         o = o || {};
10527         el.queueFx(o, function(){
10528             this.setOpacity(0);
10529             this.fixDisplay();
10530             this.dom.style.visibility = 'visible';
10531             var to = o.endOpacity || 1;
10532             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10533                 o, null, .5, "easeOut", function(){
10534                 if(to == 1){
10535                     this.clearOpacity();
10536                 }
10537                 el.afterFx(o);
10538             });
10539         });
10540         return this;
10541     },
10542
10543    /**
10544     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10545     * using the "endOpacity" config option.
10546     * Usage:
10547 <pre><code>
10548 // default: fade out from the element's current opacity to 0
10549 el.fadeOut();
10550
10551 // custom: fade out from the element's current opacity to 25% over 2 seconds
10552 el.fadeOut({ endOpacity: .25, duration: 2});
10553
10554 // common config options shown with default values
10555 el.fadeOut({
10556     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10557     easing: 'easeOut',
10558     duration: .5
10559     remove: false,
10560     useDisplay: false
10561 });
10562 </code></pre>
10563     * @param {Object} options (optional) Object literal with any of the Fx config options
10564     * @return {Roo.Element} The Element
10565     */
10566     fadeOut : function(o){
10567         var el = this.getFxEl();
10568         o = o || {};
10569         el.queueFx(o, function(){
10570             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10571                 o, null, .5, "easeOut", function(){
10572                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10573                      this.dom.style.display = "none";
10574                 }else{
10575                      this.dom.style.visibility = "hidden";
10576                 }
10577                 this.clearOpacity();
10578                 el.afterFx(o);
10579             });
10580         });
10581         return this;
10582     },
10583
10584    /**
10585     * Animates the transition of an element's dimensions from a starting height/width
10586     * to an ending height/width.
10587     * Usage:
10588 <pre><code>
10589 // change height and width to 100x100 pixels
10590 el.scale(100, 100);
10591
10592 // common config options shown with default values.  The height and width will default to
10593 // the element's existing values if passed as null.
10594 el.scale(
10595     [element's width],
10596     [element's height], {
10597     easing: 'easeOut',
10598     duration: .35
10599 });
10600 </code></pre>
10601     * @param {Number} width  The new width (pass undefined to keep the original width)
10602     * @param {Number} height  The new height (pass undefined to keep the original height)
10603     * @param {Object} options (optional) Object literal with any of the Fx config options
10604     * @return {Roo.Element} The Element
10605     */
10606     scale : function(w, h, o){
10607         this.shift(Roo.apply({}, o, {
10608             width: w,
10609             height: h
10610         }));
10611         return this;
10612     },
10613
10614    /**
10615     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10616     * Any of these properties not specified in the config object will not be changed.  This effect 
10617     * requires that at least one new dimension, position or opacity setting must be passed in on
10618     * the config object in order for the function to have any effect.
10619     * Usage:
10620 <pre><code>
10621 // slide the element horizontally to x position 200 while changing the height and opacity
10622 el.shift({ x: 200, height: 50, opacity: .8 });
10623
10624 // common config options shown with default values.
10625 el.shift({
10626     width: [element's width],
10627     height: [element's height],
10628     x: [element's x position],
10629     y: [element's y position],
10630     opacity: [element's opacity],
10631     easing: 'easeOut',
10632     duration: .35
10633 });
10634 </code></pre>
10635     * @param {Object} options  Object literal with any of the Fx config options
10636     * @return {Roo.Element} The Element
10637     */
10638     shift : function(o){
10639         var el = this.getFxEl();
10640         o = o || {};
10641         el.queueFx(o, function(){
10642             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10643             if(w !== undefined){
10644                 a.width = {to: this.adjustWidth(w)};
10645             }
10646             if(h !== undefined){
10647                 a.height = {to: this.adjustHeight(h)};
10648             }
10649             if(x !== undefined || y !== undefined){
10650                 a.points = {to: [
10651                     x !== undefined ? x : this.getX(),
10652                     y !== undefined ? y : this.getY()
10653                 ]};
10654             }
10655             if(op !== undefined){
10656                 a.opacity = {to: op};
10657             }
10658             if(o.xy !== undefined){
10659                 a.points = {to: o.xy};
10660             }
10661             arguments.callee.anim = this.fxanim(a,
10662                 o, 'motion', .35, "easeOut", function(){
10663                 el.afterFx(o);
10664             });
10665         });
10666         return this;
10667     },
10668
10669         /**
10670          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10671          * ending point of the effect.
10672          * Usage:
10673          *<pre><code>
10674 // default: slide the element downward while fading out
10675 el.ghost();
10676
10677 // custom: slide the element out to the right with a 2-second duration
10678 el.ghost('r', { duration: 2 });
10679
10680 // common config options shown with default values
10681 el.ghost('b', {
10682     easing: 'easeOut',
10683     duration: .5
10684     remove: false,
10685     useDisplay: false
10686 });
10687 </code></pre>
10688          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10689          * @param {Object} options (optional) Object literal with any of the Fx config options
10690          * @return {Roo.Element} The Element
10691          */
10692     ghost : function(anchor, o){
10693         var el = this.getFxEl();
10694         o = o || {};
10695
10696         el.queueFx(o, function(){
10697             anchor = anchor || "b";
10698
10699             // restore values after effect
10700             var r = this.getFxRestore();
10701             var w = this.getWidth(),
10702                 h = this.getHeight();
10703
10704             var st = this.dom.style;
10705
10706             var after = function(){
10707                 if(o.useDisplay){
10708                     el.setDisplayed(false);
10709                 }else{
10710                     el.hide();
10711                 }
10712
10713                 el.clearOpacity();
10714                 el.setPositioning(r.pos);
10715                 st.width = r.width;
10716                 st.height = r.height;
10717
10718                 el.afterFx(o);
10719             };
10720
10721             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10722             switch(anchor.toLowerCase()){
10723                 case "t":
10724                     pt.by = [0, -h];
10725                 break;
10726                 case "l":
10727                     pt.by = [-w, 0];
10728                 break;
10729                 case "r":
10730                     pt.by = [w, 0];
10731                 break;
10732                 case "b":
10733                     pt.by = [0, h];
10734                 break;
10735                 case "tl":
10736                     pt.by = [-w, -h];
10737                 break;
10738                 case "bl":
10739                     pt.by = [-w, h];
10740                 break;
10741                 case "br":
10742                     pt.by = [w, h];
10743                 break;
10744                 case "tr":
10745                     pt.by = [w, -h];
10746                 break;
10747             }
10748
10749             arguments.callee.anim = this.fxanim(a,
10750                 o,
10751                 'motion',
10752                 .5,
10753                 "easeOut", after);
10754         });
10755         return this;
10756     },
10757
10758         /**
10759          * Ensures that all effects queued after syncFx is called on the element are
10760          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10761          * @return {Roo.Element} The Element
10762          */
10763     syncFx : function(){
10764         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10765             block : false,
10766             concurrent : true,
10767             stopFx : false
10768         });
10769         return this;
10770     },
10771
10772         /**
10773          * Ensures that all effects queued after sequenceFx is called on the element are
10774          * run in sequence.  This is the opposite of {@link #syncFx}.
10775          * @return {Roo.Element} The Element
10776          */
10777     sequenceFx : function(){
10778         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10779             block : false,
10780             concurrent : false,
10781             stopFx : false
10782         });
10783         return this;
10784     },
10785
10786         /* @private */
10787     nextFx : function(){
10788         var ef = this.fxQueue[0];
10789         if(ef){
10790             ef.call(this);
10791         }
10792     },
10793
10794         /**
10795          * Returns true if the element has any effects actively running or queued, else returns false.
10796          * @return {Boolean} True if element has active effects, else false
10797          */
10798     hasActiveFx : function(){
10799         return this.fxQueue && this.fxQueue[0];
10800     },
10801
10802         /**
10803          * Stops any running effects and clears the element's internal effects queue if it contains
10804          * any additional effects that haven't started yet.
10805          * @return {Roo.Element} The Element
10806          */
10807     stopFx : function(){
10808         if(this.hasActiveFx()){
10809             var cur = this.fxQueue[0];
10810             if(cur && cur.anim && cur.anim.isAnimated()){
10811                 this.fxQueue = [cur]; // clear out others
10812                 cur.anim.stop(true);
10813             }
10814         }
10815         return this;
10816     },
10817
10818         /* @private */
10819     beforeFx : function(o){
10820         if(this.hasActiveFx() && !o.concurrent){
10821            if(o.stopFx){
10822                this.stopFx();
10823                return true;
10824            }
10825            return false;
10826         }
10827         return true;
10828     },
10829
10830         /**
10831          * Returns true if the element is currently blocking so that no other effect can be queued
10832          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10833          * used to ensure that an effect initiated by a user action runs to completion prior to the
10834          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10835          * @return {Boolean} True if blocking, else false
10836          */
10837     hasFxBlock : function(){
10838         var q = this.fxQueue;
10839         return q && q[0] && q[0].block;
10840     },
10841
10842         /* @private */
10843     queueFx : function(o, fn){
10844         if(!this.fxQueue){
10845             this.fxQueue = [];
10846         }
10847         if(!this.hasFxBlock()){
10848             Roo.applyIf(o, this.fxDefaults);
10849             if(!o.concurrent){
10850                 var run = this.beforeFx(o);
10851                 fn.block = o.block;
10852                 this.fxQueue.push(fn);
10853                 if(run){
10854                     this.nextFx();
10855                 }
10856             }else{
10857                 fn.call(this);
10858             }
10859         }
10860         return this;
10861     },
10862
10863         /* @private */
10864     fxWrap : function(pos, o, vis){
10865         var wrap;
10866         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10867             var wrapXY;
10868             if(o.fixPosition){
10869                 wrapXY = this.getXY();
10870             }
10871             var div = document.createElement("div");
10872             div.style.visibility = vis;
10873             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10874             wrap.setPositioning(pos);
10875             if(wrap.getStyle("position") == "static"){
10876                 wrap.position("relative");
10877             }
10878             this.clearPositioning('auto');
10879             wrap.clip();
10880             wrap.dom.appendChild(this.dom);
10881             if(wrapXY){
10882                 wrap.setXY(wrapXY);
10883             }
10884         }
10885         return wrap;
10886     },
10887
10888         /* @private */
10889     fxUnwrap : function(wrap, pos, o){
10890         this.clearPositioning();
10891         this.setPositioning(pos);
10892         if(!o.wrap){
10893             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10894             wrap.remove();
10895         }
10896     },
10897
10898         /* @private */
10899     getFxRestore : function(){
10900         var st = this.dom.style;
10901         return {pos: this.getPositioning(), width: st.width, height : st.height};
10902     },
10903
10904         /* @private */
10905     afterFx : function(o){
10906         if(o.afterStyle){
10907             this.applyStyles(o.afterStyle);
10908         }
10909         if(o.afterCls){
10910             this.addClass(o.afterCls);
10911         }
10912         if(o.remove === true){
10913             this.remove();
10914         }
10915         Roo.callback(o.callback, o.scope, [this]);
10916         if(!o.concurrent){
10917             this.fxQueue.shift();
10918             this.nextFx();
10919         }
10920     },
10921
10922         /* @private */
10923     getFxEl : function(){ // support for composite element fx
10924         return Roo.get(this.dom);
10925     },
10926
10927         /* @private */
10928     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10929         animType = animType || 'run';
10930         opt = opt || {};
10931         var anim = Roo.lib.Anim[animType](
10932             this.dom, args,
10933             (opt.duration || defaultDur) || .35,
10934             (opt.easing || defaultEase) || 'easeOut',
10935             function(){
10936                 Roo.callback(cb, this);
10937             },
10938             this
10939         );
10940         opt.anim = anim;
10941         return anim;
10942     }
10943 };
10944
10945 // backwords compat
10946 Roo.Fx.resize = Roo.Fx.scale;
10947
10948 //When included, Roo.Fx is automatically applied to Element so that all basic
10949 //effects are available directly via the Element API
10950 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10951  * Based on:
10952  * Ext JS Library 1.1.1
10953  * Copyright(c) 2006-2007, Ext JS, LLC.
10954  *
10955  * Originally Released Under LGPL - original licence link has changed is not relivant.
10956  *
10957  * Fork - LGPL
10958  * <script type="text/javascript">
10959  */
10960
10961
10962 /**
10963  * @class Roo.CompositeElement
10964  * Standard composite class. Creates a Roo.Element for every element in the collection.
10965  * <br><br>
10966  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10967  * actions will be performed on all the elements in this collection.</b>
10968  * <br><br>
10969  * All methods return <i>this</i> and can be chained.
10970  <pre><code>
10971  var els = Roo.select("#some-el div.some-class", true);
10972  // or select directly from an existing element
10973  var el = Roo.get('some-el');
10974  el.select('div.some-class', true);
10975
10976  els.setWidth(100); // all elements become 100 width
10977  els.hide(true); // all elements fade out and hide
10978  // or
10979  els.setWidth(100).hide(true);
10980  </code></pre>
10981  */
10982 Roo.CompositeElement = function(els){
10983     this.elements = [];
10984     this.addElements(els);
10985 };
10986 Roo.CompositeElement.prototype = {
10987     isComposite: true,
10988     addElements : function(els){
10989         if(!els) return this;
10990         if(typeof els == "string"){
10991             els = Roo.Element.selectorFunction(els);
10992         }
10993         var yels = this.elements;
10994         var index = yels.length-1;
10995         for(var i = 0, len = els.length; i < len; i++) {
10996                 yels[++index] = Roo.get(els[i]);
10997         }
10998         return this;
10999     },
11000
11001     /**
11002     * Clears this composite and adds the elements returned by the passed selector.
11003     * @param {String/Array} els A string CSS selector, an array of elements or an element
11004     * @return {CompositeElement} this
11005     */
11006     fill : function(els){
11007         this.elements = [];
11008         this.add(els);
11009         return this;
11010     },
11011
11012     /**
11013     * Filters this composite to only elements that match the passed selector.
11014     * @param {String} selector A string CSS selector
11015     * @param {Boolean} inverse return inverse filter (not matches)
11016     * @return {CompositeElement} this
11017     */
11018     filter : function(selector, inverse){
11019         var els = [];
11020         inverse = inverse || false;
11021         this.each(function(el){
11022             var match = inverse ? !el.is(selector) : el.is(selector);
11023             if(match){
11024                 els[els.length] = el.dom;
11025             }
11026         });
11027         this.fill(els);
11028         return this;
11029     },
11030
11031     invoke : function(fn, args){
11032         var els = this.elements;
11033         for(var i = 0, len = els.length; i < len; i++) {
11034                 Roo.Element.prototype[fn].apply(els[i], args);
11035         }
11036         return this;
11037     },
11038     /**
11039     * Adds elements to this composite.
11040     * @param {String/Array} els A string CSS selector, an array of elements or an element
11041     * @return {CompositeElement} this
11042     */
11043     add : function(els){
11044         if(typeof els == "string"){
11045             this.addElements(Roo.Element.selectorFunction(els));
11046         }else if(els.length !== undefined){
11047             this.addElements(els);
11048         }else{
11049             this.addElements([els]);
11050         }
11051         return this;
11052     },
11053     /**
11054     * Calls the passed function passing (el, this, index) for each element in this composite.
11055     * @param {Function} fn The function to call
11056     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11057     * @return {CompositeElement} this
11058     */
11059     each : function(fn, scope){
11060         var els = this.elements;
11061         for(var i = 0, len = els.length; i < len; i++){
11062             if(fn.call(scope || els[i], els[i], this, i) === false) {
11063                 break;
11064             }
11065         }
11066         return this;
11067     },
11068
11069     /**
11070      * Returns the Element object at the specified index
11071      * @param {Number} index
11072      * @return {Roo.Element}
11073      */
11074     item : function(index){
11075         return this.elements[index] || null;
11076     },
11077
11078     /**
11079      * Returns the first Element
11080      * @return {Roo.Element}
11081      */
11082     first : function(){
11083         return this.item(0);
11084     },
11085
11086     /**
11087      * Returns the last Element
11088      * @return {Roo.Element}
11089      */
11090     last : function(){
11091         return this.item(this.elements.length-1);
11092     },
11093
11094     /**
11095      * Returns the number of elements in this composite
11096      * @return Number
11097      */
11098     getCount : function(){
11099         return this.elements.length;
11100     },
11101
11102     /**
11103      * Returns true if this composite contains the passed element
11104      * @return Boolean
11105      */
11106     contains : function(el){
11107         return this.indexOf(el) !== -1;
11108     },
11109
11110     /**
11111      * Returns true if this composite contains the passed element
11112      * @return Boolean
11113      */
11114     indexOf : function(el){
11115         return this.elements.indexOf(Roo.get(el));
11116     },
11117
11118
11119     /**
11120     * Removes the specified element(s).
11121     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11122     * or an array of any of those.
11123     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11124     * @return {CompositeElement} this
11125     */
11126     removeElement : function(el, removeDom){
11127         if(el instanceof Array){
11128             for(var i = 0, len = el.length; i < len; i++){
11129                 this.removeElement(el[i]);
11130             }
11131             return this;
11132         }
11133         var index = typeof el == 'number' ? el : this.indexOf(el);
11134         if(index !== -1){
11135             if(removeDom){
11136                 var d = this.elements[index];
11137                 if(d.dom){
11138                     d.remove();
11139                 }else{
11140                     d.parentNode.removeChild(d);
11141                 }
11142             }
11143             this.elements.splice(index, 1);
11144         }
11145         return this;
11146     },
11147
11148     /**
11149     * Replaces the specified element with the passed element.
11150     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11151     * to replace.
11152     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11153     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11154     * @return {CompositeElement} this
11155     */
11156     replaceElement : function(el, replacement, domReplace){
11157         var index = typeof el == 'number' ? el : this.indexOf(el);
11158         if(index !== -1){
11159             if(domReplace){
11160                 this.elements[index].replaceWith(replacement);
11161             }else{
11162                 this.elements.splice(index, 1, Roo.get(replacement))
11163             }
11164         }
11165         return this;
11166     },
11167
11168     /**
11169      * Removes all elements.
11170      */
11171     clear : function(){
11172         this.elements = [];
11173     }
11174 };
11175 (function(){
11176     Roo.CompositeElement.createCall = function(proto, fnName){
11177         if(!proto[fnName]){
11178             proto[fnName] = function(){
11179                 return this.invoke(fnName, arguments);
11180             };
11181         }
11182     };
11183     for(var fnName in Roo.Element.prototype){
11184         if(typeof Roo.Element.prototype[fnName] == "function"){
11185             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11186         }
11187     };
11188 })();
11189 /*
11190  * Based on:
11191  * Ext JS Library 1.1.1
11192  * Copyright(c) 2006-2007, Ext JS, LLC.
11193  *
11194  * Originally Released Under LGPL - original licence link has changed is not relivant.
11195  *
11196  * Fork - LGPL
11197  * <script type="text/javascript">
11198  */
11199
11200 /**
11201  * @class Roo.CompositeElementLite
11202  * @extends Roo.CompositeElement
11203  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11204  <pre><code>
11205  var els = Roo.select("#some-el div.some-class");
11206  // or select directly from an existing element
11207  var el = Roo.get('some-el');
11208  el.select('div.some-class');
11209
11210  els.setWidth(100); // all elements become 100 width
11211  els.hide(true); // all elements fade out and hide
11212  // or
11213  els.setWidth(100).hide(true);
11214  </code></pre><br><br>
11215  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11216  * actions will be performed on all the elements in this collection.</b>
11217  */
11218 Roo.CompositeElementLite = function(els){
11219     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11220     this.el = new Roo.Element.Flyweight();
11221 };
11222 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11223     addElements : function(els){
11224         if(els){
11225             if(els instanceof Array){
11226                 this.elements = this.elements.concat(els);
11227             }else{
11228                 var yels = this.elements;
11229                 var index = yels.length-1;
11230                 for(var i = 0, len = els.length; i < len; i++) {
11231                     yels[++index] = els[i];
11232                 }
11233             }
11234         }
11235         return this;
11236     },
11237     invoke : function(fn, args){
11238         var els = this.elements;
11239         var el = this.el;
11240         for(var i = 0, len = els.length; i < len; i++) {
11241             el.dom = els[i];
11242                 Roo.Element.prototype[fn].apply(el, args);
11243         }
11244         return this;
11245     },
11246     /**
11247      * Returns a flyweight Element of the dom element object at the specified index
11248      * @param {Number} index
11249      * @return {Roo.Element}
11250      */
11251     item : function(index){
11252         if(!this.elements[index]){
11253             return null;
11254         }
11255         this.el.dom = this.elements[index];
11256         return this.el;
11257     },
11258
11259     // fixes scope with flyweight
11260     addListener : function(eventName, handler, scope, opt){
11261         var els = this.elements;
11262         for(var i = 0, len = els.length; i < len; i++) {
11263             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11264         }
11265         return this;
11266     },
11267
11268     /**
11269     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11270     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11271     * a reference to the dom node, use el.dom.</b>
11272     * @param {Function} fn The function to call
11273     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11274     * @return {CompositeElement} this
11275     */
11276     each : function(fn, scope){
11277         var els = this.elements;
11278         var el = this.el;
11279         for(var i = 0, len = els.length; i < len; i++){
11280             el.dom = els[i];
11281                 if(fn.call(scope || el, el, this, i) === false){
11282                 break;
11283             }
11284         }
11285         return this;
11286     },
11287
11288     indexOf : function(el){
11289         return this.elements.indexOf(Roo.getDom(el));
11290     },
11291
11292     replaceElement : function(el, replacement, domReplace){
11293         var index = typeof el == 'number' ? el : this.indexOf(el);
11294         if(index !== -1){
11295             replacement = Roo.getDom(replacement);
11296             if(domReplace){
11297                 var d = this.elements[index];
11298                 d.parentNode.insertBefore(replacement, d);
11299                 d.parentNode.removeChild(d);
11300             }
11301             this.elements.splice(index, 1, replacement);
11302         }
11303         return this;
11304     }
11305 });
11306 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11307
11308 /*
11309  * Based on:
11310  * Ext JS Library 1.1.1
11311  * Copyright(c) 2006-2007, Ext JS, LLC.
11312  *
11313  * Originally Released Under LGPL - original licence link has changed is not relivant.
11314  *
11315  * Fork - LGPL
11316  * <script type="text/javascript">
11317  */
11318
11319  
11320
11321 /**
11322  * @class Roo.data.Connection
11323  * @extends Roo.util.Observable
11324  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11325  * either to a configured URL, or to a URL specified at request time.<br><br>
11326  * <p>
11327  * Requests made by this class are asynchronous, and will return immediately. No data from
11328  * the server will be available to the statement immediately following the {@link #request} call.
11329  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11330  * <p>
11331  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11332  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11333  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11334  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11335  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11336  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11337  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11338  * standard DOM methods.
11339  * @constructor
11340  * @param {Object} config a configuration object.
11341  */
11342 Roo.data.Connection = function(config){
11343     Roo.apply(this, config);
11344     this.addEvents({
11345         /**
11346          * @event beforerequest
11347          * Fires before a network request is made to retrieve a data object.
11348          * @param {Connection} conn This Connection object.
11349          * @param {Object} options The options config object passed to the {@link #request} method.
11350          */
11351         "beforerequest" : true,
11352         /**
11353          * @event requestcomplete
11354          * Fires if the request was successfully completed.
11355          * @param {Connection} conn This Connection object.
11356          * @param {Object} response The XHR object containing the response data.
11357          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11358          * @param {Object} options The options config object passed to the {@link #request} method.
11359          */
11360         "requestcomplete" : true,
11361         /**
11362          * @event requestexception
11363          * Fires if an error HTTP status was returned from the server.
11364          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11365          * @param {Connection} conn This Connection object.
11366          * @param {Object} response The XHR object containing the response data.
11367          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11368          * @param {Object} options The options config object passed to the {@link #request} method.
11369          */
11370         "requestexception" : true
11371     });
11372     Roo.data.Connection.superclass.constructor.call(this);
11373 };
11374
11375 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11376     /**
11377      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11378      */
11379     /**
11380      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11381      * extra parameters to each request made by this object. (defaults to undefined)
11382      */
11383     /**
11384      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11385      *  to each request made by this object. (defaults to undefined)
11386      */
11387     /**
11388      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11389      */
11390     /**
11391      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11392      */
11393     timeout : 30000,
11394     /**
11395      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11396      * @type Boolean
11397      */
11398     autoAbort:false,
11399
11400     /**
11401      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11402      * @type Boolean
11403      */
11404     disableCaching: true,
11405
11406     /**
11407      * Sends an HTTP request to a remote server.
11408      * @param {Object} options An object which may contain the following properties:<ul>
11409      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11410      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11411      * request, a url encoded string or a function to call to get either.</li>
11412      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11413      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11414      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11415      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11416      * <li>options {Object} The parameter to the request call.</li>
11417      * <li>success {Boolean} True if the request succeeded.</li>
11418      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11419      * </ul></li>
11420      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11421      * The callback is passed the following parameters:<ul>
11422      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11423      * <li>options {Object} The parameter to the request call.</li>
11424      * </ul></li>
11425      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11426      * The callback is passed the following parameters:<ul>
11427      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11428      * <li>options {Object} The parameter to the request call.</li>
11429      * </ul></li>
11430      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11431      * for the callback function. Defaults to the browser window.</li>
11432      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11433      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11434      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11435      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11436      * params for the post data. Any params will be appended to the URL.</li>
11437      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11438      * </ul>
11439      * @return {Number} transactionId
11440      */
11441     request : function(o){
11442         if(this.fireEvent("beforerequest", this, o) !== false){
11443             var p = o.params;
11444
11445             if(typeof p == "function"){
11446                 p = p.call(o.scope||window, o);
11447             }
11448             if(typeof p == "object"){
11449                 p = Roo.urlEncode(o.params);
11450             }
11451             if(this.extraParams){
11452                 var extras = Roo.urlEncode(this.extraParams);
11453                 p = p ? (p + '&' + extras) : extras;
11454             }
11455
11456             var url = o.url || this.url;
11457             if(typeof url == 'function'){
11458                 url = url.call(o.scope||window, o);
11459             }
11460
11461             if(o.form){
11462                 var form = Roo.getDom(o.form);
11463                 url = url || form.action;
11464
11465                 var enctype = form.getAttribute("enctype");
11466                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11467                     return this.doFormUpload(o, p, url);
11468                 }
11469                 var f = Roo.lib.Ajax.serializeForm(form);
11470                 p = p ? (p + '&' + f) : f;
11471             }
11472
11473             var hs = o.headers;
11474             if(this.defaultHeaders){
11475                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11476                 if(!o.headers){
11477                     o.headers = hs;
11478                 }
11479             }
11480
11481             var cb = {
11482                 success: this.handleResponse,
11483                 failure: this.handleFailure,
11484                 scope: this,
11485                 argument: {options: o},
11486                 timeout : o.timeout || this.timeout
11487             };
11488
11489             var method = o.method||this.method||(p ? "POST" : "GET");
11490
11491             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11492                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11493             }
11494
11495             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11496                 if(o.autoAbort){
11497                     this.abort();
11498                 }
11499             }else if(this.autoAbort !== false){
11500                 this.abort();
11501             }
11502
11503             if((method == 'GET' && p) || o.xmlData){
11504                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11505                 p = '';
11506             }
11507             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11508             return this.transId;
11509         }else{
11510             Roo.callback(o.callback, o.scope, [o, null, null]);
11511             return null;
11512         }
11513     },
11514
11515     /**
11516      * Determine whether this object has a request outstanding.
11517      * @param {Number} transactionId (Optional) defaults to the last transaction
11518      * @return {Boolean} True if there is an outstanding request.
11519      */
11520     isLoading : function(transId){
11521         if(transId){
11522             return Roo.lib.Ajax.isCallInProgress(transId);
11523         }else{
11524             return this.transId ? true : false;
11525         }
11526     },
11527
11528     /**
11529      * Aborts any outstanding request.
11530      * @param {Number} transactionId (Optional) defaults to the last transaction
11531      */
11532     abort : function(transId){
11533         if(transId || this.isLoading()){
11534             Roo.lib.Ajax.abort(transId || this.transId);
11535         }
11536     },
11537
11538     // private
11539     handleResponse : function(response){
11540         this.transId = false;
11541         var options = response.argument.options;
11542         response.argument = options ? options.argument : null;
11543         this.fireEvent("requestcomplete", this, response, options);
11544         Roo.callback(options.success, options.scope, [response, options]);
11545         Roo.callback(options.callback, options.scope, [options, true, response]);
11546     },
11547
11548     // private
11549     handleFailure : function(response, e){
11550         this.transId = false;
11551         var options = response.argument.options;
11552         response.argument = options ? options.argument : null;
11553         this.fireEvent("requestexception", this, response, options, e);
11554         Roo.callback(options.failure, options.scope, [response, options]);
11555         Roo.callback(options.callback, options.scope, [options, false, response]);
11556     },
11557
11558     // private
11559     doFormUpload : function(o, ps, url){
11560         var id = Roo.id();
11561         var frame = document.createElement('iframe');
11562         frame.id = id;
11563         frame.name = id;
11564         frame.className = 'x-hidden';
11565         if(Roo.isIE){
11566             frame.src = Roo.SSL_SECURE_URL;
11567         }
11568         document.body.appendChild(frame);
11569
11570         if(Roo.isIE){
11571            document.frames[id].name = id;
11572         }
11573
11574         var form = Roo.getDom(o.form);
11575         form.target = id;
11576         form.method = 'POST';
11577         form.enctype = form.encoding = 'multipart/form-data';
11578         if(url){
11579             form.action = url;
11580         }
11581
11582         var hiddens, hd;
11583         if(ps){ // add dynamic params
11584             hiddens = [];
11585             ps = Roo.urlDecode(ps, false);
11586             for(var k in ps){
11587                 if(ps.hasOwnProperty(k)){
11588                     hd = document.createElement('input');
11589                     hd.type = 'hidden';
11590                     hd.name = k;
11591                     hd.value = ps[k];
11592                     form.appendChild(hd);
11593                     hiddens.push(hd);
11594                 }
11595             }
11596         }
11597
11598         function cb(){
11599             var r = {  // bogus response object
11600                 responseText : '',
11601                 responseXML : null
11602             };
11603
11604             r.argument = o ? o.argument : null;
11605
11606             try { //
11607                 var doc;
11608                 if(Roo.isIE){
11609                     doc = frame.contentWindow.document;
11610                 }else {
11611                     doc = (frame.contentDocument || window.frames[id].document);
11612                 }
11613                 if(doc && doc.body){
11614                     r.responseText = doc.body.innerHTML;
11615                 }
11616                 if(doc && doc.XMLDocument){
11617                     r.responseXML = doc.XMLDocument;
11618                 }else {
11619                     r.responseXML = doc;
11620                 }
11621             }
11622             catch(e) {
11623                 // ignore
11624             }
11625
11626             Roo.EventManager.removeListener(frame, 'load', cb, this);
11627
11628             this.fireEvent("requestcomplete", this, r, o);
11629             Roo.callback(o.success, o.scope, [r, o]);
11630             Roo.callback(o.callback, o.scope, [o, true, r]);
11631
11632             setTimeout(function(){document.body.removeChild(frame);}, 100);
11633         }
11634
11635         Roo.EventManager.on(frame, 'load', cb, this);
11636         form.submit();
11637
11638         if(hiddens){ // remove dynamic params
11639             for(var i = 0, len = hiddens.length; i < len; i++){
11640                 form.removeChild(hiddens[i]);
11641             }
11642         }
11643     }
11644 });
11645 /*
11646  * Based on:
11647  * Ext JS Library 1.1.1
11648  * Copyright(c) 2006-2007, Ext JS, LLC.
11649  *
11650  * Originally Released Under LGPL - original licence link has changed is not relivant.
11651  *
11652  * Fork - LGPL
11653  * <script type="text/javascript">
11654  */
11655  
11656 /**
11657  * Global Ajax request class.
11658  * 
11659  * @class Roo.Ajax
11660  * @extends Roo.data.Connection
11661  * @static
11662  * 
11663  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11664  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11665  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11666  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11667  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11668  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11669  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11670  */
11671 Roo.Ajax = new Roo.data.Connection({
11672     // fix up the docs
11673     /**
11674      * @scope Roo.Ajax
11675      * @type {Boolear} 
11676      */
11677     autoAbort : false,
11678
11679     /**
11680      * Serialize the passed form into a url encoded string
11681      * @scope Roo.Ajax
11682      * @param {String/HTMLElement} form
11683      * @return {String}
11684      */
11685     serializeForm : function(form){
11686         return Roo.lib.Ajax.serializeForm(form);
11687     }
11688 });/*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698
11699  
11700 /**
11701  * @class Roo.UpdateManager
11702  * @extends Roo.util.Observable
11703  * Provides AJAX-style update for Element object.<br><br>
11704  * Usage:<br>
11705  * <pre><code>
11706  * // Get it from a Roo.Element object
11707  * var el = Roo.get("foo");
11708  * var mgr = el.getUpdateManager();
11709  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11710  * ...
11711  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11712  * <br>
11713  * // or directly (returns the same UpdateManager instance)
11714  * var mgr = new Roo.UpdateManager("myElementId");
11715  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11716  * mgr.on("update", myFcnNeedsToKnow);
11717  * <br>
11718    // short handed call directly from the element object
11719    Roo.get("foo").load({
11720         url: "bar.php",
11721         scripts:true,
11722         params: "for=bar",
11723         text: "Loading Foo..."
11724    });
11725  * </code></pre>
11726  * @constructor
11727  * Create new UpdateManager directly.
11728  * @param {String/HTMLElement/Roo.Element} el The element to update
11729  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11730  */
11731 Roo.UpdateManager = function(el, forceNew){
11732     el = Roo.get(el);
11733     if(!forceNew && el.updateManager){
11734         return el.updateManager;
11735     }
11736     /**
11737      * The Element object
11738      * @type Roo.Element
11739      */
11740     this.el = el;
11741     /**
11742      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11743      * @type String
11744      */
11745     this.defaultUrl = null;
11746
11747     this.addEvents({
11748         /**
11749          * @event beforeupdate
11750          * Fired before an update is made, return false from your handler and the update is cancelled.
11751          * @param {Roo.Element} el
11752          * @param {String/Object/Function} url
11753          * @param {String/Object} params
11754          */
11755         "beforeupdate": true,
11756         /**
11757          * @event update
11758          * Fired after successful update is made.
11759          * @param {Roo.Element} el
11760          * @param {Object} oResponseObject The response Object
11761          */
11762         "update": true,
11763         /**
11764          * @event failure
11765          * Fired on update failure.
11766          * @param {Roo.Element} el
11767          * @param {Object} oResponseObject The response Object
11768          */
11769         "failure": true
11770     });
11771     var d = Roo.UpdateManager.defaults;
11772     /**
11773      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11774      * @type String
11775      */
11776     this.sslBlankUrl = d.sslBlankUrl;
11777     /**
11778      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11779      * @type Boolean
11780      */
11781     this.disableCaching = d.disableCaching;
11782     /**
11783      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11784      * @type String
11785      */
11786     this.indicatorText = d.indicatorText;
11787     /**
11788      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11789      * @type String
11790      */
11791     this.showLoadIndicator = d.showLoadIndicator;
11792     /**
11793      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11794      * @type Number
11795      */
11796     this.timeout = d.timeout;
11797
11798     /**
11799      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11800      * @type Boolean
11801      */
11802     this.loadScripts = d.loadScripts;
11803
11804     /**
11805      * Transaction object of current executing transaction
11806      */
11807     this.transaction = null;
11808
11809     /**
11810      * @private
11811      */
11812     this.autoRefreshProcId = null;
11813     /**
11814      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11815      * @type Function
11816      */
11817     this.refreshDelegate = this.refresh.createDelegate(this);
11818     /**
11819      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11820      * @type Function
11821      */
11822     this.updateDelegate = this.update.createDelegate(this);
11823     /**
11824      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11825      * @type Function
11826      */
11827     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11828     /**
11829      * @private
11830      */
11831     this.successDelegate = this.processSuccess.createDelegate(this);
11832     /**
11833      * @private
11834      */
11835     this.failureDelegate = this.processFailure.createDelegate(this);
11836
11837     if(!this.renderer){
11838      /**
11839       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11840       */
11841     this.renderer = new Roo.UpdateManager.BasicRenderer();
11842     }
11843     
11844     Roo.UpdateManager.superclass.constructor.call(this);
11845 };
11846
11847 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11848     /**
11849      * Get the Element this UpdateManager is bound to
11850      * @return {Roo.Element} The element
11851      */
11852     getEl : function(){
11853         return this.el;
11854     },
11855     /**
11856      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11857      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11858 <pre><code>
11859 um.update({<br/>
11860     url: "your-url.php",<br/>
11861     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11862     callback: yourFunction,<br/>
11863     scope: yourObject, //(optional scope)  <br/>
11864     discardUrl: false, <br/>
11865     nocache: false,<br/>
11866     text: "Loading...",<br/>
11867     timeout: 30,<br/>
11868     scripts: false<br/>
11869 });
11870 </code></pre>
11871      * The only required property is url. The optional properties nocache, text and scripts
11872      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11873      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11874      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11875      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11876      */
11877     update : function(url, params, callback, discardUrl){
11878         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11879             var method = this.method,
11880                 cfg;
11881             if(typeof url == "object"){ // must be config object
11882                 cfg = url;
11883                 url = cfg.url;
11884                 params = params || cfg.params;
11885                 callback = callback || cfg.callback;
11886                 discardUrl = discardUrl || cfg.discardUrl;
11887                 if(callback && cfg.scope){
11888                     callback = callback.createDelegate(cfg.scope);
11889                 }
11890                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11891                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11892                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11893                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11894                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11895             }
11896             this.showLoading();
11897             if(!discardUrl){
11898                 this.defaultUrl = url;
11899             }
11900             if(typeof url == "function"){
11901                 url = url.call(this);
11902             }
11903
11904             method = method || (params ? "POST" : "GET");
11905             if(method == "GET"){
11906                 url = this.prepareUrl(url);
11907             }
11908
11909             var o = Roo.apply(cfg ||{}, {
11910                 url : url,
11911                 params: params,
11912                 success: this.successDelegate,
11913                 failure: this.failureDelegate,
11914                 callback: undefined,
11915                 timeout: (this.timeout*1000),
11916                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11917             });
11918             Roo.log("updated manager called with timeout of " + o.timeout);
11919             this.transaction = Roo.Ajax.request(o);
11920         }
11921     },
11922
11923     /**
11924      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11925      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11926      * @param {String/HTMLElement} form The form Id or form element
11927      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11928      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11929      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11930      */
11931     formUpdate : function(form, url, reset, callback){
11932         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11933             if(typeof url == "function"){
11934                 url = url.call(this);
11935             }
11936             form = Roo.getDom(form);
11937             this.transaction = Roo.Ajax.request({
11938                 form: form,
11939                 url:url,
11940                 success: this.successDelegate,
11941                 failure: this.failureDelegate,
11942                 timeout: (this.timeout*1000),
11943                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11944             });
11945             this.showLoading.defer(1, this);
11946         }
11947     },
11948
11949     /**
11950      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11951      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11952      */
11953     refresh : function(callback){
11954         if(this.defaultUrl == null){
11955             return;
11956         }
11957         this.update(this.defaultUrl, null, callback, true);
11958     },
11959
11960     /**
11961      * Set this element to auto refresh.
11962      * @param {Number} interval How often to update (in seconds).
11963      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11964      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11965      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11966      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11967      */
11968     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11969         if(refreshNow){
11970             this.update(url || this.defaultUrl, params, callback, true);
11971         }
11972         if(this.autoRefreshProcId){
11973             clearInterval(this.autoRefreshProcId);
11974         }
11975         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11976     },
11977
11978     /**
11979      * Stop auto refresh on this element.
11980      */
11981      stopAutoRefresh : function(){
11982         if(this.autoRefreshProcId){
11983             clearInterval(this.autoRefreshProcId);
11984             delete this.autoRefreshProcId;
11985         }
11986     },
11987
11988     isAutoRefreshing : function(){
11989        return this.autoRefreshProcId ? true : false;
11990     },
11991     /**
11992      * Called to update the element to "Loading" state. Override to perform custom action.
11993      */
11994     showLoading : function(){
11995         if(this.showLoadIndicator){
11996             this.el.update(this.indicatorText);
11997         }
11998     },
11999
12000     /**
12001      * Adds unique parameter to query string if disableCaching = true
12002      * @private
12003      */
12004     prepareUrl : function(url){
12005         if(this.disableCaching){
12006             var append = "_dc=" + (new Date().getTime());
12007             if(url.indexOf("?") !== -1){
12008                 url += "&" + append;
12009             }else{
12010                 url += "?" + append;
12011             }
12012         }
12013         return url;
12014     },
12015
12016     /**
12017      * @private
12018      */
12019     processSuccess : function(response){
12020         this.transaction = null;
12021         if(response.argument.form && response.argument.reset){
12022             try{ // put in try/catch since some older FF releases had problems with this
12023                 response.argument.form.reset();
12024             }catch(e){}
12025         }
12026         if(this.loadScripts){
12027             this.renderer.render(this.el, response, this,
12028                 this.updateComplete.createDelegate(this, [response]));
12029         }else{
12030             this.renderer.render(this.el, response, this);
12031             this.updateComplete(response);
12032         }
12033     },
12034
12035     updateComplete : function(response){
12036         this.fireEvent("update", this.el, response);
12037         if(typeof response.argument.callback == "function"){
12038             response.argument.callback(this.el, true, response);
12039         }
12040     },
12041
12042     /**
12043      * @private
12044      */
12045     processFailure : function(response){
12046         this.transaction = null;
12047         this.fireEvent("failure", this.el, response);
12048         if(typeof response.argument.callback == "function"){
12049             response.argument.callback(this.el, false, response);
12050         }
12051     },
12052
12053     /**
12054      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12055      * @param {Object} renderer The object implementing the render() method
12056      */
12057     setRenderer : function(renderer){
12058         this.renderer = renderer;
12059     },
12060
12061     getRenderer : function(){
12062        return this.renderer;
12063     },
12064
12065     /**
12066      * Set the defaultUrl used for updates
12067      * @param {String/Function} defaultUrl The url or a function to call to get the url
12068      */
12069     setDefaultUrl : function(defaultUrl){
12070         this.defaultUrl = defaultUrl;
12071     },
12072
12073     /**
12074      * Aborts the executing transaction
12075      */
12076     abort : function(){
12077         if(this.transaction){
12078             Roo.Ajax.abort(this.transaction);
12079         }
12080     },
12081
12082     /**
12083      * Returns true if an update is in progress
12084      * @return {Boolean}
12085      */
12086     isUpdating : function(){
12087         if(this.transaction){
12088             return Roo.Ajax.isLoading(this.transaction);
12089         }
12090         return false;
12091     }
12092 });
12093
12094 /**
12095  * @class Roo.UpdateManager.defaults
12096  * @static (not really - but it helps the doc tool)
12097  * The defaults collection enables customizing the default properties of UpdateManager
12098  */
12099    Roo.UpdateManager.defaults = {
12100        /**
12101          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12102          * @type Number
12103          */
12104          timeout : 30,
12105
12106          /**
12107          * True to process scripts by default (Defaults to false).
12108          * @type Boolean
12109          */
12110         loadScripts : false,
12111
12112         /**
12113         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12114         * @type String
12115         */
12116         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12117         /**
12118          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12119          * @type Boolean
12120          */
12121         disableCaching : false,
12122         /**
12123          * Whether to show indicatorText when loading (Defaults to true).
12124          * @type Boolean
12125          */
12126         showLoadIndicator : true,
12127         /**
12128          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12129          * @type String
12130          */
12131         indicatorText : '<div class="loading-indicator">Loading...</div>'
12132    };
12133
12134 /**
12135  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12136  *Usage:
12137  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12138  * @param {String/HTMLElement/Roo.Element} el The element to update
12139  * @param {String} url The url
12140  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12141  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12142  * @static
12143  * @deprecated
12144  * @member Roo.UpdateManager
12145  */
12146 Roo.UpdateManager.updateElement = function(el, url, params, options){
12147     var um = Roo.get(el, true).getUpdateManager();
12148     Roo.apply(um, options);
12149     um.update(url, params, options ? options.callback : null);
12150 };
12151 // alias for backwards compat
12152 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12153 /**
12154  * @class Roo.UpdateManager.BasicRenderer
12155  * Default Content renderer. Updates the elements innerHTML with the responseText.
12156  */
12157 Roo.UpdateManager.BasicRenderer = function(){};
12158
12159 Roo.UpdateManager.BasicRenderer.prototype = {
12160     /**
12161      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12162      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12163      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12164      * @param {Roo.Element} el The element being rendered
12165      * @param {Object} response The YUI Connect response object
12166      * @param {UpdateManager} updateManager The calling update manager
12167      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12168      */
12169      render : function(el, response, updateManager, callback){
12170         el.update(response.responseText, updateManager.loadScripts, callback);
12171     }
12172 };
12173 /*
12174  * Based on:
12175  * Roo JS
12176  * (c)) Alan Knowles
12177  * Licence : LGPL
12178  */
12179
12180
12181 /**
12182  * @class Roo.DomTemplate
12183  * @extends Roo.Template
12184  * An effort at a dom based template engine..
12185  *
12186  * Similar to XTemplate, except it uses dom parsing to create the template..
12187  *
12188  * Supported features:
12189  *
12190  *  Tags:
12191
12192 <pre><code>
12193       {a_variable} - output encoded.
12194       {a_variable.format:("Y-m-d")} - call a method on the variable
12195       {a_variable:raw} - unencoded output
12196       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12197       {a_variable:this.method_on_template(...)} - call a method on the template object.
12198  
12199 </code></pre>
12200  *  The tpl tag:
12201 <pre><code>
12202         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12203         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12204         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12205         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12206   
12207 </code></pre>
12208  *      
12209  */
12210 Roo.DomTemplate = function()
12211 {
12212      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12213      if (this.html) {
12214         this.compile();
12215      }
12216 };
12217
12218
12219 Roo.extend(Roo.DomTemplate, Roo.Template, {
12220     /**
12221      * id counter for sub templates.
12222      */
12223     id : 0,
12224     /**
12225      * flag to indicate if dom parser is inside a pre,
12226      * it will strip whitespace if not.
12227      */
12228     inPre : false,
12229     
12230     /**
12231      * The various sub templates
12232      */
12233     tpls : false,
12234     
12235     
12236     
12237     /**
12238      *
12239      * basic tag replacing syntax
12240      * WORD:WORD()
12241      *
12242      * // you can fake an object call by doing this
12243      *  x.t:(test,tesT) 
12244      * 
12245      */
12246     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12247     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12248     
12249     iterChild : function (node, method) {
12250         
12251         var oldPre = this.inPre;
12252         if (node.tagName == 'PRE') {
12253             this.inPre = true;
12254         }
12255         for( var i = 0; i < node.childNodes.length; i++) {
12256             method.call(this, node.childNodes[i]);
12257         }
12258         this.inPre = oldPre;
12259     },
12260     
12261     
12262     
12263     /**
12264      * compile the template
12265      *
12266      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12267      *
12268      */
12269     compile: function()
12270     {
12271         var s = this.html;
12272         
12273         // covert the html into DOM...
12274         var doc = false;
12275         var div =false;
12276         try {
12277             doc = document.implementation.createHTMLDocument("");
12278             doc.documentElement.innerHTML =   this.html  ;
12279             div = doc.documentElement;
12280         } catch (e) {
12281             // old IE... - nasty -- it causes all sorts of issues.. with
12282             // images getting pulled from server..
12283             div = document.createElement('div');
12284             div.innerHTML = this.html;
12285         }
12286         //doc.documentElement.innerHTML = htmlBody
12287          
12288         
12289         
12290         this.tpls = [];
12291         var _t = this;
12292         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12293         
12294         var tpls = this.tpls;
12295         
12296         // create a top level template from the snippet..
12297         
12298         //Roo.log(div.innerHTML);
12299         
12300         var tpl = {
12301             uid : 'master',
12302             id : this.id++,
12303             attr : false,
12304             value : false,
12305             body : div.innerHTML,
12306             
12307             forCall : false,
12308             execCall : false,
12309             dom : div,
12310             isTop : true
12311             
12312         };
12313         tpls.unshift(tpl);
12314         
12315         
12316         // compile them...
12317         this.tpls = [];
12318         Roo.each(tpls, function(tp){
12319             this.compileTpl(tp);
12320             this.tpls[tp.id] = tp;
12321         }, this);
12322         
12323         this.master = tpls[0];
12324         return this;
12325         
12326         
12327     },
12328     
12329     compileNode : function(node, istop) {
12330         // test for
12331         //Roo.log(node);
12332         
12333         
12334         // skip anything not a tag..
12335         if (node.nodeType != 1) {
12336             if (node.nodeType == 3 && !this.inPre) {
12337                 // reduce white space..
12338                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12339                 
12340             }
12341             return;
12342         }
12343         
12344         var tpl = {
12345             uid : false,
12346             id : false,
12347             attr : false,
12348             value : false,
12349             body : '',
12350             
12351             forCall : false,
12352             execCall : false,
12353             dom : false,
12354             isTop : istop
12355             
12356             
12357         };
12358         
12359         
12360         switch(true) {
12361             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12362             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12363             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12364             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12365             // no default..
12366         }
12367         
12368         
12369         if (!tpl.attr) {
12370             // just itterate children..
12371             this.iterChild(node,this.compileNode);
12372             return;
12373         }
12374         tpl.uid = this.id++;
12375         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12376         node.removeAttribute('roo-'+ tpl.attr);
12377         if (tpl.attr != 'name') {
12378             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12379             node.parentNode.replaceChild(placeholder,  node);
12380         } else {
12381             
12382             var placeholder =  document.createElement('span');
12383             placeholder.className = 'roo-tpl-' + tpl.value;
12384             node.parentNode.replaceChild(placeholder,  node);
12385         }
12386         
12387         // parent now sees '{domtplXXXX}
12388         this.iterChild(node,this.compileNode);
12389         
12390         // we should now have node body...
12391         var div = document.createElement('div');
12392         div.appendChild(node);
12393         tpl.dom = node;
12394         // this has the unfortunate side effect of converting tagged attributes
12395         // eg. href="{...}" into %7C...%7D
12396         // this has been fixed by searching for those combo's although it's a bit hacky..
12397         
12398         
12399         tpl.body = div.innerHTML;
12400         
12401         
12402          
12403         tpl.id = tpl.uid;
12404         switch(tpl.attr) {
12405             case 'for' :
12406                 switch (tpl.value) {
12407                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12408                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12409                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12410                 }
12411                 break;
12412             
12413             case 'exec':
12414                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12415                 break;
12416             
12417             case 'if':     
12418                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12419                 break;
12420             
12421             case 'name':
12422                 tpl.id  = tpl.value; // replace non characters???
12423                 break;
12424             
12425         }
12426         
12427         
12428         this.tpls.push(tpl);
12429         
12430         
12431         
12432     },
12433     
12434     
12435     
12436     
12437     /**
12438      * Compile a segment of the template into a 'sub-template'
12439      *
12440      * 
12441      * 
12442      *
12443      */
12444     compileTpl : function(tpl)
12445     {
12446         var fm = Roo.util.Format;
12447         var useF = this.disableFormats !== true;
12448         
12449         var sep = Roo.isGecko ? "+\n" : ",\n";
12450         
12451         var undef = function(str) {
12452             Roo.debug && Roo.log("Property not found :"  + str);
12453             return '';
12454         };
12455           
12456         //Roo.log(tpl.body);
12457         
12458         
12459         
12460         var fn = function(m, lbrace, name, format, args)
12461         {
12462             //Roo.log("ARGS");
12463             //Roo.log(arguments);
12464             args = args ? args.replace(/\\'/g,"'") : args;
12465             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12466             if (typeof(format) == 'undefined') {
12467                 format =  'htmlEncode'; 
12468             }
12469             if (format == 'raw' ) {
12470                 format = false;
12471             }
12472             
12473             if(name.substr(0, 6) == 'domtpl'){
12474                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12475             }
12476             
12477             // build an array of options to determine if value is undefined..
12478             
12479             // basically get 'xxxx.yyyy' then do
12480             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12481             //    (function () { Roo.log("Property not found"); return ''; })() :
12482             //    ......
12483             
12484             var udef_ar = [];
12485             var lookfor = '';
12486             Roo.each(name.split('.'), function(st) {
12487                 lookfor += (lookfor.length ? '.': '') + st;
12488                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12489             });
12490             
12491             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12492             
12493             
12494             if(format && useF){
12495                 
12496                 args = args ? ',' + args : "";
12497                  
12498                 if(format.substr(0, 5) != "this."){
12499                     format = "fm." + format + '(';
12500                 }else{
12501                     format = 'this.call("'+ format.substr(5) + '", ';
12502                     args = ", values";
12503                 }
12504                 
12505                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12506             }
12507              
12508             if (args && args.length) {
12509                 // called with xxyx.yuu:(test,test)
12510                 // change to ()
12511                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12512             }
12513             // raw.. - :raw modifier..
12514             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12515             
12516         };
12517         var body;
12518         // branched to use + in gecko and [].join() in others
12519         if(Roo.isGecko){
12520             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12521                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12522                     "';};};";
12523         }else{
12524             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12525             body.push(tpl.body.replace(/(\r\n|\n)/g,
12526                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12527             body.push("'].join('');};};");
12528             body = body.join('');
12529         }
12530         
12531         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12532        
12533         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12534         eval(body);
12535         
12536         return this;
12537     },
12538      
12539     /**
12540      * same as applyTemplate, except it's done to one of the subTemplates
12541      * when using named templates, you can do:
12542      *
12543      * var str = pl.applySubTemplate('your-name', values);
12544      *
12545      * 
12546      * @param {Number} id of the template
12547      * @param {Object} values to apply to template
12548      * @param {Object} parent (normaly the instance of this object)
12549      */
12550     applySubTemplate : function(id, values, parent)
12551     {
12552         
12553         
12554         var t = this.tpls[id];
12555         
12556         
12557         try { 
12558             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12559                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12560                 return '';
12561             }
12562         } catch(e) {
12563             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12564             Roo.log(values);
12565           
12566             return '';
12567         }
12568         try { 
12569             
12570             if(t.execCall && t.execCall.call(this, values, parent)){
12571                 return '';
12572             }
12573         } catch(e) {
12574             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12575             Roo.log(values);
12576             return '';
12577         }
12578         
12579         try {
12580             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12581             parent = t.target ? values : parent;
12582             if(t.forCall && vs instanceof Array){
12583                 var buf = [];
12584                 for(var i = 0, len = vs.length; i < len; i++){
12585                     try {
12586                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12587                     } catch (e) {
12588                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12589                         Roo.log(e.body);
12590                         //Roo.log(t.compiled);
12591                         Roo.log(vs[i]);
12592                     }   
12593                 }
12594                 return buf.join('');
12595             }
12596         } catch (e) {
12597             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12598             Roo.log(values);
12599             return '';
12600         }
12601         try {
12602             return t.compiled.call(this, vs, parent);
12603         } catch (e) {
12604             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12605             Roo.log(e.body);
12606             //Roo.log(t.compiled);
12607             Roo.log(values);
12608             return '';
12609         }
12610     },
12611
12612    
12613
12614     applyTemplate : function(values){
12615         return this.master.compiled.call(this, values, {});
12616         //var s = this.subs;
12617     },
12618
12619     apply : function(){
12620         return this.applyTemplate.apply(this, arguments);
12621     }
12622
12623  });
12624
12625 Roo.DomTemplate.from = function(el){
12626     el = Roo.getDom(el);
12627     return new Roo.Domtemplate(el.value || el.innerHTML);
12628 };/*
12629  * Based on:
12630  * Ext JS Library 1.1.1
12631  * Copyright(c) 2006-2007, Ext JS, LLC.
12632  *
12633  * Originally Released Under LGPL - original licence link has changed is not relivant.
12634  *
12635  * Fork - LGPL
12636  * <script type="text/javascript">
12637  */
12638
12639 /**
12640  * @class Roo.util.DelayedTask
12641  * Provides a convenient method of performing setTimeout where a new
12642  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12643  * You can use this class to buffer
12644  * the keypress events for a certain number of milliseconds, and perform only if they stop
12645  * for that amount of time.
12646  * @constructor The parameters to this constructor serve as defaults and are not required.
12647  * @param {Function} fn (optional) The default function to timeout
12648  * @param {Object} scope (optional) The default scope of that timeout
12649  * @param {Array} args (optional) The default Array of arguments
12650  */
12651 Roo.util.DelayedTask = function(fn, scope, args){
12652     var id = null, d, t;
12653
12654     var call = function(){
12655         var now = new Date().getTime();
12656         if(now - t >= d){
12657             clearInterval(id);
12658             id = null;
12659             fn.apply(scope, args || []);
12660         }
12661     };
12662     /**
12663      * Cancels any pending timeout and queues a new one
12664      * @param {Number} delay The milliseconds to delay
12665      * @param {Function} newFn (optional) Overrides function passed to constructor
12666      * @param {Object} newScope (optional) Overrides scope passed to constructor
12667      * @param {Array} newArgs (optional) Overrides args passed to constructor
12668      */
12669     this.delay = function(delay, newFn, newScope, newArgs){
12670         if(id && delay != d){
12671             this.cancel();
12672         }
12673         d = delay;
12674         t = new Date().getTime();
12675         fn = newFn || fn;
12676         scope = newScope || scope;
12677         args = newArgs || args;
12678         if(!id){
12679             id = setInterval(call, d);
12680         }
12681     };
12682
12683     /**
12684      * Cancel the last queued timeout
12685      */
12686     this.cancel = function(){
12687         if(id){
12688             clearInterval(id);
12689             id = null;
12690         }
12691     };
12692 };/*
12693  * Based on:
12694  * Ext JS Library 1.1.1
12695  * Copyright(c) 2006-2007, Ext JS, LLC.
12696  *
12697  * Originally Released Under LGPL - original licence link has changed is not relivant.
12698  *
12699  * Fork - LGPL
12700  * <script type="text/javascript">
12701  */
12702  
12703  
12704 Roo.util.TaskRunner = function(interval){
12705     interval = interval || 10;
12706     var tasks = [], removeQueue = [];
12707     var id = 0;
12708     var running = false;
12709
12710     var stopThread = function(){
12711         running = false;
12712         clearInterval(id);
12713         id = 0;
12714     };
12715
12716     var startThread = function(){
12717         if(!running){
12718             running = true;
12719             id = setInterval(runTasks, interval);
12720         }
12721     };
12722
12723     var removeTask = function(task){
12724         removeQueue.push(task);
12725         if(task.onStop){
12726             task.onStop();
12727         }
12728     };
12729
12730     var runTasks = function(){
12731         if(removeQueue.length > 0){
12732             for(var i = 0, len = removeQueue.length; i < len; i++){
12733                 tasks.remove(removeQueue[i]);
12734             }
12735             removeQueue = [];
12736             if(tasks.length < 1){
12737                 stopThread();
12738                 return;
12739             }
12740         }
12741         var now = new Date().getTime();
12742         for(var i = 0, len = tasks.length; i < len; ++i){
12743             var t = tasks[i];
12744             var itime = now - t.taskRunTime;
12745             if(t.interval <= itime){
12746                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12747                 t.taskRunTime = now;
12748                 if(rt === false || t.taskRunCount === t.repeat){
12749                     removeTask(t);
12750                     return;
12751                 }
12752             }
12753             if(t.duration && t.duration <= (now - t.taskStartTime)){
12754                 removeTask(t);
12755             }
12756         }
12757     };
12758
12759     /**
12760      * Queues a new task.
12761      * @param {Object} task
12762      */
12763     this.start = function(task){
12764         tasks.push(task);
12765         task.taskStartTime = new Date().getTime();
12766         task.taskRunTime = 0;
12767         task.taskRunCount = 0;
12768         startThread();
12769         return task;
12770     };
12771
12772     this.stop = function(task){
12773         removeTask(task);
12774         return task;
12775     };
12776
12777     this.stopAll = function(){
12778         stopThread();
12779         for(var i = 0, len = tasks.length; i < len; i++){
12780             if(tasks[i].onStop){
12781                 tasks[i].onStop();
12782             }
12783         }
12784         tasks = [];
12785         removeQueue = [];
12786     };
12787 };
12788
12789 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12790  * Based on:
12791  * Ext JS Library 1.1.1
12792  * Copyright(c) 2006-2007, Ext JS, LLC.
12793  *
12794  * Originally Released Under LGPL - original licence link has changed is not relivant.
12795  *
12796  * Fork - LGPL
12797  * <script type="text/javascript">
12798  */
12799
12800  
12801 /**
12802  * @class Roo.util.MixedCollection
12803  * @extends Roo.util.Observable
12804  * A Collection class that maintains both numeric indexes and keys and exposes events.
12805  * @constructor
12806  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12807  * collection (defaults to false)
12808  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12809  * and return the key value for that item.  This is used when available to look up the key on items that
12810  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12811  * equivalent to providing an implementation for the {@link #getKey} method.
12812  */
12813 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12814     this.items = [];
12815     this.map = {};
12816     this.keys = [];
12817     this.length = 0;
12818     this.addEvents({
12819         /**
12820          * @event clear
12821          * Fires when the collection is cleared.
12822          */
12823         "clear" : true,
12824         /**
12825          * @event add
12826          * Fires when an item is added to the collection.
12827          * @param {Number} index The index at which the item was added.
12828          * @param {Object} o The item added.
12829          * @param {String} key The key associated with the added item.
12830          */
12831         "add" : true,
12832         /**
12833          * @event replace
12834          * Fires when an item is replaced in the collection.
12835          * @param {String} key he key associated with the new added.
12836          * @param {Object} old The item being replaced.
12837          * @param {Object} new The new item.
12838          */
12839         "replace" : true,
12840         /**
12841          * @event remove
12842          * Fires when an item is removed from the collection.
12843          * @param {Object} o The item being removed.
12844          * @param {String} key (optional) The key associated with the removed item.
12845          */
12846         "remove" : true,
12847         "sort" : true
12848     });
12849     this.allowFunctions = allowFunctions === true;
12850     if(keyFn){
12851         this.getKey = keyFn;
12852     }
12853     Roo.util.MixedCollection.superclass.constructor.call(this);
12854 };
12855
12856 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12857     allowFunctions : false,
12858     
12859 /**
12860  * Adds an item to the collection.
12861  * @param {String} key The key to associate with the item
12862  * @param {Object} o The item to add.
12863  * @return {Object} The item added.
12864  */
12865     add : function(key, o){
12866         if(arguments.length == 1){
12867             o = arguments[0];
12868             key = this.getKey(o);
12869         }
12870         if(typeof key == "undefined" || key === null){
12871             this.length++;
12872             this.items.push(o);
12873             this.keys.push(null);
12874         }else{
12875             var old = this.map[key];
12876             if(old){
12877                 return this.replace(key, o);
12878             }
12879             this.length++;
12880             this.items.push(o);
12881             this.map[key] = o;
12882             this.keys.push(key);
12883         }
12884         this.fireEvent("add", this.length-1, o, key);
12885         return o;
12886     },
12887        
12888 /**
12889   * MixedCollection has a generic way to fetch keys if you implement getKey.
12890 <pre><code>
12891 // normal way
12892 var mc = new Roo.util.MixedCollection();
12893 mc.add(someEl.dom.id, someEl);
12894 mc.add(otherEl.dom.id, otherEl);
12895 //and so on
12896
12897 // using getKey
12898 var mc = new Roo.util.MixedCollection();
12899 mc.getKey = function(el){
12900    return el.dom.id;
12901 };
12902 mc.add(someEl);
12903 mc.add(otherEl);
12904
12905 // or via the constructor
12906 var mc = new Roo.util.MixedCollection(false, function(el){
12907    return el.dom.id;
12908 });
12909 mc.add(someEl);
12910 mc.add(otherEl);
12911 </code></pre>
12912  * @param o {Object} The item for which to find the key.
12913  * @return {Object} The key for the passed item.
12914  */
12915     getKey : function(o){
12916          return o.id; 
12917     },
12918    
12919 /**
12920  * Replaces an item in the collection.
12921  * @param {String} key The key associated with the item to replace, or the item to replace.
12922  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12923  * @return {Object}  The new item.
12924  */
12925     replace : function(key, o){
12926         if(arguments.length == 1){
12927             o = arguments[0];
12928             key = this.getKey(o);
12929         }
12930         var old = this.item(key);
12931         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12932              return this.add(key, o);
12933         }
12934         var index = this.indexOfKey(key);
12935         this.items[index] = o;
12936         this.map[key] = o;
12937         this.fireEvent("replace", key, old, o);
12938         return o;
12939     },
12940    
12941 /**
12942  * Adds all elements of an Array or an Object to the collection.
12943  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12944  * an Array of values, each of which are added to the collection.
12945  */
12946     addAll : function(objs){
12947         if(arguments.length > 1 || objs instanceof Array){
12948             var args = arguments.length > 1 ? arguments : objs;
12949             for(var i = 0, len = args.length; i < len; i++){
12950                 this.add(args[i]);
12951             }
12952         }else{
12953             for(var key in objs){
12954                 if(this.allowFunctions || typeof objs[key] != "function"){
12955                     this.add(key, objs[key]);
12956                 }
12957             }
12958         }
12959     },
12960    
12961 /**
12962  * Executes the specified function once for every item in the collection, passing each
12963  * item as the first and only parameter. returning false from the function will stop the iteration.
12964  * @param {Function} fn The function to execute for each item.
12965  * @param {Object} scope (optional) The scope in which to execute the function.
12966  */
12967     each : function(fn, scope){
12968         var items = [].concat(this.items); // each safe for removal
12969         for(var i = 0, len = items.length; i < len; i++){
12970             if(fn.call(scope || items[i], items[i], i, len) === false){
12971                 break;
12972             }
12973         }
12974     },
12975    
12976 /**
12977  * Executes the specified function once for every key in the collection, passing each
12978  * key, and its associated item as the first two parameters.
12979  * @param {Function} fn The function to execute for each item.
12980  * @param {Object} scope (optional) The scope in which to execute the function.
12981  */
12982     eachKey : function(fn, scope){
12983         for(var i = 0, len = this.keys.length; i < len; i++){
12984             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12985         }
12986     },
12987    
12988 /**
12989  * Returns the first item in the collection which elicits a true return value from the
12990  * passed selection function.
12991  * @param {Function} fn The selection function to execute for each item.
12992  * @param {Object} scope (optional) The scope in which to execute the function.
12993  * @return {Object} The first item in the collection which returned true from the selection function.
12994  */
12995     find : function(fn, scope){
12996         for(var i = 0, len = this.items.length; i < len; i++){
12997             if(fn.call(scope || window, this.items[i], this.keys[i])){
12998                 return this.items[i];
12999             }
13000         }
13001         return null;
13002     },
13003    
13004 /**
13005  * Inserts an item at the specified index in the collection.
13006  * @param {Number} index The index to insert the item at.
13007  * @param {String} key The key to associate with the new item, or the item itself.
13008  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13009  * @return {Object} The item inserted.
13010  */
13011     insert : function(index, key, o){
13012         if(arguments.length == 2){
13013             o = arguments[1];
13014             key = this.getKey(o);
13015         }
13016         if(index >= this.length){
13017             return this.add(key, o);
13018         }
13019         this.length++;
13020         this.items.splice(index, 0, o);
13021         if(typeof key != "undefined" && key != null){
13022             this.map[key] = o;
13023         }
13024         this.keys.splice(index, 0, key);
13025         this.fireEvent("add", index, o, key);
13026         return o;
13027     },
13028    
13029 /**
13030  * Removed an item from the collection.
13031  * @param {Object} o The item to remove.
13032  * @return {Object} The item removed.
13033  */
13034     remove : function(o){
13035         return this.removeAt(this.indexOf(o));
13036     },
13037    
13038 /**
13039  * Remove an item from a specified index in the collection.
13040  * @param {Number} index The index within the collection of the item to remove.
13041  */
13042     removeAt : function(index){
13043         if(index < this.length && index >= 0){
13044             this.length--;
13045             var o = this.items[index];
13046             this.items.splice(index, 1);
13047             var key = this.keys[index];
13048             if(typeof key != "undefined"){
13049                 delete this.map[key];
13050             }
13051             this.keys.splice(index, 1);
13052             this.fireEvent("remove", o, key);
13053         }
13054     },
13055    
13056 /**
13057  * Removed an item associated with the passed key fom the collection.
13058  * @param {String} key The key of the item to remove.
13059  */
13060     removeKey : function(key){
13061         return this.removeAt(this.indexOfKey(key));
13062     },
13063    
13064 /**
13065  * Returns the number of items in the collection.
13066  * @return {Number} the number of items in the collection.
13067  */
13068     getCount : function(){
13069         return this.length; 
13070     },
13071    
13072 /**
13073  * Returns index within the collection of the passed Object.
13074  * @param {Object} o The item to find the index of.
13075  * @return {Number} index of the item.
13076  */
13077     indexOf : function(o){
13078         if(!this.items.indexOf){
13079             for(var i = 0, len = this.items.length; i < len; i++){
13080                 if(this.items[i] == o) return i;
13081             }
13082             return -1;
13083         }else{
13084             return this.items.indexOf(o);
13085         }
13086     },
13087    
13088 /**
13089  * Returns index within the collection of the passed key.
13090  * @param {String} key The key to find the index of.
13091  * @return {Number} index of the key.
13092  */
13093     indexOfKey : function(key){
13094         if(!this.keys.indexOf){
13095             for(var i = 0, len = this.keys.length; i < len; i++){
13096                 if(this.keys[i] == key) return i;
13097             }
13098             return -1;
13099         }else{
13100             return this.keys.indexOf(key);
13101         }
13102     },
13103    
13104 /**
13105  * Returns the item associated with the passed key OR index. Key has priority over index.
13106  * @param {String/Number} key The key or index of the item.
13107  * @return {Object} The item associated with the passed key.
13108  */
13109     item : function(key){
13110         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13111         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13112     },
13113     
13114 /**
13115  * Returns the item at the specified index.
13116  * @param {Number} index The index of the item.
13117  * @return {Object}
13118  */
13119     itemAt : function(index){
13120         return this.items[index];
13121     },
13122     
13123 /**
13124  * Returns the item associated with the passed key.
13125  * @param {String/Number} key The key of the item.
13126  * @return {Object} The item associated with the passed key.
13127  */
13128     key : function(key){
13129         return this.map[key];
13130     },
13131    
13132 /**
13133  * Returns true if the collection contains the passed Object as an item.
13134  * @param {Object} o  The Object to look for in the collection.
13135  * @return {Boolean} True if the collection contains the Object as an item.
13136  */
13137     contains : function(o){
13138         return this.indexOf(o) != -1;
13139     },
13140    
13141 /**
13142  * Returns true if the collection contains the passed Object as a key.
13143  * @param {String} key The key to look for in the collection.
13144  * @return {Boolean} True if the collection contains the Object as a key.
13145  */
13146     containsKey : function(key){
13147         return typeof this.map[key] != "undefined";
13148     },
13149    
13150 /**
13151  * Removes all items from the collection.
13152  */
13153     clear : function(){
13154         this.length = 0;
13155         this.items = [];
13156         this.keys = [];
13157         this.map = {};
13158         this.fireEvent("clear");
13159     },
13160    
13161 /**
13162  * Returns the first item in the collection.
13163  * @return {Object} the first item in the collection..
13164  */
13165     first : function(){
13166         return this.items[0]; 
13167     },
13168    
13169 /**
13170  * Returns the last item in the collection.
13171  * @return {Object} the last item in the collection..
13172  */
13173     last : function(){
13174         return this.items[this.length-1];   
13175     },
13176     
13177     _sort : function(property, dir, fn){
13178         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13179         fn = fn || function(a, b){
13180             return a-b;
13181         };
13182         var c = [], k = this.keys, items = this.items;
13183         for(var i = 0, len = items.length; i < len; i++){
13184             c[c.length] = {key: k[i], value: items[i], index: i};
13185         }
13186         c.sort(function(a, b){
13187             var v = fn(a[property], b[property]) * dsc;
13188             if(v == 0){
13189                 v = (a.index < b.index ? -1 : 1);
13190             }
13191             return v;
13192         });
13193         for(var i = 0, len = c.length; i < len; i++){
13194             items[i] = c[i].value;
13195             k[i] = c[i].key;
13196         }
13197         this.fireEvent("sort", this);
13198     },
13199     
13200     /**
13201      * Sorts this collection with the passed comparison function
13202      * @param {String} direction (optional) "ASC" or "DESC"
13203      * @param {Function} fn (optional) comparison function
13204      */
13205     sort : function(dir, fn){
13206         this._sort("value", dir, fn);
13207     },
13208     
13209     /**
13210      * Sorts this collection by keys
13211      * @param {String} direction (optional) "ASC" or "DESC"
13212      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13213      */
13214     keySort : function(dir, fn){
13215         this._sort("key", dir, fn || function(a, b){
13216             return String(a).toUpperCase()-String(b).toUpperCase();
13217         });
13218     },
13219     
13220     /**
13221      * Returns a range of items in this collection
13222      * @param {Number} startIndex (optional) defaults to 0
13223      * @param {Number} endIndex (optional) default to the last item
13224      * @return {Array} An array of items
13225      */
13226     getRange : function(start, end){
13227         var items = this.items;
13228         if(items.length < 1){
13229             return [];
13230         }
13231         start = start || 0;
13232         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13233         var r = [];
13234         if(start <= end){
13235             for(var i = start; i <= end; i++) {
13236                     r[r.length] = items[i];
13237             }
13238         }else{
13239             for(var i = start; i >= end; i--) {
13240                     r[r.length] = items[i];
13241             }
13242         }
13243         return r;
13244     },
13245         
13246     /**
13247      * Filter the <i>objects</i> in this collection by a specific property. 
13248      * Returns a new collection that has been filtered.
13249      * @param {String} property A property on your objects
13250      * @param {String/RegExp} value Either string that the property values 
13251      * should start with or a RegExp to test against the property
13252      * @return {MixedCollection} The new filtered collection
13253      */
13254     filter : function(property, value){
13255         if(!value.exec){ // not a regex
13256             value = String(value);
13257             if(value.length == 0){
13258                 return this.clone();
13259             }
13260             value = new RegExp("^" + Roo.escapeRe(value), "i");
13261         }
13262         return this.filterBy(function(o){
13263             return o && value.test(o[property]);
13264         });
13265         },
13266     
13267     /**
13268      * Filter by a function. * Returns a new collection that has been filtered.
13269      * The passed function will be called with each 
13270      * object in the collection. If the function returns true, the value is included 
13271      * otherwise it is filtered.
13272      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13273      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13274      * @return {MixedCollection} The new filtered collection
13275      */
13276     filterBy : function(fn, scope){
13277         var r = new Roo.util.MixedCollection();
13278         r.getKey = this.getKey;
13279         var k = this.keys, it = this.items;
13280         for(var i = 0, len = it.length; i < len; i++){
13281             if(fn.call(scope||this, it[i], k[i])){
13282                                 r.add(k[i], it[i]);
13283                         }
13284         }
13285         return r;
13286     },
13287     
13288     /**
13289      * Creates a duplicate of this collection
13290      * @return {MixedCollection}
13291      */
13292     clone : function(){
13293         var r = new Roo.util.MixedCollection();
13294         var k = this.keys, it = this.items;
13295         for(var i = 0, len = it.length; i < len; i++){
13296             r.add(k[i], it[i]);
13297         }
13298         r.getKey = this.getKey;
13299         return r;
13300     }
13301 });
13302 /**
13303  * Returns the item associated with the passed key or index.
13304  * @method
13305  * @param {String/Number} key The key or index of the item.
13306  * @return {Object} The item associated with the passed key.
13307  */
13308 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13309  * Based on:
13310  * Ext JS Library 1.1.1
13311  * Copyright(c) 2006-2007, Ext JS, LLC.
13312  *
13313  * Originally Released Under LGPL - original licence link has changed is not relivant.
13314  *
13315  * Fork - LGPL
13316  * <script type="text/javascript">
13317  */
13318 /**
13319  * @class Roo.util.JSON
13320  * Modified version of Douglas Crockford"s json.js that doesn"t
13321  * mess with the Object prototype 
13322  * http://www.json.org/js.html
13323  * @singleton
13324  */
13325 Roo.util.JSON = new (function(){
13326     var useHasOwn = {}.hasOwnProperty ? true : false;
13327     
13328     // crashes Safari in some instances
13329     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13330     
13331     var pad = function(n) {
13332         return n < 10 ? "0" + n : n;
13333     };
13334     
13335     var m = {
13336         "\b": '\\b',
13337         "\t": '\\t',
13338         "\n": '\\n',
13339         "\f": '\\f',
13340         "\r": '\\r',
13341         '"' : '\\"',
13342         "\\": '\\\\'
13343     };
13344
13345     var encodeString = function(s){
13346         if (/["\\\x00-\x1f]/.test(s)) {
13347             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13348                 var c = m[b];
13349                 if(c){
13350                     return c;
13351                 }
13352                 c = b.charCodeAt();
13353                 return "\\u00" +
13354                     Math.floor(c / 16).toString(16) +
13355                     (c % 16).toString(16);
13356             }) + '"';
13357         }
13358         return '"' + s + '"';
13359     };
13360     
13361     var encodeArray = function(o){
13362         var a = ["["], b, i, l = o.length, v;
13363             for (i = 0; i < l; i += 1) {
13364                 v = o[i];
13365                 switch (typeof v) {
13366                     case "undefined":
13367                     case "function":
13368                     case "unknown":
13369                         break;
13370                     default:
13371                         if (b) {
13372                             a.push(',');
13373                         }
13374                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13375                         b = true;
13376                 }
13377             }
13378             a.push("]");
13379             return a.join("");
13380     };
13381     
13382     var encodeDate = function(o){
13383         return '"' + o.getFullYear() + "-" +
13384                 pad(o.getMonth() + 1) + "-" +
13385                 pad(o.getDate()) + "T" +
13386                 pad(o.getHours()) + ":" +
13387                 pad(o.getMinutes()) + ":" +
13388                 pad(o.getSeconds()) + '"';
13389     };
13390     
13391     /**
13392      * Encodes an Object, Array or other value
13393      * @param {Mixed} o The variable to encode
13394      * @return {String} The JSON string
13395      */
13396     this.encode = function(o)
13397     {
13398         // should this be extended to fully wrap stringify..
13399         
13400         if(typeof o == "undefined" || o === null){
13401             return "null";
13402         }else if(o instanceof Array){
13403             return encodeArray(o);
13404         }else if(o instanceof Date){
13405             return encodeDate(o);
13406         }else if(typeof o == "string"){
13407             return encodeString(o);
13408         }else if(typeof o == "number"){
13409             return isFinite(o) ? String(o) : "null";
13410         }else if(typeof o == "boolean"){
13411             return String(o);
13412         }else {
13413             var a = ["{"], b, i, v;
13414             for (i in o) {
13415                 if(!useHasOwn || o.hasOwnProperty(i)) {
13416                     v = o[i];
13417                     switch (typeof v) {
13418                     case "undefined":
13419                     case "function":
13420                     case "unknown":
13421                         break;
13422                     default:
13423                         if(b){
13424                             a.push(',');
13425                         }
13426                         a.push(this.encode(i), ":",
13427                                 v === null ? "null" : this.encode(v));
13428                         b = true;
13429                     }
13430                 }
13431             }
13432             a.push("}");
13433             return a.join("");
13434         }
13435     };
13436     
13437     /**
13438      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13439      * @param {String} json The JSON string
13440      * @return {Object} The resulting object
13441      */
13442     this.decode = function(json){
13443         
13444         return  /** eval:var:json */ eval("(" + json + ')');
13445     };
13446 })();
13447 /** 
13448  * Shorthand for {@link Roo.util.JSON#encode}
13449  * @member Roo encode 
13450  * @method */
13451 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13452 /** 
13453  * Shorthand for {@link Roo.util.JSON#decode}
13454  * @member Roo decode 
13455  * @method */
13456 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13457 /*
13458  * Based on:
13459  * Ext JS Library 1.1.1
13460  * Copyright(c) 2006-2007, Ext JS, LLC.
13461  *
13462  * Originally Released Under LGPL - original licence link has changed is not relivant.
13463  *
13464  * Fork - LGPL
13465  * <script type="text/javascript">
13466  */
13467  
13468 /**
13469  * @class Roo.util.Format
13470  * Reusable data formatting functions
13471  * @singleton
13472  */
13473 Roo.util.Format = function(){
13474     var trimRe = /^\s+|\s+$/g;
13475     return {
13476         /**
13477          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13478          * @param {String} value The string to truncate
13479          * @param {Number} length The maximum length to allow before truncating
13480          * @return {String} The converted text
13481          */
13482         ellipsis : function(value, len){
13483             if(value && value.length > len){
13484                 return value.substr(0, len-3)+"...";
13485             }
13486             return value;
13487         },
13488
13489         /**
13490          * Checks a reference and converts it to empty string if it is undefined
13491          * @param {Mixed} value Reference to check
13492          * @return {Mixed} Empty string if converted, otherwise the original value
13493          */
13494         undef : function(value){
13495             return typeof value != "undefined" ? value : "";
13496         },
13497
13498         /**
13499          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13500          * @param {String} value The string to encode
13501          * @return {String} The encoded text
13502          */
13503         htmlEncode : function(value){
13504             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13505         },
13506
13507         /**
13508          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13509          * @param {String} value The string to decode
13510          * @return {String} The decoded text
13511          */
13512         htmlDecode : function(value){
13513             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13514         },
13515
13516         /**
13517          * Trims any whitespace from either side of a string
13518          * @param {String} value The text to trim
13519          * @return {String} The trimmed text
13520          */
13521         trim : function(value){
13522             return String(value).replace(trimRe, "");
13523         },
13524
13525         /**
13526          * Returns a substring from within an original string
13527          * @param {String} value The original text
13528          * @param {Number} start The start index of the substring
13529          * @param {Number} length The length of the substring
13530          * @return {String} The substring
13531          */
13532         substr : function(value, start, length){
13533             return String(value).substr(start, length);
13534         },
13535
13536         /**
13537          * Converts a string to all lower case letters
13538          * @param {String} value The text to convert
13539          * @return {String} The converted text
13540          */
13541         lowercase : function(value){
13542             return String(value).toLowerCase();
13543         },
13544
13545         /**
13546          * Converts a string to all upper case letters
13547          * @param {String} value The text to convert
13548          * @return {String} The converted text
13549          */
13550         uppercase : function(value){
13551             return String(value).toUpperCase();
13552         },
13553
13554         /**
13555          * Converts the first character only of a string to upper case
13556          * @param {String} value The text to convert
13557          * @return {String} The converted text
13558          */
13559         capitalize : function(value){
13560             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13561         },
13562
13563         // private
13564         call : function(value, fn){
13565             if(arguments.length > 2){
13566                 var args = Array.prototype.slice.call(arguments, 2);
13567                 args.unshift(value);
13568                  
13569                 return /** eval:var:value */  eval(fn).apply(window, args);
13570             }else{
13571                 /** eval:var:value */
13572                 return /** eval:var:value */ eval(fn).call(window, value);
13573             }
13574         },
13575
13576        
13577         /**
13578          * safer version of Math.toFixed..??/
13579          * @param {Number/String} value The numeric value to format
13580          * @param {Number/String} value Decimal places 
13581          * @return {String} The formatted currency string
13582          */
13583         toFixed : function(v, n)
13584         {
13585             // why not use to fixed - precision is buggered???
13586             if (!n) {
13587                 return Math.round(v-0);
13588             }
13589             var fact = Math.pow(10,n+1);
13590             v = (Math.round((v-0)*fact))/fact;
13591             var z = (''+fact).substring(2);
13592             if (v == Math.floor(v)) {
13593                 return Math.floor(v) + '.' + z;
13594             }
13595             
13596             // now just padd decimals..
13597             var ps = String(v).split('.');
13598             var fd = (ps[1] + z);
13599             var r = fd.substring(0,n); 
13600             var rm = fd.substring(n); 
13601             if (rm < 5) {
13602                 return ps[0] + '.' + r;
13603             }
13604             r*=1; // turn it into a number;
13605             r++;
13606             if (String(r).length != n) {
13607                 ps[0]*=1;
13608                 ps[0]++;
13609                 r = String(r).substring(1); // chop the end off.
13610             }
13611             
13612             return ps[0] + '.' + r;
13613              
13614         },
13615         
13616         /**
13617          * Format a number as US currency
13618          * @param {Number/String} value The numeric value to format
13619          * @return {String} The formatted currency string
13620          */
13621         usMoney : function(v){
13622             return '$' + Roo.util.Format.number(v);
13623         },
13624         
13625         /**
13626          * Format a number
13627          * eventually this should probably emulate php's number_format
13628          * @param {Number/String} value The numeric value to format
13629          * @param {Number} decimals number of decimal places
13630          * @return {String} The formatted currency string
13631          */
13632         number : function(v,decimals)
13633         {
13634             // multiply and round.
13635             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13636             var mul = Math.pow(10, decimals);
13637             var zero = String(mul).substring(1);
13638             v = (Math.round((v-0)*mul))/mul;
13639             
13640             // if it's '0' number.. then
13641             
13642             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13643             v = String(v);
13644             var ps = v.split('.');
13645             var whole = ps[0];
13646             
13647             
13648             var r = /(\d+)(\d{3})/;
13649             // add comma's
13650             while (r.test(whole)) {
13651                 whole = whole.replace(r, '$1' + ',' + '$2');
13652             }
13653             
13654             
13655             var sub = ps[1] ?
13656                     // has decimals..
13657                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13658                     // does not have decimals
13659                     (decimals ? ('.' + zero) : '');
13660             
13661             
13662             return whole + sub ;
13663         },
13664         
13665         /**
13666          * Parse a value into a formatted date using the specified format pattern.
13667          * @param {Mixed} value The value to format
13668          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13669          * @return {String} The formatted date string
13670          */
13671         date : function(v, format){
13672             if(!v){
13673                 return "";
13674             }
13675             if(!(v instanceof Date)){
13676                 v = new Date(Date.parse(v));
13677             }
13678             return v.dateFormat(format || Roo.util.Format.defaults.date);
13679         },
13680
13681         /**
13682          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13683          * @param {String} format Any valid date format string
13684          * @return {Function} The date formatting function
13685          */
13686         dateRenderer : function(format){
13687             return function(v){
13688                 return Roo.util.Format.date(v, format);  
13689             };
13690         },
13691
13692         // private
13693         stripTagsRE : /<\/?[^>]+>/gi,
13694         
13695         /**
13696          * Strips all HTML tags
13697          * @param {Mixed} value The text from which to strip tags
13698          * @return {String} The stripped text
13699          */
13700         stripTags : function(v){
13701             return !v ? v : String(v).replace(this.stripTagsRE, "");
13702         }
13703     };
13704 }();
13705 Roo.util.Format.defaults = {
13706     date : 'd/M/Y'
13707 };/*
13708  * Based on:
13709  * Ext JS Library 1.1.1
13710  * Copyright(c) 2006-2007, Ext JS, LLC.
13711  *
13712  * Originally Released Under LGPL - original licence link has changed is not relivant.
13713  *
13714  * Fork - LGPL
13715  * <script type="text/javascript">
13716  */
13717
13718
13719  
13720
13721 /**
13722  * @class Roo.MasterTemplate
13723  * @extends Roo.Template
13724  * Provides a template that can have child templates. The syntax is:
13725 <pre><code>
13726 var t = new Roo.MasterTemplate(
13727         '&lt;select name="{name}"&gt;',
13728                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13729         '&lt;/select&gt;'
13730 );
13731 t.add('options', {value: 'foo', text: 'bar'});
13732 // or you can add multiple child elements in one shot
13733 t.addAll('options', [
13734     {value: 'foo', text: 'bar'},
13735     {value: 'foo2', text: 'bar2'},
13736     {value: 'foo3', text: 'bar3'}
13737 ]);
13738 // then append, applying the master template values
13739 t.append('my-form', {name: 'my-select'});
13740 </code></pre>
13741 * A name attribute for the child template is not required if you have only one child
13742 * template or you want to refer to them by index.
13743  */
13744 Roo.MasterTemplate = function(){
13745     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13746     this.originalHtml = this.html;
13747     var st = {};
13748     var m, re = this.subTemplateRe;
13749     re.lastIndex = 0;
13750     var subIndex = 0;
13751     while(m = re.exec(this.html)){
13752         var name = m[1], content = m[2];
13753         st[subIndex] = {
13754             name: name,
13755             index: subIndex,
13756             buffer: [],
13757             tpl : new Roo.Template(content)
13758         };
13759         if(name){
13760             st[name] = st[subIndex];
13761         }
13762         st[subIndex].tpl.compile();
13763         st[subIndex].tpl.call = this.call.createDelegate(this);
13764         subIndex++;
13765     }
13766     this.subCount = subIndex;
13767     this.subs = st;
13768 };
13769 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13770     /**
13771     * The regular expression used to match sub templates
13772     * @type RegExp
13773     * @property
13774     */
13775     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13776
13777     /**
13778      * Applies the passed values to a child template.
13779      * @param {String/Number} name (optional) The name or index of the child template
13780      * @param {Array/Object} values The values to be applied to the template
13781      * @return {MasterTemplate} this
13782      */
13783      add : function(name, values){
13784         if(arguments.length == 1){
13785             values = arguments[0];
13786             name = 0;
13787         }
13788         var s = this.subs[name];
13789         s.buffer[s.buffer.length] = s.tpl.apply(values);
13790         return this;
13791     },
13792
13793     /**
13794      * Applies all the passed values to a child template.
13795      * @param {String/Number} name (optional) The name or index of the child template
13796      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13797      * @param {Boolean} reset (optional) True to reset the template first
13798      * @return {MasterTemplate} this
13799      */
13800     fill : function(name, values, reset){
13801         var a = arguments;
13802         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13803             values = a[0];
13804             name = 0;
13805             reset = a[1];
13806         }
13807         if(reset){
13808             this.reset();
13809         }
13810         for(var i = 0, len = values.length; i < len; i++){
13811             this.add(name, values[i]);
13812         }
13813         return this;
13814     },
13815
13816     /**
13817      * Resets the template for reuse
13818      * @return {MasterTemplate} this
13819      */
13820      reset : function(){
13821         var s = this.subs;
13822         for(var i = 0; i < this.subCount; i++){
13823             s[i].buffer = [];
13824         }
13825         return this;
13826     },
13827
13828     applyTemplate : function(values){
13829         var s = this.subs;
13830         var replaceIndex = -1;
13831         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13832             return s[++replaceIndex].buffer.join("");
13833         });
13834         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13835     },
13836
13837     apply : function(){
13838         return this.applyTemplate.apply(this, arguments);
13839     },
13840
13841     compile : function(){return this;}
13842 });
13843
13844 /**
13845  * Alias for fill().
13846  * @method
13847  */
13848 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13849  /**
13850  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13851  * var tpl = Roo.MasterTemplate.from('element-id');
13852  * @param {String/HTMLElement} el
13853  * @param {Object} config
13854  * @static
13855  */
13856 Roo.MasterTemplate.from = function(el, config){
13857     el = Roo.getDom(el);
13858     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13859 };/*
13860  * Based on:
13861  * Ext JS Library 1.1.1
13862  * Copyright(c) 2006-2007, Ext JS, LLC.
13863  *
13864  * Originally Released Under LGPL - original licence link has changed is not relivant.
13865  *
13866  * Fork - LGPL
13867  * <script type="text/javascript">
13868  */
13869
13870  
13871 /**
13872  * @class Roo.util.CSS
13873  * Utility class for manipulating CSS rules
13874  * @singleton
13875  */
13876 Roo.util.CSS = function(){
13877         var rules = null;
13878         var doc = document;
13879
13880     var camelRe = /(-[a-z])/gi;
13881     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13882
13883    return {
13884    /**
13885     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13886     * tag and appended to the HEAD of the document.
13887     * @param {String|Object} cssText The text containing the css rules
13888     * @param {String} id An id to add to the stylesheet for later removal
13889     * @return {StyleSheet}
13890     */
13891     createStyleSheet : function(cssText, id){
13892         var ss;
13893         var head = doc.getElementsByTagName("head")[0];
13894         var nrules = doc.createElement("style");
13895         nrules.setAttribute("type", "text/css");
13896         if(id){
13897             nrules.setAttribute("id", id);
13898         }
13899         if (typeof(cssText) != 'string') {
13900             // support object maps..
13901             // not sure if this a good idea.. 
13902             // perhaps it should be merged with the general css handling
13903             // and handle js style props.
13904             var cssTextNew = [];
13905             for(var n in cssText) {
13906                 var citems = [];
13907                 for(var k in cssText[n]) {
13908                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13909                 }
13910                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13911                 
13912             }
13913             cssText = cssTextNew.join("\n");
13914             
13915         }
13916        
13917        
13918        if(Roo.isIE){
13919            head.appendChild(nrules);
13920            ss = nrules.styleSheet;
13921            ss.cssText = cssText;
13922        }else{
13923            try{
13924                 nrules.appendChild(doc.createTextNode(cssText));
13925            }catch(e){
13926                nrules.cssText = cssText; 
13927            }
13928            head.appendChild(nrules);
13929            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13930        }
13931        this.cacheStyleSheet(ss);
13932        return ss;
13933    },
13934
13935    /**
13936     * Removes a style or link tag by id
13937     * @param {String} id The id of the tag
13938     */
13939    removeStyleSheet : function(id){
13940        var existing = doc.getElementById(id);
13941        if(existing){
13942            existing.parentNode.removeChild(existing);
13943        }
13944    },
13945
13946    /**
13947     * Dynamically swaps an existing stylesheet reference for a new one
13948     * @param {String} id The id of an existing link tag to remove
13949     * @param {String} url The href of the new stylesheet to include
13950     */
13951    swapStyleSheet : function(id, url){
13952        this.removeStyleSheet(id);
13953        var ss = doc.createElement("link");
13954        ss.setAttribute("rel", "stylesheet");
13955        ss.setAttribute("type", "text/css");
13956        ss.setAttribute("id", id);
13957        ss.setAttribute("href", url);
13958        doc.getElementsByTagName("head")[0].appendChild(ss);
13959    },
13960    
13961    /**
13962     * Refresh the rule cache if you have dynamically added stylesheets
13963     * @return {Object} An object (hash) of rules indexed by selector
13964     */
13965    refreshCache : function(){
13966        return this.getRules(true);
13967    },
13968
13969    // private
13970    cacheStyleSheet : function(stylesheet){
13971        if(!rules){
13972            rules = {};
13973        }
13974        try{// try catch for cross domain access issue
13975            var ssRules = stylesheet.cssRules || stylesheet.rules;
13976            for(var j = ssRules.length-1; j >= 0; --j){
13977                rules[ssRules[j].selectorText] = ssRules[j];
13978            }
13979        }catch(e){}
13980    },
13981    
13982    /**
13983     * Gets all css rules for the document
13984     * @param {Boolean} refreshCache true to refresh the internal cache
13985     * @return {Object} An object (hash) of rules indexed by selector
13986     */
13987    getRules : function(refreshCache){
13988                 if(rules == null || refreshCache){
13989                         rules = {};
13990                         var ds = doc.styleSheets;
13991                         for(var i =0, len = ds.length; i < len; i++){
13992                             try{
13993                         this.cacheStyleSheet(ds[i]);
13994                     }catch(e){} 
13995                 }
13996                 }
13997                 return rules;
13998         },
13999         
14000         /**
14001     * Gets an an individual CSS rule by selector(s)
14002     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14003     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14004     * @return {CSSRule} The CSS rule or null if one is not found
14005     */
14006    getRule : function(selector, refreshCache){
14007                 var rs = this.getRules(refreshCache);
14008                 if(!(selector instanceof Array)){
14009                     return rs[selector];
14010                 }
14011                 for(var i = 0; i < selector.length; i++){
14012                         if(rs[selector[i]]){
14013                                 return rs[selector[i]];
14014                         }
14015                 }
14016                 return null;
14017         },
14018         
14019         
14020         /**
14021     * Updates a rule property
14022     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14023     * @param {String} property The css property
14024     * @param {String} value The new value for the property
14025     * @return {Boolean} true If a rule was found and updated
14026     */
14027    updateRule : function(selector, property, value){
14028                 if(!(selector instanceof Array)){
14029                         var rule = this.getRule(selector);
14030                         if(rule){
14031                                 rule.style[property.replace(camelRe, camelFn)] = value;
14032                                 return true;
14033                         }
14034                 }else{
14035                         for(var i = 0; i < selector.length; i++){
14036                                 if(this.updateRule(selector[i], property, value)){
14037                                         return true;
14038                                 }
14039                         }
14040                 }
14041                 return false;
14042         }
14043    };   
14044 }();/*
14045  * Based on:
14046  * Ext JS Library 1.1.1
14047  * Copyright(c) 2006-2007, Ext JS, LLC.
14048  *
14049  * Originally Released Under LGPL - original licence link has changed is not relivant.
14050  *
14051  * Fork - LGPL
14052  * <script type="text/javascript">
14053  */
14054
14055  
14056
14057 /**
14058  * @class Roo.util.ClickRepeater
14059  * @extends Roo.util.Observable
14060  * 
14061  * A wrapper class which can be applied to any element. Fires a "click" event while the
14062  * mouse is pressed. The interval between firings may be specified in the config but
14063  * defaults to 10 milliseconds.
14064  * 
14065  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14066  * 
14067  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14068  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14069  * Similar to an autorepeat key delay.
14070  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14071  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14072  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14073  *           "interval" and "delay" are ignored. "immediate" is honored.
14074  * @cfg {Boolean} preventDefault True to prevent the default click event
14075  * @cfg {Boolean} stopDefault True to stop the default click event
14076  * 
14077  * @history
14078  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14079  *     2007-02-02 jvs Renamed to ClickRepeater
14080  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14081  *
14082  *  @constructor
14083  * @param {String/HTMLElement/Element} el The element to listen on
14084  * @param {Object} config
14085  **/
14086 Roo.util.ClickRepeater = function(el, config)
14087 {
14088     this.el = Roo.get(el);
14089     this.el.unselectable();
14090
14091     Roo.apply(this, config);
14092
14093     this.addEvents({
14094     /**
14095      * @event mousedown
14096      * Fires when the mouse button is depressed.
14097      * @param {Roo.util.ClickRepeater} this
14098      */
14099         "mousedown" : true,
14100     /**
14101      * @event click
14102      * Fires on a specified interval during the time the element is pressed.
14103      * @param {Roo.util.ClickRepeater} this
14104      */
14105         "click" : true,
14106     /**
14107      * @event mouseup
14108      * Fires when the mouse key is released.
14109      * @param {Roo.util.ClickRepeater} this
14110      */
14111         "mouseup" : true
14112     });
14113
14114     this.el.on("mousedown", this.handleMouseDown, this);
14115     if(this.preventDefault || this.stopDefault){
14116         this.el.on("click", function(e){
14117             if(this.preventDefault){
14118                 e.preventDefault();
14119             }
14120             if(this.stopDefault){
14121                 e.stopEvent();
14122             }
14123         }, this);
14124     }
14125
14126     // allow inline handler
14127     if(this.handler){
14128         this.on("click", this.handler,  this.scope || this);
14129     }
14130
14131     Roo.util.ClickRepeater.superclass.constructor.call(this);
14132 };
14133
14134 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14135     interval : 20,
14136     delay: 250,
14137     preventDefault : true,
14138     stopDefault : false,
14139     timer : 0,
14140
14141     // private
14142     handleMouseDown : function(){
14143         clearTimeout(this.timer);
14144         this.el.blur();
14145         if(this.pressClass){
14146             this.el.addClass(this.pressClass);
14147         }
14148         this.mousedownTime = new Date();
14149
14150         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14151         this.el.on("mouseout", this.handleMouseOut, this);
14152
14153         this.fireEvent("mousedown", this);
14154         this.fireEvent("click", this);
14155         
14156         this.timer = this.click.defer(this.delay || this.interval, this);
14157     },
14158
14159     // private
14160     click : function(){
14161         this.fireEvent("click", this);
14162         this.timer = this.click.defer(this.getInterval(), this);
14163     },
14164
14165     // private
14166     getInterval: function(){
14167         if(!this.accelerate){
14168             return this.interval;
14169         }
14170         var pressTime = this.mousedownTime.getElapsed();
14171         if(pressTime < 500){
14172             return 400;
14173         }else if(pressTime < 1700){
14174             return 320;
14175         }else if(pressTime < 2600){
14176             return 250;
14177         }else if(pressTime < 3500){
14178             return 180;
14179         }else if(pressTime < 4400){
14180             return 140;
14181         }else if(pressTime < 5300){
14182             return 80;
14183         }else if(pressTime < 6200){
14184             return 50;
14185         }else{
14186             return 10;
14187         }
14188     },
14189
14190     // private
14191     handleMouseOut : function(){
14192         clearTimeout(this.timer);
14193         if(this.pressClass){
14194             this.el.removeClass(this.pressClass);
14195         }
14196         this.el.on("mouseover", this.handleMouseReturn, this);
14197     },
14198
14199     // private
14200     handleMouseReturn : function(){
14201         this.el.un("mouseover", this.handleMouseReturn);
14202         if(this.pressClass){
14203             this.el.addClass(this.pressClass);
14204         }
14205         this.click();
14206     },
14207
14208     // private
14209     handleMouseUp : function(){
14210         clearTimeout(this.timer);
14211         this.el.un("mouseover", this.handleMouseReturn);
14212         this.el.un("mouseout", this.handleMouseOut);
14213         Roo.get(document).un("mouseup", this.handleMouseUp);
14214         this.el.removeClass(this.pressClass);
14215         this.fireEvent("mouseup", this);
14216     }
14217 });/*
14218  * Based on:
14219  * Ext JS Library 1.1.1
14220  * Copyright(c) 2006-2007, Ext JS, LLC.
14221  *
14222  * Originally Released Under LGPL - original licence link has changed is not relivant.
14223  *
14224  * Fork - LGPL
14225  * <script type="text/javascript">
14226  */
14227
14228  
14229 /**
14230  * @class Roo.KeyNav
14231  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14232  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14233  * way to implement custom navigation schemes for any UI component.</p>
14234  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14235  * pageUp, pageDown, del, home, end.  Usage:</p>
14236  <pre><code>
14237 var nav = new Roo.KeyNav("my-element", {
14238     "left" : function(e){
14239         this.moveLeft(e.ctrlKey);
14240     },
14241     "right" : function(e){
14242         this.moveRight(e.ctrlKey);
14243     },
14244     "enter" : function(e){
14245         this.save();
14246     },
14247     scope : this
14248 });
14249 </code></pre>
14250  * @constructor
14251  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14252  * @param {Object} config The config
14253  */
14254 Roo.KeyNav = function(el, config){
14255     this.el = Roo.get(el);
14256     Roo.apply(this, config);
14257     if(!this.disabled){
14258         this.disabled = true;
14259         this.enable();
14260     }
14261 };
14262
14263 Roo.KeyNav.prototype = {
14264     /**
14265      * @cfg {Boolean} disabled
14266      * True to disable this KeyNav instance (defaults to false)
14267      */
14268     disabled : false,
14269     /**
14270      * @cfg {String} defaultEventAction
14271      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14272      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14273      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14274      */
14275     defaultEventAction: "stopEvent",
14276     /**
14277      * @cfg {Boolean} forceKeyDown
14278      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14279      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14280      * handle keydown instead of keypress.
14281      */
14282     forceKeyDown : false,
14283
14284     // private
14285     prepareEvent : function(e){
14286         var k = e.getKey();
14287         var h = this.keyToHandler[k];
14288         //if(h && this[h]){
14289         //    e.stopPropagation();
14290         //}
14291         if(Roo.isSafari && h && k >= 37 && k <= 40){
14292             e.stopEvent();
14293         }
14294     },
14295
14296     // private
14297     relay : function(e){
14298         var k = e.getKey();
14299         var h = this.keyToHandler[k];
14300         if(h && this[h]){
14301             if(this.doRelay(e, this[h], h) !== true){
14302                 e[this.defaultEventAction]();
14303             }
14304         }
14305     },
14306
14307     // private
14308     doRelay : function(e, h, hname){
14309         return h.call(this.scope || this, e);
14310     },
14311
14312     // possible handlers
14313     enter : false,
14314     left : false,
14315     right : false,
14316     up : false,
14317     down : false,
14318     tab : false,
14319     esc : false,
14320     pageUp : false,
14321     pageDown : false,
14322     del : false,
14323     home : false,
14324     end : false,
14325
14326     // quick lookup hash
14327     keyToHandler : {
14328         37 : "left",
14329         39 : "right",
14330         38 : "up",
14331         40 : "down",
14332         33 : "pageUp",
14333         34 : "pageDown",
14334         46 : "del",
14335         36 : "home",
14336         35 : "end",
14337         13 : "enter",
14338         27 : "esc",
14339         9  : "tab"
14340     },
14341
14342         /**
14343          * Enable this KeyNav
14344          */
14345         enable: function(){
14346                 if(this.disabled){
14347             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14348             // the EventObject will normalize Safari automatically
14349             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14350                 this.el.on("keydown", this.relay,  this);
14351             }else{
14352                 this.el.on("keydown", this.prepareEvent,  this);
14353                 this.el.on("keypress", this.relay,  this);
14354             }
14355                     this.disabled = false;
14356                 }
14357         },
14358
14359         /**
14360          * Disable this KeyNav
14361          */
14362         disable: function(){
14363                 if(!this.disabled){
14364                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14365                 this.el.un("keydown", this.relay);
14366             }else{
14367                 this.el.un("keydown", this.prepareEvent);
14368                 this.el.un("keypress", this.relay);
14369             }
14370                     this.disabled = true;
14371                 }
14372         }
14373 };/*
14374  * Based on:
14375  * Ext JS Library 1.1.1
14376  * Copyright(c) 2006-2007, Ext JS, LLC.
14377  *
14378  * Originally Released Under LGPL - original licence link has changed is not relivant.
14379  *
14380  * Fork - LGPL
14381  * <script type="text/javascript">
14382  */
14383
14384  
14385 /**
14386  * @class Roo.KeyMap
14387  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14388  * The constructor accepts the same config object as defined by {@link #addBinding}.
14389  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14390  * combination it will call the function with this signature (if the match is a multi-key
14391  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14392  * A KeyMap can also handle a string representation of keys.<br />
14393  * Usage:
14394  <pre><code>
14395 // map one key by key code
14396 var map = new Roo.KeyMap("my-element", {
14397     key: 13, // or Roo.EventObject.ENTER
14398     fn: myHandler,
14399     scope: myObject
14400 });
14401
14402 // map multiple keys to one action by string
14403 var map = new Roo.KeyMap("my-element", {
14404     key: "a\r\n\t",
14405     fn: myHandler,
14406     scope: myObject
14407 });
14408
14409 // map multiple keys to multiple actions by strings and array of codes
14410 var map = new Roo.KeyMap("my-element", [
14411     {
14412         key: [10,13],
14413         fn: function(){ alert("Return was pressed"); }
14414     }, {
14415         key: "abc",
14416         fn: function(){ alert('a, b or c was pressed'); }
14417     }, {
14418         key: "\t",
14419         ctrl:true,
14420         shift:true,
14421         fn: function(){ alert('Control + shift + tab was pressed.'); }
14422     }
14423 ]);
14424 </code></pre>
14425  * <b>Note: A KeyMap starts enabled</b>
14426  * @constructor
14427  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14428  * @param {Object} config The config (see {@link #addBinding})
14429  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14430  */
14431 Roo.KeyMap = function(el, config, eventName){
14432     this.el  = Roo.get(el);
14433     this.eventName = eventName || "keydown";
14434     this.bindings = [];
14435     if(config){
14436         this.addBinding(config);
14437     }
14438     this.enable();
14439 };
14440
14441 Roo.KeyMap.prototype = {
14442     /**
14443      * True to stop the event from bubbling and prevent the default browser action if the
14444      * key was handled by the KeyMap (defaults to false)
14445      * @type Boolean
14446      */
14447     stopEvent : false,
14448
14449     /**
14450      * Add a new binding to this KeyMap. The following config object properties are supported:
14451      * <pre>
14452 Property    Type             Description
14453 ----------  ---------------  ----------------------------------------------------------------------
14454 key         String/Array     A single keycode or an array of keycodes to handle
14455 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14456 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14457 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14458 fn          Function         The function to call when KeyMap finds the expected key combination
14459 scope       Object           The scope of the callback function
14460 </pre>
14461      *
14462      * Usage:
14463      * <pre><code>
14464 // Create a KeyMap
14465 var map = new Roo.KeyMap(document, {
14466     key: Roo.EventObject.ENTER,
14467     fn: handleKey,
14468     scope: this
14469 });
14470
14471 //Add a new binding to the existing KeyMap later
14472 map.addBinding({
14473     key: 'abc',
14474     shift: true,
14475     fn: handleKey,
14476     scope: this
14477 });
14478 </code></pre>
14479      * @param {Object/Array} config A single KeyMap config or an array of configs
14480      */
14481         addBinding : function(config){
14482         if(config instanceof Array){
14483             for(var i = 0, len = config.length; i < len; i++){
14484                 this.addBinding(config[i]);
14485             }
14486             return;
14487         }
14488         var keyCode = config.key,
14489             shift = config.shift, 
14490             ctrl = config.ctrl, 
14491             alt = config.alt,
14492             fn = config.fn,
14493             scope = config.scope;
14494         if(typeof keyCode == "string"){
14495             var ks = [];
14496             var keyString = keyCode.toUpperCase();
14497             for(var j = 0, len = keyString.length; j < len; j++){
14498                 ks.push(keyString.charCodeAt(j));
14499             }
14500             keyCode = ks;
14501         }
14502         var keyArray = keyCode instanceof Array;
14503         var handler = function(e){
14504             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14505                 var k = e.getKey();
14506                 if(keyArray){
14507                     for(var i = 0, len = keyCode.length; i < len; i++){
14508                         if(keyCode[i] == k){
14509                           if(this.stopEvent){
14510                               e.stopEvent();
14511                           }
14512                           fn.call(scope || window, k, e);
14513                           return;
14514                         }
14515                     }
14516                 }else{
14517                     if(k == keyCode){
14518                         if(this.stopEvent){
14519                            e.stopEvent();
14520                         }
14521                         fn.call(scope || window, k, e);
14522                     }
14523                 }
14524             }
14525         };
14526         this.bindings.push(handler);  
14527         },
14528
14529     /**
14530      * Shorthand for adding a single key listener
14531      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14532      * following options:
14533      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14534      * @param {Function} fn The function to call
14535      * @param {Object} scope (optional) The scope of the function
14536      */
14537     on : function(key, fn, scope){
14538         var keyCode, shift, ctrl, alt;
14539         if(typeof key == "object" && !(key instanceof Array)){
14540             keyCode = key.key;
14541             shift = key.shift;
14542             ctrl = key.ctrl;
14543             alt = key.alt;
14544         }else{
14545             keyCode = key;
14546         }
14547         this.addBinding({
14548             key: keyCode,
14549             shift: shift,
14550             ctrl: ctrl,
14551             alt: alt,
14552             fn: fn,
14553             scope: scope
14554         })
14555     },
14556
14557     // private
14558     handleKeyDown : function(e){
14559             if(this.enabled){ //just in case
14560             var b = this.bindings;
14561             for(var i = 0, len = b.length; i < len; i++){
14562                 b[i].call(this, e);
14563             }
14564             }
14565         },
14566         
14567         /**
14568          * Returns true if this KeyMap is enabled
14569          * @return {Boolean} 
14570          */
14571         isEnabled : function(){
14572             return this.enabled;  
14573         },
14574         
14575         /**
14576          * Enables this KeyMap
14577          */
14578         enable: function(){
14579                 if(!this.enabled){
14580                     this.el.on(this.eventName, this.handleKeyDown, this);
14581                     this.enabled = true;
14582                 }
14583         },
14584
14585         /**
14586          * Disable this KeyMap
14587          */
14588         disable: function(){
14589                 if(this.enabled){
14590                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14591                     this.enabled = false;
14592                 }
14593         }
14594 };/*
14595  * Based on:
14596  * Ext JS Library 1.1.1
14597  * Copyright(c) 2006-2007, Ext JS, LLC.
14598  *
14599  * Originally Released Under LGPL - original licence link has changed is not relivant.
14600  *
14601  * Fork - LGPL
14602  * <script type="text/javascript">
14603  */
14604
14605  
14606 /**
14607  * @class Roo.util.TextMetrics
14608  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14609  * wide, in pixels, a given block of text will be.
14610  * @singleton
14611  */
14612 Roo.util.TextMetrics = function(){
14613     var shared;
14614     return {
14615         /**
14616          * Measures the size of the specified text
14617          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14618          * that can affect the size of the rendered text
14619          * @param {String} text The text to measure
14620          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14621          * in order to accurately measure the text height
14622          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14623          */
14624         measure : function(el, text, fixedWidth){
14625             if(!shared){
14626                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14627             }
14628             shared.bind(el);
14629             shared.setFixedWidth(fixedWidth || 'auto');
14630             return shared.getSize(text);
14631         },
14632
14633         /**
14634          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14635          * the overhead of multiple calls to initialize the style properties on each measurement.
14636          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14637          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14638          * in order to accurately measure the text height
14639          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14640          */
14641         createInstance : function(el, fixedWidth){
14642             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14643         }
14644     };
14645 }();
14646
14647  
14648
14649 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14650     var ml = new Roo.Element(document.createElement('div'));
14651     document.body.appendChild(ml.dom);
14652     ml.position('absolute');
14653     ml.setLeftTop(-1000, -1000);
14654     ml.hide();
14655
14656     if(fixedWidth){
14657         ml.setWidth(fixedWidth);
14658     }
14659      
14660     var instance = {
14661         /**
14662          * Returns the size of the specified text based on the internal element's style and width properties
14663          * @memberOf Roo.util.TextMetrics.Instance#
14664          * @param {String} text The text to measure
14665          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14666          */
14667         getSize : function(text){
14668             ml.update(text);
14669             var s = ml.getSize();
14670             ml.update('');
14671             return s;
14672         },
14673
14674         /**
14675          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14676          * that can affect the size of the rendered text
14677          * @memberOf Roo.util.TextMetrics.Instance#
14678          * @param {String/HTMLElement} el The element, dom node or id
14679          */
14680         bind : function(el){
14681             ml.setStyle(
14682                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14683             );
14684         },
14685
14686         /**
14687          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14688          * to set a fixed width in order to accurately measure the text height.
14689          * @memberOf Roo.util.TextMetrics.Instance#
14690          * @param {Number} width The width to set on the element
14691          */
14692         setFixedWidth : function(width){
14693             ml.setWidth(width);
14694         },
14695
14696         /**
14697          * Returns the measured width of the specified text
14698          * @memberOf Roo.util.TextMetrics.Instance#
14699          * @param {String} text The text to measure
14700          * @return {Number} width The width in pixels
14701          */
14702         getWidth : function(text){
14703             ml.dom.style.width = 'auto';
14704             return this.getSize(text).width;
14705         },
14706
14707         /**
14708          * Returns the measured height of the specified text.  For multiline text, be sure to call
14709          * {@link #setFixedWidth} if necessary.
14710          * @memberOf Roo.util.TextMetrics.Instance#
14711          * @param {String} text The text to measure
14712          * @return {Number} height The height in pixels
14713          */
14714         getHeight : function(text){
14715             return this.getSize(text).height;
14716         }
14717     };
14718
14719     instance.bind(bindTo);
14720
14721     return instance;
14722 };
14723
14724 // backwards compat
14725 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14726  * Based on:
14727  * Ext JS Library 1.1.1
14728  * Copyright(c) 2006-2007, Ext JS, LLC.
14729  *
14730  * Originally Released Under LGPL - original licence link has changed is not relivant.
14731  *
14732  * Fork - LGPL
14733  * <script type="text/javascript">
14734  */
14735
14736 /**
14737  * @class Roo.state.Provider
14738  * Abstract base class for state provider implementations. This class provides methods
14739  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14740  * Provider interface.
14741  */
14742 Roo.state.Provider = function(){
14743     /**
14744      * @event statechange
14745      * Fires when a state change occurs.
14746      * @param {Provider} this This state provider
14747      * @param {String} key The state key which was changed
14748      * @param {String} value The encoded value for the state
14749      */
14750     this.addEvents({
14751         "statechange": true
14752     });
14753     this.state = {};
14754     Roo.state.Provider.superclass.constructor.call(this);
14755 };
14756 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14757     /**
14758      * Returns the current value for a key
14759      * @param {String} name The key name
14760      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14761      * @return {Mixed} The state data
14762      */
14763     get : function(name, defaultValue){
14764         return typeof this.state[name] == "undefined" ?
14765             defaultValue : this.state[name];
14766     },
14767     
14768     /**
14769      * Clears a value from the state
14770      * @param {String} name The key name
14771      */
14772     clear : function(name){
14773         delete this.state[name];
14774         this.fireEvent("statechange", this, name, null);
14775     },
14776     
14777     /**
14778      * Sets the value for a key
14779      * @param {String} name The key name
14780      * @param {Mixed} value The value to set
14781      */
14782     set : function(name, value){
14783         this.state[name] = value;
14784         this.fireEvent("statechange", this, name, value);
14785     },
14786     
14787     /**
14788      * Decodes a string previously encoded with {@link #encodeValue}.
14789      * @param {String} value The value to decode
14790      * @return {Mixed} The decoded value
14791      */
14792     decodeValue : function(cookie){
14793         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14794         var matches = re.exec(unescape(cookie));
14795         if(!matches || !matches[1]) return; // non state cookie
14796         var type = matches[1];
14797         var v = matches[2];
14798         switch(type){
14799             case "n":
14800                 return parseFloat(v);
14801             case "d":
14802                 return new Date(Date.parse(v));
14803             case "b":
14804                 return (v == "1");
14805             case "a":
14806                 var all = [];
14807                 var values = v.split("^");
14808                 for(var i = 0, len = values.length; i < len; i++){
14809                     all.push(this.decodeValue(values[i]));
14810                 }
14811                 return all;
14812            case "o":
14813                 var all = {};
14814                 var values = v.split("^");
14815                 for(var i = 0, len = values.length; i < len; i++){
14816                     var kv = values[i].split("=");
14817                     all[kv[0]] = this.decodeValue(kv[1]);
14818                 }
14819                 return all;
14820            default:
14821                 return v;
14822         }
14823     },
14824     
14825     /**
14826      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14827      * @param {Mixed} value The value to encode
14828      * @return {String} The encoded value
14829      */
14830     encodeValue : function(v){
14831         var enc;
14832         if(typeof v == "number"){
14833             enc = "n:" + v;
14834         }else if(typeof v == "boolean"){
14835             enc = "b:" + (v ? "1" : "0");
14836         }else if(v instanceof Date){
14837             enc = "d:" + v.toGMTString();
14838         }else if(v instanceof Array){
14839             var flat = "";
14840             for(var i = 0, len = v.length; i < len; i++){
14841                 flat += this.encodeValue(v[i]);
14842                 if(i != len-1) flat += "^";
14843             }
14844             enc = "a:" + flat;
14845         }else if(typeof v == "object"){
14846             var flat = "";
14847             for(var key in v){
14848                 if(typeof v[key] != "function"){
14849                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14850                 }
14851             }
14852             enc = "o:" + flat.substring(0, flat.length-1);
14853         }else{
14854             enc = "s:" + v;
14855         }
14856         return escape(enc);        
14857     }
14858 });
14859
14860 /*
14861  * Based on:
14862  * Ext JS Library 1.1.1
14863  * Copyright(c) 2006-2007, Ext JS, LLC.
14864  *
14865  * Originally Released Under LGPL - original licence link has changed is not relivant.
14866  *
14867  * Fork - LGPL
14868  * <script type="text/javascript">
14869  */
14870 /**
14871  * @class Roo.state.Manager
14872  * This is the global state manager. By default all components that are "state aware" check this class
14873  * for state information if you don't pass them a custom state provider. In order for this class
14874  * to be useful, it must be initialized with a provider when your application initializes.
14875  <pre><code>
14876 // in your initialization function
14877 init : function(){
14878    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14879    ...
14880    // supposed you have a {@link Roo.BorderLayout}
14881    var layout = new Roo.BorderLayout(...);
14882    layout.restoreState();
14883    // or a {Roo.BasicDialog}
14884    var dialog = new Roo.BasicDialog(...);
14885    dialog.restoreState();
14886  </code></pre>
14887  * @singleton
14888  */
14889 Roo.state.Manager = function(){
14890     var provider = new Roo.state.Provider();
14891     
14892     return {
14893         /**
14894          * Configures the default state provider for your application
14895          * @param {Provider} stateProvider The state provider to set
14896          */
14897         setProvider : function(stateProvider){
14898             provider = stateProvider;
14899         },
14900         
14901         /**
14902          * Returns the current value for a key
14903          * @param {String} name The key name
14904          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14905          * @return {Mixed} The state data
14906          */
14907         get : function(key, defaultValue){
14908             return provider.get(key, defaultValue);
14909         },
14910         
14911         /**
14912          * Sets the value for a key
14913          * @param {String} name The key name
14914          * @param {Mixed} value The state data
14915          */
14916          set : function(key, value){
14917             provider.set(key, value);
14918         },
14919         
14920         /**
14921          * Clears a value from the state
14922          * @param {String} name The key name
14923          */
14924         clear : function(key){
14925             provider.clear(key);
14926         },
14927         
14928         /**
14929          * Gets the currently configured state provider
14930          * @return {Provider} The state provider
14931          */
14932         getProvider : function(){
14933             return provider;
14934         }
14935     };
14936 }();
14937 /*
14938  * Based on:
14939  * Ext JS Library 1.1.1
14940  * Copyright(c) 2006-2007, Ext JS, LLC.
14941  *
14942  * Originally Released Under LGPL - original licence link has changed is not relivant.
14943  *
14944  * Fork - LGPL
14945  * <script type="text/javascript">
14946  */
14947 /**
14948  * @class Roo.state.CookieProvider
14949  * @extends Roo.state.Provider
14950  * The default Provider implementation which saves state via cookies.
14951  * <br />Usage:
14952  <pre><code>
14953    var cp = new Roo.state.CookieProvider({
14954        path: "/cgi-bin/",
14955        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14956        domain: "roojs.com"
14957    })
14958    Roo.state.Manager.setProvider(cp);
14959  </code></pre>
14960  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14961  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14962  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14963  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14964  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14965  * domain the page is running on including the 'www' like 'www.roojs.com')
14966  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14967  * @constructor
14968  * Create a new CookieProvider
14969  * @param {Object} config The configuration object
14970  */
14971 Roo.state.CookieProvider = function(config){
14972     Roo.state.CookieProvider.superclass.constructor.call(this);
14973     this.path = "/";
14974     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14975     this.domain = null;
14976     this.secure = false;
14977     Roo.apply(this, config);
14978     this.state = this.readCookies();
14979 };
14980
14981 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14982     // private
14983     set : function(name, value){
14984         if(typeof value == "undefined" || value === null){
14985             this.clear(name);
14986             return;
14987         }
14988         this.setCookie(name, value);
14989         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14990     },
14991
14992     // private
14993     clear : function(name){
14994         this.clearCookie(name);
14995         Roo.state.CookieProvider.superclass.clear.call(this, name);
14996     },
14997
14998     // private
14999     readCookies : function(){
15000         var cookies = {};
15001         var c = document.cookie + ";";
15002         var re = /\s?(.*?)=(.*?);/g;
15003         var matches;
15004         while((matches = re.exec(c)) != null){
15005             var name = matches[1];
15006             var value = matches[2];
15007             if(name && name.substring(0,3) == "ys-"){
15008                 cookies[name.substr(3)] = this.decodeValue(value);
15009             }
15010         }
15011         return cookies;
15012     },
15013
15014     // private
15015     setCookie : function(name, value){
15016         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15017            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15018            ((this.path == null) ? "" : ("; path=" + this.path)) +
15019            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15020            ((this.secure == true) ? "; secure" : "");
15021     },
15022
15023     // private
15024     clearCookie : function(name){
15025         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15026            ((this.path == null) ? "" : ("; path=" + this.path)) +
15027            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15028            ((this.secure == true) ? "; secure" : "");
15029     }
15030 });/*
15031  * Based on:
15032  * Ext JS Library 1.1.1
15033  * Copyright(c) 2006-2007, Ext JS, LLC.
15034  *
15035  * Originally Released Under LGPL - original licence link has changed is not relivant.
15036  *
15037  * Fork - LGPL
15038  * <script type="text/javascript">
15039  */
15040  
15041
15042 /**
15043  * @class Roo.ComponentMgr
15044  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15045  * @singleton
15046  */
15047 Roo.ComponentMgr = function(){
15048     var all = new Roo.util.MixedCollection();
15049
15050     return {
15051         /**
15052          * Registers a component.
15053          * @param {Roo.Component} c The component
15054          */
15055         register : function(c){
15056             all.add(c);
15057         },
15058
15059         /**
15060          * Unregisters a component.
15061          * @param {Roo.Component} c The component
15062          */
15063         unregister : function(c){
15064             all.remove(c);
15065         },
15066
15067         /**
15068          * Returns a component by id
15069          * @param {String} id The component id
15070          */
15071         get : function(id){
15072             return all.get(id);
15073         },
15074
15075         /**
15076          * Registers a function that will be called when a specified component is added to ComponentMgr
15077          * @param {String} id The component id
15078          * @param {Funtction} fn The callback function
15079          * @param {Object} scope The scope of the callback
15080          */
15081         onAvailable : function(id, fn, scope){
15082             all.on("add", function(index, o){
15083                 if(o.id == id){
15084                     fn.call(scope || o, o);
15085                     all.un("add", fn, scope);
15086                 }
15087             });
15088         }
15089     };
15090 }();/*
15091  * Based on:
15092  * Ext JS Library 1.1.1
15093  * Copyright(c) 2006-2007, Ext JS, LLC.
15094  *
15095  * Originally Released Under LGPL - original licence link has changed is not relivant.
15096  *
15097  * Fork - LGPL
15098  * <script type="text/javascript">
15099  */
15100  
15101 /**
15102  * @class Roo.Component
15103  * @extends Roo.util.Observable
15104  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15105  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15106  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15107  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15108  * All visual components (widgets) that require rendering into a layout should subclass Component.
15109  * @constructor
15110  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15111  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15112  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15113  */
15114 Roo.Component = function(config){
15115     config = config || {};
15116     if(config.tagName || config.dom || typeof config == "string"){ // element object
15117         config = {el: config, id: config.id || config};
15118     }
15119     this.initialConfig = config;
15120
15121     Roo.apply(this, config);
15122     this.addEvents({
15123         /**
15124          * @event disable
15125          * Fires after the component is disabled.
15126              * @param {Roo.Component} this
15127              */
15128         disable : true,
15129         /**
15130          * @event enable
15131          * Fires after the component is enabled.
15132              * @param {Roo.Component} this
15133              */
15134         enable : true,
15135         /**
15136          * @event beforeshow
15137          * Fires before the component is shown.  Return false to stop the show.
15138              * @param {Roo.Component} this
15139              */
15140         beforeshow : true,
15141         /**
15142          * @event show
15143          * Fires after the component is shown.
15144              * @param {Roo.Component} this
15145              */
15146         show : true,
15147         /**
15148          * @event beforehide
15149          * Fires before the component is hidden. Return false to stop the hide.
15150              * @param {Roo.Component} this
15151              */
15152         beforehide : true,
15153         /**
15154          * @event hide
15155          * Fires after the component is hidden.
15156              * @param {Roo.Component} this
15157              */
15158         hide : true,
15159         /**
15160          * @event beforerender
15161          * Fires before the component is rendered. Return false to stop the render.
15162              * @param {Roo.Component} this
15163              */
15164         beforerender : true,
15165         /**
15166          * @event render
15167          * Fires after the component is rendered.
15168              * @param {Roo.Component} this
15169              */
15170         render : true,
15171         /**
15172          * @event beforedestroy
15173          * Fires before the component is destroyed. Return false to stop the destroy.
15174              * @param {Roo.Component} this
15175              */
15176         beforedestroy : true,
15177         /**
15178          * @event destroy
15179          * Fires after the component is destroyed.
15180              * @param {Roo.Component} this
15181              */
15182         destroy : true
15183     });
15184     if(!this.id){
15185         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15186     }
15187     Roo.ComponentMgr.register(this);
15188     Roo.Component.superclass.constructor.call(this);
15189     this.initComponent();
15190     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15191         this.render(this.renderTo);
15192         delete this.renderTo;
15193     }
15194 };
15195
15196 /** @private */
15197 Roo.Component.AUTO_ID = 1000;
15198
15199 Roo.extend(Roo.Component, Roo.util.Observable, {
15200     /**
15201      * @scope Roo.Component.prototype
15202      * @type {Boolean}
15203      * true if this component is hidden. Read-only.
15204      */
15205     hidden : false,
15206     /**
15207      * @type {Boolean}
15208      * true if this component is disabled. Read-only.
15209      */
15210     disabled : false,
15211     /**
15212      * @type {Boolean}
15213      * true if this component has been rendered. Read-only.
15214      */
15215     rendered : false,
15216     
15217     /** @cfg {String} disableClass
15218      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15219      */
15220     disabledClass : "x-item-disabled",
15221         /** @cfg {Boolean} allowDomMove
15222          * Whether the component can move the Dom node when rendering (defaults to true).
15223          */
15224     allowDomMove : true,
15225     /** @cfg {String} hideMode (display|visibility)
15226      * How this component should hidden. Supported values are
15227      * "visibility" (css visibility), "offsets" (negative offset position) and
15228      * "display" (css display) - defaults to "display".
15229      */
15230     hideMode: 'display',
15231
15232     /** @private */
15233     ctype : "Roo.Component",
15234
15235     /**
15236      * @cfg {String} actionMode 
15237      * which property holds the element that used for  hide() / show() / disable() / enable()
15238      * default is 'el' 
15239      */
15240     actionMode : "el",
15241
15242     /** @private */
15243     getActionEl : function(){
15244         return this[this.actionMode];
15245     },
15246
15247     initComponent : Roo.emptyFn,
15248     /**
15249      * If this is a lazy rendering component, render it to its container element.
15250      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15251      */
15252     render : function(container, position){
15253         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15254             if(!container && this.el){
15255                 this.el = Roo.get(this.el);
15256                 container = this.el.dom.parentNode;
15257                 this.allowDomMove = false;
15258             }
15259             this.container = Roo.get(container);
15260             this.rendered = true;
15261             if(position !== undefined){
15262                 if(typeof position == 'number'){
15263                     position = this.container.dom.childNodes[position];
15264                 }else{
15265                     position = Roo.getDom(position);
15266                 }
15267             }
15268             this.onRender(this.container, position || null);
15269             if(this.cls){
15270                 this.el.addClass(this.cls);
15271                 delete this.cls;
15272             }
15273             if(this.style){
15274                 this.el.applyStyles(this.style);
15275                 delete this.style;
15276             }
15277             this.fireEvent("render", this);
15278             this.afterRender(this.container);
15279             if(this.hidden){
15280                 this.hide();
15281             }
15282             if(this.disabled){
15283                 this.disable();
15284             }
15285         }
15286         return this;
15287     },
15288
15289     /** @private */
15290     // default function is not really useful
15291     onRender : function(ct, position){
15292         if(this.el){
15293             this.el = Roo.get(this.el);
15294             if(this.allowDomMove !== false){
15295                 ct.dom.insertBefore(this.el.dom, position);
15296             }
15297         }
15298     },
15299
15300     /** @private */
15301     getAutoCreate : function(){
15302         var cfg = typeof this.autoCreate == "object" ?
15303                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15304         if(this.id && !cfg.id){
15305             cfg.id = this.id;
15306         }
15307         return cfg;
15308     },
15309
15310     /** @private */
15311     afterRender : Roo.emptyFn,
15312
15313     /**
15314      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15315      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15316      */
15317     destroy : function(){
15318         if(this.fireEvent("beforedestroy", this) !== false){
15319             this.purgeListeners();
15320             this.beforeDestroy();
15321             if(this.rendered){
15322                 this.el.removeAllListeners();
15323                 this.el.remove();
15324                 if(this.actionMode == "container"){
15325                     this.container.remove();
15326                 }
15327             }
15328             this.onDestroy();
15329             Roo.ComponentMgr.unregister(this);
15330             this.fireEvent("destroy", this);
15331         }
15332     },
15333
15334         /** @private */
15335     beforeDestroy : function(){
15336
15337     },
15338
15339         /** @private */
15340         onDestroy : function(){
15341
15342     },
15343
15344     /**
15345      * Returns the underlying {@link Roo.Element}.
15346      * @return {Roo.Element} The element
15347      */
15348     getEl : function(){
15349         return this.el;
15350     },
15351
15352     /**
15353      * Returns the id of this component.
15354      * @return {String}
15355      */
15356     getId : function(){
15357         return this.id;
15358     },
15359
15360     /**
15361      * Try to focus this component.
15362      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15363      * @return {Roo.Component} this
15364      */
15365     focus : function(selectText){
15366         if(this.rendered){
15367             this.el.focus();
15368             if(selectText === true){
15369                 this.el.dom.select();
15370             }
15371         }
15372         return this;
15373     },
15374
15375     /** @private */
15376     blur : function(){
15377         if(this.rendered){
15378             this.el.blur();
15379         }
15380         return this;
15381     },
15382
15383     /**
15384      * Disable this component.
15385      * @return {Roo.Component} this
15386      */
15387     disable : function(){
15388         if(this.rendered){
15389             this.onDisable();
15390         }
15391         this.disabled = true;
15392         this.fireEvent("disable", this);
15393         return this;
15394     },
15395
15396         // private
15397     onDisable : function(){
15398         this.getActionEl().addClass(this.disabledClass);
15399         this.el.dom.disabled = true;
15400     },
15401
15402     /**
15403      * Enable this component.
15404      * @return {Roo.Component} this
15405      */
15406     enable : function(){
15407         if(this.rendered){
15408             this.onEnable();
15409         }
15410         this.disabled = false;
15411         this.fireEvent("enable", this);
15412         return this;
15413     },
15414
15415         // private
15416     onEnable : function(){
15417         this.getActionEl().removeClass(this.disabledClass);
15418         this.el.dom.disabled = false;
15419     },
15420
15421     /**
15422      * Convenience function for setting disabled/enabled by boolean.
15423      * @param {Boolean} disabled
15424      */
15425     setDisabled : function(disabled){
15426         this[disabled ? "disable" : "enable"]();
15427     },
15428
15429     /**
15430      * Show this component.
15431      * @return {Roo.Component} this
15432      */
15433     show: function(){
15434         if(this.fireEvent("beforeshow", this) !== false){
15435             this.hidden = false;
15436             if(this.rendered){
15437                 this.onShow();
15438             }
15439             this.fireEvent("show", this);
15440         }
15441         return this;
15442     },
15443
15444     // private
15445     onShow : function(){
15446         var ae = this.getActionEl();
15447         if(this.hideMode == 'visibility'){
15448             ae.dom.style.visibility = "visible";
15449         }else if(this.hideMode == 'offsets'){
15450             ae.removeClass('x-hidden');
15451         }else{
15452             ae.dom.style.display = "";
15453         }
15454     },
15455
15456     /**
15457      * Hide this component.
15458      * @return {Roo.Component} this
15459      */
15460     hide: function(){
15461         if(this.fireEvent("beforehide", this) !== false){
15462             this.hidden = true;
15463             if(this.rendered){
15464                 this.onHide();
15465             }
15466             this.fireEvent("hide", this);
15467         }
15468         return this;
15469     },
15470
15471     // private
15472     onHide : function(){
15473         var ae = this.getActionEl();
15474         if(this.hideMode == 'visibility'){
15475             ae.dom.style.visibility = "hidden";
15476         }else if(this.hideMode == 'offsets'){
15477             ae.addClass('x-hidden');
15478         }else{
15479             ae.dom.style.display = "none";
15480         }
15481     },
15482
15483     /**
15484      * Convenience function to hide or show this component by boolean.
15485      * @param {Boolean} visible True to show, false to hide
15486      * @return {Roo.Component} this
15487      */
15488     setVisible: function(visible){
15489         if(visible) {
15490             this.show();
15491         }else{
15492             this.hide();
15493         }
15494         return this;
15495     },
15496
15497     /**
15498      * Returns true if this component is visible.
15499      */
15500     isVisible : function(){
15501         return this.getActionEl().isVisible();
15502     },
15503
15504     cloneConfig : function(overrides){
15505         overrides = overrides || {};
15506         var id = overrides.id || Roo.id();
15507         var cfg = Roo.applyIf(overrides, this.initialConfig);
15508         cfg.id = id; // prevent dup id
15509         return new this.constructor(cfg);
15510     }
15511 });/*
15512  * Based on:
15513  * Ext JS Library 1.1.1
15514  * Copyright(c) 2006-2007, Ext JS, LLC.
15515  *
15516  * Originally Released Under LGPL - original licence link has changed is not relivant.
15517  *
15518  * Fork - LGPL
15519  * <script type="text/javascript">
15520  */
15521
15522 /**
15523  * @class Roo.BoxComponent
15524  * @extends Roo.Component
15525  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15526  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15527  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15528  * layout containers.
15529  * @constructor
15530  * @param {Roo.Element/String/Object} config The configuration options.
15531  */
15532 Roo.BoxComponent = function(config){
15533     Roo.Component.call(this, config);
15534     this.addEvents({
15535         /**
15536          * @event resize
15537          * Fires after the component is resized.
15538              * @param {Roo.Component} this
15539              * @param {Number} adjWidth The box-adjusted width that was set
15540              * @param {Number} adjHeight The box-adjusted height that was set
15541              * @param {Number} rawWidth The width that was originally specified
15542              * @param {Number} rawHeight The height that was originally specified
15543              */
15544         resize : true,
15545         /**
15546          * @event move
15547          * Fires after the component is moved.
15548              * @param {Roo.Component} this
15549              * @param {Number} x The new x position
15550              * @param {Number} y The new y position
15551              */
15552         move : true
15553     });
15554 };
15555
15556 Roo.extend(Roo.BoxComponent, Roo.Component, {
15557     // private, set in afterRender to signify that the component has been rendered
15558     boxReady : false,
15559     // private, used to defer height settings to subclasses
15560     deferHeight: false,
15561     /** @cfg {Number} width
15562      * width (optional) size of component
15563      */
15564      /** @cfg {Number} height
15565      * height (optional) size of component
15566      */
15567      
15568     /**
15569      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15570      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15571      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15572      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15573      * @return {Roo.BoxComponent} this
15574      */
15575     setSize : function(w, h){
15576         // support for standard size objects
15577         if(typeof w == 'object'){
15578             h = w.height;
15579             w = w.width;
15580         }
15581         // not rendered
15582         if(!this.boxReady){
15583             this.width = w;
15584             this.height = h;
15585             return this;
15586         }
15587
15588         // prevent recalcs when not needed
15589         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15590             return this;
15591         }
15592         this.lastSize = {width: w, height: h};
15593
15594         var adj = this.adjustSize(w, h);
15595         var aw = adj.width, ah = adj.height;
15596         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15597             var rz = this.getResizeEl();
15598             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15599                 rz.setSize(aw, ah);
15600             }else if(!this.deferHeight && ah !== undefined){
15601                 rz.setHeight(ah);
15602             }else if(aw !== undefined){
15603                 rz.setWidth(aw);
15604             }
15605             this.onResize(aw, ah, w, h);
15606             this.fireEvent('resize', this, aw, ah, w, h);
15607         }
15608         return this;
15609     },
15610
15611     /**
15612      * Gets the current size of the component's underlying element.
15613      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15614      */
15615     getSize : function(){
15616         return this.el.getSize();
15617     },
15618
15619     /**
15620      * Gets the current XY position of the component's underlying element.
15621      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15622      * @return {Array} The XY position of the element (e.g., [100, 200])
15623      */
15624     getPosition : function(local){
15625         if(local === true){
15626             return [this.el.getLeft(true), this.el.getTop(true)];
15627         }
15628         return this.xy || this.el.getXY();
15629     },
15630
15631     /**
15632      * Gets the current box measurements of the component's underlying element.
15633      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15634      * @returns {Object} box An object in the format {x, y, width, height}
15635      */
15636     getBox : function(local){
15637         var s = this.el.getSize();
15638         if(local){
15639             s.x = this.el.getLeft(true);
15640             s.y = this.el.getTop(true);
15641         }else{
15642             var xy = this.xy || this.el.getXY();
15643             s.x = xy[0];
15644             s.y = xy[1];
15645         }
15646         return s;
15647     },
15648
15649     /**
15650      * Sets the current box measurements of the component's underlying element.
15651      * @param {Object} box An object in the format {x, y, width, height}
15652      * @returns {Roo.BoxComponent} this
15653      */
15654     updateBox : function(box){
15655         this.setSize(box.width, box.height);
15656         this.setPagePosition(box.x, box.y);
15657         return this;
15658     },
15659
15660     // protected
15661     getResizeEl : function(){
15662         return this.resizeEl || this.el;
15663     },
15664
15665     // protected
15666     getPositionEl : function(){
15667         return this.positionEl || this.el;
15668     },
15669
15670     /**
15671      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15672      * This method fires the move event.
15673      * @param {Number} left The new left
15674      * @param {Number} top The new top
15675      * @returns {Roo.BoxComponent} this
15676      */
15677     setPosition : function(x, y){
15678         this.x = x;
15679         this.y = y;
15680         if(!this.boxReady){
15681             return this;
15682         }
15683         var adj = this.adjustPosition(x, y);
15684         var ax = adj.x, ay = adj.y;
15685
15686         var el = this.getPositionEl();
15687         if(ax !== undefined || ay !== undefined){
15688             if(ax !== undefined && ay !== undefined){
15689                 el.setLeftTop(ax, ay);
15690             }else if(ax !== undefined){
15691                 el.setLeft(ax);
15692             }else if(ay !== undefined){
15693                 el.setTop(ay);
15694             }
15695             this.onPosition(ax, ay);
15696             this.fireEvent('move', this, ax, ay);
15697         }
15698         return this;
15699     },
15700
15701     /**
15702      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15703      * This method fires the move event.
15704      * @param {Number} x The new x position
15705      * @param {Number} y The new y position
15706      * @returns {Roo.BoxComponent} this
15707      */
15708     setPagePosition : function(x, y){
15709         this.pageX = x;
15710         this.pageY = y;
15711         if(!this.boxReady){
15712             return;
15713         }
15714         if(x === undefined || y === undefined){ // cannot translate undefined points
15715             return;
15716         }
15717         var p = this.el.translatePoints(x, y);
15718         this.setPosition(p.left, p.top);
15719         return this;
15720     },
15721
15722     // private
15723     onRender : function(ct, position){
15724         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15725         if(this.resizeEl){
15726             this.resizeEl = Roo.get(this.resizeEl);
15727         }
15728         if(this.positionEl){
15729             this.positionEl = Roo.get(this.positionEl);
15730         }
15731     },
15732
15733     // private
15734     afterRender : function(){
15735         Roo.BoxComponent.superclass.afterRender.call(this);
15736         this.boxReady = true;
15737         this.setSize(this.width, this.height);
15738         if(this.x || this.y){
15739             this.setPosition(this.x, this.y);
15740         }
15741         if(this.pageX || this.pageY){
15742             this.setPagePosition(this.pageX, this.pageY);
15743         }
15744     },
15745
15746     /**
15747      * Force the component's size to recalculate based on the underlying element's current height and width.
15748      * @returns {Roo.BoxComponent} this
15749      */
15750     syncSize : function(){
15751         delete this.lastSize;
15752         this.setSize(this.el.getWidth(), this.el.getHeight());
15753         return this;
15754     },
15755
15756     /**
15757      * Called after the component is resized, this method is empty by default but can be implemented by any
15758      * subclass that needs to perform custom logic after a resize occurs.
15759      * @param {Number} adjWidth The box-adjusted width that was set
15760      * @param {Number} adjHeight The box-adjusted height that was set
15761      * @param {Number} rawWidth The width that was originally specified
15762      * @param {Number} rawHeight The height that was originally specified
15763      */
15764     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15765
15766     },
15767
15768     /**
15769      * Called after the component is moved, this method is empty by default but can be implemented by any
15770      * subclass that needs to perform custom logic after a move occurs.
15771      * @param {Number} x The new x position
15772      * @param {Number} y The new y position
15773      */
15774     onPosition : function(x, y){
15775
15776     },
15777
15778     // private
15779     adjustSize : function(w, h){
15780         if(this.autoWidth){
15781             w = 'auto';
15782         }
15783         if(this.autoHeight){
15784             h = 'auto';
15785         }
15786         return {width : w, height: h};
15787     },
15788
15789     // private
15790     adjustPosition : function(x, y){
15791         return {x : x, y: y};
15792     }
15793 });/*
15794  * Original code for Roojs - LGPL
15795  * <script type="text/javascript">
15796  */
15797  
15798 /**
15799  * @class Roo.XComponent
15800  * A delayed Element creator...
15801  * Or a way to group chunks of interface together.
15802  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15803  *  used in conjunction with XComponent.build() it will create an instance of each element,
15804  *  then call addxtype() to build the User interface.
15805  * 
15806  * Mypart.xyx = new Roo.XComponent({
15807
15808     parent : 'Mypart.xyz', // empty == document.element.!!
15809     order : '001',
15810     name : 'xxxx'
15811     region : 'xxxx'
15812     disabled : function() {} 
15813      
15814     tree : function() { // return an tree of xtype declared components
15815         var MODULE = this;
15816         return 
15817         {
15818             xtype : 'NestedLayoutPanel',
15819             // technicall
15820         }
15821      ]
15822  *})
15823  *
15824  *
15825  * It can be used to build a big heiracy, with parent etc.
15826  * or you can just use this to render a single compoent to a dom element
15827  * MYPART.render(Roo.Element | String(id) | dom_element )
15828  *
15829  *
15830  * Usage patterns.
15831  *
15832  * Classic Roo
15833  *
15834  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15835  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15836  *
15837  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15838  *
15839  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15840  * - if mulitple topModules exist, the last one is defined as the top module.
15841  *
15842  * Embeded Roo
15843  * 
15844  * When the top level or multiple modules are to embedded into a existing HTML page,
15845  * the parent element can container '#id' of the element where the module will be drawn.
15846  *
15847  * Bootstrap Roo
15848  *
15849  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15850  * it relies more on a include mechanism, where sub modules are included into an outer page.
15851  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15852  * 
15853  * Bootstrap Roo Included elements
15854  *
15855  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15856  * hence confusing the component builder as it thinks there are multiple top level elements. 
15857  *
15858  * 
15859  * 
15860  * @extends Roo.util.Observable
15861  * @constructor
15862  * @param cfg {Object} configuration of component
15863  * 
15864  */
15865 Roo.XComponent = function(cfg) {
15866     Roo.apply(this, cfg);
15867     this.addEvents({ 
15868         /**
15869              * @event built
15870              * Fires when this the componnt is built
15871              * @param {Roo.XComponent} c the component
15872              */
15873         'built' : true
15874         
15875     });
15876     this.region = this.region || 'center'; // default..
15877     Roo.XComponent.register(this);
15878     this.modules = false;
15879     this.el = false; // where the layout goes..
15880     
15881     
15882 }
15883 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15884     /**
15885      * @property el
15886      * The created element (with Roo.factory())
15887      * @type {Roo.Layout}
15888      */
15889     el  : false,
15890     
15891     /**
15892      * @property el
15893      * for BC  - use el in new code
15894      * @type {Roo.Layout}
15895      */
15896     panel : false,
15897     
15898     /**
15899      * @property layout
15900      * for BC  - use el in new code
15901      * @type {Roo.Layout}
15902      */
15903     layout : false,
15904     
15905      /**
15906      * @cfg {Function|boolean} disabled
15907      * If this module is disabled by some rule, return true from the funtion
15908      */
15909     disabled : false,
15910     
15911     /**
15912      * @cfg {String} parent 
15913      * Name of parent element which it get xtype added to..
15914      */
15915     parent: false,
15916     
15917     /**
15918      * @cfg {String} order
15919      * Used to set the order in which elements are created (usefull for multiple tabs)
15920      */
15921     
15922     order : false,
15923     /**
15924      * @cfg {String} name
15925      * String to display while loading.
15926      */
15927     name : false,
15928     /**
15929      * @cfg {String} region
15930      * Region to render component to (defaults to center)
15931      */
15932     region : 'center',
15933     
15934     /**
15935      * @cfg {Array} items
15936      * A single item array - the first element is the root of the tree..
15937      * It's done this way to stay compatible with the Xtype system...
15938      */
15939     items : false,
15940     
15941     /**
15942      * @property _tree
15943      * The method that retuns the tree of parts that make up this compoennt 
15944      * @type {function}
15945      */
15946     _tree  : false,
15947     
15948      /**
15949      * render
15950      * render element to dom or tree
15951      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15952      */
15953     
15954     render : function(el)
15955     {
15956         
15957         el = el || false;
15958         var hp = this.parent ? 1 : 0;
15959         Roo.debug &&  Roo.log(this);
15960         
15961         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15962             // if parent is a '#.....' string, then let's use that..
15963             var ename = this.parent.substr(1);
15964             this.parent = false;
15965             Roo.debug && Roo.log(ename);
15966             switch (ename) {
15967                 case 'bootstrap-body' :
15968                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15969                         this.parent = { el :  new  Roo.bootstrap.Body() };
15970                         Roo.debug && Roo.log("setting el to doc body");
15971                          
15972                     } else {
15973                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15974                     }
15975                     break;
15976                 case 'bootstrap':
15977                     this.parent = { el : true};
15978                     // fall through
15979                 default:
15980                     el = Roo.get(ename);
15981                     break;
15982             }
15983                 
15984             
15985             if (!el && !this.parent) {
15986                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15987                 return;
15988             }
15989         }
15990         Roo.debug && Roo.log("EL:");
15991         Roo.debug && Roo.log(el);
15992         Roo.debug && Roo.log("this.parent.el:");
15993         Roo.debug && Roo.log(this.parent.el);
15994         
15995         var tree = this._tree ? this._tree() : this.tree();
15996
15997         // altertive root elements ??? - we need a better way to indicate these.
15998         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15999                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16000         
16001         if (!this.parent && is_alt) {
16002             //el = Roo.get(document.body);
16003             this.parent = { el : true };
16004         }
16005             
16006             
16007         
16008         if (!this.parent) {
16009             
16010             Roo.debug && Roo.log("no parent - creating one");
16011             
16012             el = el ? Roo.get(el) : false;      
16013             
16014             // it's a top level one..
16015             this.parent =  {
16016                 el : new Roo.BorderLayout(el || document.body, {
16017                 
16018                      center: {
16019                          titlebar: false,
16020                          autoScroll:false,
16021                          closeOnTab: true,
16022                          tabPosition: 'top',
16023                           //resizeTabs: true,
16024                          alwaysShowTabs: el && hp? false :  true,
16025                          hideTabs: el || !hp ? true :  false,
16026                          minTabWidth: 140
16027                      }
16028                  })
16029             }
16030         }
16031         
16032         if (!this.parent.el) {
16033                 // probably an old style ctor, which has been disabled.
16034                 return;
16035
16036         }
16037                 // The 'tree' method is  '_tree now' 
16038             
16039         tree.region = tree.region || this.region;
16040         
16041         if (this.parent.el === true) {
16042             // bootstrap... - body..
16043             this.parent.el = Roo.factory(tree);
16044         }
16045         
16046         this.el = this.parent.el.addxtype(tree);
16047         this.fireEvent('built', this);
16048         
16049         this.panel = this.el;
16050         this.layout = this.panel.layout;
16051         this.parentLayout = this.parent.layout  || false;  
16052          
16053     }
16054     
16055 });
16056
16057 Roo.apply(Roo.XComponent, {
16058     /**
16059      * @property  hideProgress
16060      * true to disable the building progress bar.. usefull on single page renders.
16061      * @type Boolean
16062      */
16063     hideProgress : false,
16064     /**
16065      * @property  buildCompleted
16066      * True when the builder has completed building the interface.
16067      * @type Boolean
16068      */
16069     buildCompleted : false,
16070      
16071     /**
16072      * @property  topModule
16073      * the upper most module - uses document.element as it's constructor.
16074      * @type Object
16075      */
16076      
16077     topModule  : false,
16078       
16079     /**
16080      * @property  modules
16081      * array of modules to be created by registration system.
16082      * @type {Array} of Roo.XComponent
16083      */
16084     
16085     modules : [],
16086     /**
16087      * @property  elmodules
16088      * array of modules to be created by which use #ID 
16089      * @type {Array} of Roo.XComponent
16090      */
16091      
16092     elmodules : [],
16093
16094      /**
16095      * @property  build_from_html
16096      * Build elements from html - used by bootstrap HTML stuff 
16097      *    - this is cleared after build is completed
16098      * @type {boolean} true  (default false)
16099      */
16100      
16101     build_from_html : false,
16102
16103     /**
16104      * Register components to be built later.
16105      *
16106      * This solves the following issues
16107      * - Building is not done on page load, but after an authentication process has occured.
16108      * - Interface elements are registered on page load
16109      * - Parent Interface elements may not be loaded before child, so this handles that..
16110      * 
16111      *
16112      * example:
16113      * 
16114      * MyApp.register({
16115           order : '000001',
16116           module : 'Pman.Tab.projectMgr',
16117           region : 'center',
16118           parent : 'Pman.layout',
16119           disabled : false,  // or use a function..
16120         })
16121      
16122      * * @param {Object} details about module
16123      */
16124     register : function(obj) {
16125                 
16126         Roo.XComponent.event.fireEvent('register', obj);
16127         switch(typeof(obj.disabled) ) {
16128                 
16129             case 'undefined':
16130                 break;
16131             
16132             case 'function':
16133                 if ( obj.disabled() ) {
16134                         return;
16135                 }
16136                 break;
16137             
16138             default:
16139                 if (obj.disabled) {
16140                         return;
16141                 }
16142                 break;
16143         }
16144                 
16145         this.modules.push(obj);
16146          
16147     },
16148     /**
16149      * convert a string to an object..
16150      * eg. 'AAA.BBB' -> finds AAA.BBB
16151
16152      */
16153     
16154     toObject : function(str)
16155     {
16156         if (!str || typeof(str) == 'object') {
16157             return str;
16158         }
16159         if (str.substring(0,1) == '#') {
16160             return str;
16161         }
16162
16163         var ar = str.split('.');
16164         var rt, o;
16165         rt = ar.shift();
16166             /** eval:var:o */
16167         try {
16168             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16169         } catch (e) {
16170             throw "Module not found : " + str;
16171         }
16172         
16173         if (o === false) {
16174             throw "Module not found : " + str;
16175         }
16176         Roo.each(ar, function(e) {
16177             if (typeof(o[e]) == 'undefined') {
16178                 throw "Module not found : " + str;
16179             }
16180             o = o[e];
16181         });
16182         
16183         return o;
16184         
16185     },
16186     
16187     
16188     /**
16189      * move modules into their correct place in the tree..
16190      * 
16191      */
16192     preBuild : function ()
16193     {
16194         var _t = this;
16195         Roo.each(this.modules , function (obj)
16196         {
16197             Roo.XComponent.event.fireEvent('beforebuild', obj);
16198             
16199             var opar = obj.parent;
16200             try { 
16201                 obj.parent = this.toObject(opar);
16202             } catch(e) {
16203                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16204                 return;
16205             }
16206             
16207             if (!obj.parent) {
16208                 Roo.debug && Roo.log("GOT top level module");
16209                 Roo.debug && Roo.log(obj);
16210                 obj.modules = new Roo.util.MixedCollection(false, 
16211                     function(o) { return o.order + '' }
16212                 );
16213                 this.topModule = obj;
16214                 return;
16215             }
16216                         // parent is a string (usually a dom element name..)
16217             if (typeof(obj.parent) == 'string') {
16218                 this.elmodules.push(obj);
16219                 return;
16220             }
16221             if (obj.parent.constructor != Roo.XComponent) {
16222                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16223             }
16224             if (!obj.parent.modules) {
16225                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16226                     function(o) { return o.order + '' }
16227                 );
16228             }
16229             if (obj.parent.disabled) {
16230                 obj.disabled = true;
16231             }
16232             obj.parent.modules.add(obj);
16233         }, this);
16234     },
16235     
16236      /**
16237      * make a list of modules to build.
16238      * @return {Array} list of modules. 
16239      */ 
16240     
16241     buildOrder : function()
16242     {
16243         var _this = this;
16244         var cmp = function(a,b) {   
16245             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16246         };
16247         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16248             throw "No top level modules to build";
16249         }
16250         
16251         // make a flat list in order of modules to build.
16252         var mods = this.topModule ? [ this.topModule ] : [];
16253                 
16254         
16255         // elmodules (is a list of DOM based modules )
16256         Roo.each(this.elmodules, function(e) {
16257             mods.push(e);
16258             if (!this.topModule &&
16259                 typeof(e.parent) == 'string' &&
16260                 e.parent.substring(0,1) == '#' &&
16261                 Roo.get(e.parent.substr(1))
16262                ) {
16263                 
16264                 _this.topModule = e;
16265             }
16266             
16267         });
16268
16269         
16270         // add modules to their parents..
16271         var addMod = function(m) {
16272             Roo.debug && Roo.log("build Order: add: " + m.name);
16273                 
16274             mods.push(m);
16275             if (m.modules && !m.disabled) {
16276                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16277                 m.modules.keySort('ASC',  cmp );
16278                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16279     
16280                 m.modules.each(addMod);
16281             } else {
16282                 Roo.debug && Roo.log("build Order: no child modules");
16283             }
16284             // not sure if this is used any more..
16285             if (m.finalize) {
16286                 m.finalize.name = m.name + " (clean up) ";
16287                 mods.push(m.finalize);
16288             }
16289             
16290         }
16291         if (this.topModule && this.topModule.modules) { 
16292             this.topModule.modules.keySort('ASC',  cmp );
16293             this.topModule.modules.each(addMod);
16294         } 
16295         return mods;
16296     },
16297     
16298      /**
16299      * Build the registered modules.
16300      * @param {Object} parent element.
16301      * @param {Function} optional method to call after module has been added.
16302      * 
16303      */ 
16304    
16305     build : function(opts) 
16306     {
16307         
16308         if (typeof(opts) != 'undefined') {
16309             Roo.apply(this,opts);
16310         }
16311         
16312         this.preBuild();
16313         var mods = this.buildOrder();
16314       
16315         //this.allmods = mods;
16316         //Roo.debug && Roo.log(mods);
16317         //return;
16318         if (!mods.length) { // should not happen
16319             throw "NO modules!!!";
16320         }
16321         
16322         
16323         var msg = "Building Interface...";
16324         // flash it up as modal - so we store the mask!?
16325         if (!this.hideProgress && Roo.MessageBox) {
16326             Roo.MessageBox.show({ title: 'loading' });
16327             Roo.MessageBox.show({
16328                title: "Please wait...",
16329                msg: msg,
16330                width:450,
16331                progress:true,
16332                closable:false,
16333                modal: false
16334               
16335             });
16336         }
16337         var total = mods.length;
16338         
16339         var _this = this;
16340         var progressRun = function() {
16341             if (!mods.length) {
16342                 Roo.debug && Roo.log('hide?');
16343                 if (!this.hideProgress && Roo.MessageBox) {
16344                     Roo.MessageBox.hide();
16345                 }
16346                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16347                 
16348                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16349                 
16350                 // THE END...
16351                 return false;   
16352             }
16353             
16354             var m = mods.shift();
16355             
16356             
16357             Roo.debug && Roo.log(m);
16358             // not sure if this is supported any more.. - modules that are are just function
16359             if (typeof(m) == 'function') { 
16360                 m.call(this);
16361                 return progressRun.defer(10, _this);
16362             } 
16363             
16364             
16365             msg = "Building Interface " + (total  - mods.length) + 
16366                     " of " + total + 
16367                     (m.name ? (' - ' + m.name) : '');
16368                         Roo.debug && Roo.log(msg);
16369             if (!this.hideProgress &&  Roo.MessageBox) { 
16370                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16371             }
16372             
16373          
16374             // is the module disabled?
16375             var disabled = (typeof(m.disabled) == 'function') ?
16376                 m.disabled.call(m.module.disabled) : m.disabled;    
16377             
16378             
16379             if (disabled) {
16380                 return progressRun(); // we do not update the display!
16381             }
16382             
16383             // now build 
16384             
16385                         
16386                         
16387             m.render();
16388             // it's 10 on top level, and 1 on others??? why...
16389             return progressRun.defer(10, _this);
16390              
16391         }
16392         progressRun.defer(1, _this);
16393      
16394         
16395         
16396     },
16397         
16398         
16399         /**
16400          * Event Object.
16401          *
16402          *
16403          */
16404         event: false, 
16405     /**
16406          * wrapper for event.on - aliased later..  
16407          * Typically use to register a event handler for register:
16408          *
16409          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16410          *
16411          */
16412     on : false
16413    
16414     
16415     
16416 });
16417
16418 Roo.XComponent.event = new Roo.util.Observable({
16419                 events : { 
16420                         /**
16421                          * @event register
16422                          * Fires when an Component is registered,
16423                          * set the disable property on the Component to stop registration.
16424                          * @param {Roo.XComponent} c the component being registerd.
16425                          * 
16426                          */
16427                         'register' : true,
16428             /**
16429                          * @event beforebuild
16430                          * Fires before each Component is built
16431                          * can be used to apply permissions.
16432                          * @param {Roo.XComponent} c the component being registerd.
16433                          * 
16434                          */
16435                         'beforebuild' : true,
16436                         /**
16437                          * @event buildcomplete
16438                          * Fires on the top level element when all elements have been built
16439                          * @param {Roo.XComponent} the top level component.
16440                          */
16441                         'buildcomplete' : true
16442                         
16443                 }
16444 });
16445
16446 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16447  /*
16448  * Based on:
16449  * Ext JS Library 1.1.1
16450  * Copyright(c) 2006-2007, Ext JS, LLC.
16451  *
16452  * Originally Released Under LGPL - original licence link has changed is not relivant.
16453  *
16454  * Fork - LGPL
16455  * <script type="text/javascript">
16456  */
16457
16458
16459
16460 /*
16461  * These classes are derivatives of the similarly named classes in the YUI Library.
16462  * The original license:
16463  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16464  * Code licensed under the BSD License:
16465  * http://developer.yahoo.net/yui/license.txt
16466  */
16467
16468 (function() {
16469
16470 var Event=Roo.EventManager;
16471 var Dom=Roo.lib.Dom;
16472
16473 /**
16474  * @class Roo.dd.DragDrop
16475  * @extends Roo.util.Observable
16476  * Defines the interface and base operation of items that that can be
16477  * dragged or can be drop targets.  It was designed to be extended, overriding
16478  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16479  * Up to three html elements can be associated with a DragDrop instance:
16480  * <ul>
16481  * <li>linked element: the element that is passed into the constructor.
16482  * This is the element which defines the boundaries for interaction with
16483  * other DragDrop objects.</li>
16484  * <li>handle element(s): The drag operation only occurs if the element that
16485  * was clicked matches a handle element.  By default this is the linked
16486  * element, but there are times that you will want only a portion of the
16487  * linked element to initiate the drag operation, and the setHandleElId()
16488  * method provides a way to define this.</li>
16489  * <li>drag element: this represents the element that would be moved along
16490  * with the cursor during a drag operation.  By default, this is the linked
16491  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16492  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16493  * </li>
16494  * </ul>
16495  * This class should not be instantiated until the onload event to ensure that
16496  * the associated elements are available.
16497  * The following would define a DragDrop obj that would interact with any
16498  * other DragDrop obj in the "group1" group:
16499  * <pre>
16500  *  dd = new Roo.dd.DragDrop("div1", "group1");
16501  * </pre>
16502  * Since none of the event handlers have been implemented, nothing would
16503  * actually happen if you were to run the code above.  Normally you would
16504  * override this class or one of the default implementations, but you can
16505  * also override the methods you want on an instance of the class...
16506  * <pre>
16507  *  dd.onDragDrop = function(e, id) {
16508  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16509  *  }
16510  * </pre>
16511  * @constructor
16512  * @param {String} id of the element that is linked to this instance
16513  * @param {String} sGroup the group of related DragDrop objects
16514  * @param {object} config an object containing configurable attributes
16515  *                Valid properties for DragDrop:
16516  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16517  */
16518 Roo.dd.DragDrop = function(id, sGroup, config) {
16519     if (id) {
16520         this.init(id, sGroup, config);
16521     }
16522     
16523 };
16524
16525 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16526
16527     /**
16528      * The id of the element associated with this object.  This is what we
16529      * refer to as the "linked element" because the size and position of
16530      * this element is used to determine when the drag and drop objects have
16531      * interacted.
16532      * @property id
16533      * @type String
16534      */
16535     id: null,
16536
16537     /**
16538      * Configuration attributes passed into the constructor
16539      * @property config
16540      * @type object
16541      */
16542     config: null,
16543
16544     /**
16545      * The id of the element that will be dragged.  By default this is same
16546      * as the linked element , but could be changed to another element. Ex:
16547      * Roo.dd.DDProxy
16548      * @property dragElId
16549      * @type String
16550      * @private
16551      */
16552     dragElId: null,
16553
16554     /**
16555      * the id of the element that initiates the drag operation.  By default
16556      * this is the linked element, but could be changed to be a child of this
16557      * element.  This lets us do things like only starting the drag when the
16558      * header element within the linked html element is clicked.
16559      * @property handleElId
16560      * @type String
16561      * @private
16562      */
16563     handleElId: null,
16564
16565     /**
16566      * An associative array of HTML tags that will be ignored if clicked.
16567      * @property invalidHandleTypes
16568      * @type {string: string}
16569      */
16570     invalidHandleTypes: null,
16571
16572     /**
16573      * An associative array of ids for elements that will be ignored if clicked
16574      * @property invalidHandleIds
16575      * @type {string: string}
16576      */
16577     invalidHandleIds: null,
16578
16579     /**
16580      * An indexted array of css class names for elements that will be ignored
16581      * if clicked.
16582      * @property invalidHandleClasses
16583      * @type string[]
16584      */
16585     invalidHandleClasses: null,
16586
16587     /**
16588      * The linked element's absolute X position at the time the drag was
16589      * started
16590      * @property startPageX
16591      * @type int
16592      * @private
16593      */
16594     startPageX: 0,
16595
16596     /**
16597      * The linked element's absolute X position at the time the drag was
16598      * started
16599      * @property startPageY
16600      * @type int
16601      * @private
16602      */
16603     startPageY: 0,
16604
16605     /**
16606      * The group defines a logical collection of DragDrop objects that are
16607      * related.  Instances only get events when interacting with other
16608      * DragDrop object in the same group.  This lets us define multiple
16609      * groups using a single DragDrop subclass if we want.
16610      * @property groups
16611      * @type {string: string}
16612      */
16613     groups: null,
16614
16615     /**
16616      * Individual drag/drop instances can be locked.  This will prevent
16617      * onmousedown start drag.
16618      * @property locked
16619      * @type boolean
16620      * @private
16621      */
16622     locked: false,
16623
16624     /**
16625      * Lock this instance
16626      * @method lock
16627      */
16628     lock: function() { this.locked = true; },
16629
16630     /**
16631      * Unlock this instace
16632      * @method unlock
16633      */
16634     unlock: function() { this.locked = false; },
16635
16636     /**
16637      * By default, all insances can be a drop target.  This can be disabled by
16638      * setting isTarget to false.
16639      * @method isTarget
16640      * @type boolean
16641      */
16642     isTarget: true,
16643
16644     /**
16645      * The padding configured for this drag and drop object for calculating
16646      * the drop zone intersection with this object.
16647      * @method padding
16648      * @type int[]
16649      */
16650     padding: null,
16651
16652     /**
16653      * Cached reference to the linked element
16654      * @property _domRef
16655      * @private
16656      */
16657     _domRef: null,
16658
16659     /**
16660      * Internal typeof flag
16661      * @property __ygDragDrop
16662      * @private
16663      */
16664     __ygDragDrop: true,
16665
16666     /**
16667      * Set to true when horizontal contraints are applied
16668      * @property constrainX
16669      * @type boolean
16670      * @private
16671      */
16672     constrainX: false,
16673
16674     /**
16675      * Set to true when vertical contraints are applied
16676      * @property constrainY
16677      * @type boolean
16678      * @private
16679      */
16680     constrainY: false,
16681
16682     /**
16683      * The left constraint
16684      * @property minX
16685      * @type int
16686      * @private
16687      */
16688     minX: 0,
16689
16690     /**
16691      * The right constraint
16692      * @property maxX
16693      * @type int
16694      * @private
16695      */
16696     maxX: 0,
16697
16698     /**
16699      * The up constraint
16700      * @property minY
16701      * @type int
16702      * @type int
16703      * @private
16704      */
16705     minY: 0,
16706
16707     /**
16708      * The down constraint
16709      * @property maxY
16710      * @type int
16711      * @private
16712      */
16713     maxY: 0,
16714
16715     /**
16716      * Maintain offsets when we resetconstraints.  Set to true when you want
16717      * the position of the element relative to its parent to stay the same
16718      * when the page changes
16719      *
16720      * @property maintainOffset
16721      * @type boolean
16722      */
16723     maintainOffset: false,
16724
16725     /**
16726      * Array of pixel locations the element will snap to if we specified a
16727      * horizontal graduation/interval.  This array is generated automatically
16728      * when you define a tick interval.
16729      * @property xTicks
16730      * @type int[]
16731      */
16732     xTicks: null,
16733
16734     /**
16735      * Array of pixel locations the element will snap to if we specified a
16736      * vertical graduation/interval.  This array is generated automatically
16737      * when you define a tick interval.
16738      * @property yTicks
16739      * @type int[]
16740      */
16741     yTicks: null,
16742
16743     /**
16744      * By default the drag and drop instance will only respond to the primary
16745      * button click (left button for a right-handed mouse).  Set to true to
16746      * allow drag and drop to start with any mouse click that is propogated
16747      * by the browser
16748      * @property primaryButtonOnly
16749      * @type boolean
16750      */
16751     primaryButtonOnly: true,
16752
16753     /**
16754      * The availabe property is false until the linked dom element is accessible.
16755      * @property available
16756      * @type boolean
16757      */
16758     available: false,
16759
16760     /**
16761      * By default, drags can only be initiated if the mousedown occurs in the
16762      * region the linked element is.  This is done in part to work around a
16763      * bug in some browsers that mis-report the mousedown if the previous
16764      * mouseup happened outside of the window.  This property is set to true
16765      * if outer handles are defined.
16766      *
16767      * @property hasOuterHandles
16768      * @type boolean
16769      * @default false
16770      */
16771     hasOuterHandles: false,
16772
16773     /**
16774      * Code that executes immediately before the startDrag event
16775      * @method b4StartDrag
16776      * @private
16777      */
16778     b4StartDrag: function(x, y) { },
16779
16780     /**
16781      * Abstract method called after a drag/drop object is clicked
16782      * and the drag or mousedown time thresholds have beeen met.
16783      * @method startDrag
16784      * @param {int} X click location
16785      * @param {int} Y click location
16786      */
16787     startDrag: function(x, y) { /* override this */ },
16788
16789     /**
16790      * Code that executes immediately before the onDrag event
16791      * @method b4Drag
16792      * @private
16793      */
16794     b4Drag: function(e) { },
16795
16796     /**
16797      * Abstract method called during the onMouseMove event while dragging an
16798      * object.
16799      * @method onDrag
16800      * @param {Event} e the mousemove event
16801      */
16802     onDrag: function(e) { /* override this */ },
16803
16804     /**
16805      * Abstract method called when this element fist begins hovering over
16806      * another DragDrop obj
16807      * @method onDragEnter
16808      * @param {Event} e the mousemove event
16809      * @param {String|DragDrop[]} id In POINT mode, the element
16810      * id this is hovering over.  In INTERSECT mode, an array of one or more
16811      * dragdrop items being hovered over.
16812      */
16813     onDragEnter: function(e, id) { /* override this */ },
16814
16815     /**
16816      * Code that executes immediately before the onDragOver event
16817      * @method b4DragOver
16818      * @private
16819      */
16820     b4DragOver: function(e) { },
16821
16822     /**
16823      * Abstract method called when this element is hovering over another
16824      * DragDrop obj
16825      * @method onDragOver
16826      * @param {Event} e the mousemove event
16827      * @param {String|DragDrop[]} id In POINT mode, the element
16828      * id this is hovering over.  In INTERSECT mode, an array of dd items
16829      * being hovered over.
16830      */
16831     onDragOver: function(e, id) { /* override this */ },
16832
16833     /**
16834      * Code that executes immediately before the onDragOut event
16835      * @method b4DragOut
16836      * @private
16837      */
16838     b4DragOut: function(e) { },
16839
16840     /**
16841      * Abstract method called when we are no longer hovering over an element
16842      * @method onDragOut
16843      * @param {Event} e the mousemove event
16844      * @param {String|DragDrop[]} id In POINT mode, the element
16845      * id this was hovering over.  In INTERSECT mode, an array of dd items
16846      * that the mouse is no longer over.
16847      */
16848     onDragOut: function(e, id) { /* override this */ },
16849
16850     /**
16851      * Code that executes immediately before the onDragDrop event
16852      * @method b4DragDrop
16853      * @private
16854      */
16855     b4DragDrop: function(e) { },
16856
16857     /**
16858      * Abstract method called when this item is dropped on another DragDrop
16859      * obj
16860      * @method onDragDrop
16861      * @param {Event} e the mouseup event
16862      * @param {String|DragDrop[]} id In POINT mode, the element
16863      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16864      * was dropped on.
16865      */
16866     onDragDrop: function(e, id) { /* override this */ },
16867
16868     /**
16869      * Abstract method called when this item is dropped on an area with no
16870      * drop target
16871      * @method onInvalidDrop
16872      * @param {Event} e the mouseup event
16873      */
16874     onInvalidDrop: function(e) { /* override this */ },
16875
16876     /**
16877      * Code that executes immediately before the endDrag event
16878      * @method b4EndDrag
16879      * @private
16880      */
16881     b4EndDrag: function(e) { },
16882
16883     /**
16884      * Fired when we are done dragging the object
16885      * @method endDrag
16886      * @param {Event} e the mouseup event
16887      */
16888     endDrag: function(e) { /* override this */ },
16889
16890     /**
16891      * Code executed immediately before the onMouseDown event
16892      * @method b4MouseDown
16893      * @param {Event} e the mousedown event
16894      * @private
16895      */
16896     b4MouseDown: function(e) {  },
16897
16898     /**
16899      * Event handler that fires when a drag/drop obj gets a mousedown
16900      * @method onMouseDown
16901      * @param {Event} e the mousedown event
16902      */
16903     onMouseDown: function(e) { /* override this */ },
16904
16905     /**
16906      * Event handler that fires when a drag/drop obj gets a mouseup
16907      * @method onMouseUp
16908      * @param {Event} e the mouseup event
16909      */
16910     onMouseUp: function(e) { /* override this */ },
16911
16912     /**
16913      * Override the onAvailable method to do what is needed after the initial
16914      * position was determined.
16915      * @method onAvailable
16916      */
16917     onAvailable: function () {
16918     },
16919
16920     /*
16921      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16922      * @type Object
16923      */
16924     defaultPadding : {left:0, right:0, top:0, bottom:0},
16925
16926     /*
16927      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16928  *
16929  * Usage:
16930  <pre><code>
16931  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16932                 { dragElId: "existingProxyDiv" });
16933  dd.startDrag = function(){
16934      this.constrainTo("parent-id");
16935  };
16936  </code></pre>
16937  * Or you can initalize it using the {@link Roo.Element} object:
16938  <pre><code>
16939  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16940      startDrag : function(){
16941          this.constrainTo("parent-id");
16942      }
16943  });
16944  </code></pre>
16945      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16946      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16947      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16948      * an object containing the sides to pad. For example: {right:10, bottom:10}
16949      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16950      */
16951     constrainTo : function(constrainTo, pad, inContent){
16952         if(typeof pad == "number"){
16953             pad = {left: pad, right:pad, top:pad, bottom:pad};
16954         }
16955         pad = pad || this.defaultPadding;
16956         var b = Roo.get(this.getEl()).getBox();
16957         var ce = Roo.get(constrainTo);
16958         var s = ce.getScroll();
16959         var c, cd = ce.dom;
16960         if(cd == document.body){
16961             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16962         }else{
16963             xy = ce.getXY();
16964             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16965         }
16966
16967
16968         var topSpace = b.y - c.y;
16969         var leftSpace = b.x - c.x;
16970
16971         this.resetConstraints();
16972         this.setXConstraint(leftSpace - (pad.left||0), // left
16973                 c.width - leftSpace - b.width - (pad.right||0) //right
16974         );
16975         this.setYConstraint(topSpace - (pad.top||0), //top
16976                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16977         );
16978     },
16979
16980     /**
16981      * Returns a reference to the linked element
16982      * @method getEl
16983      * @return {HTMLElement} the html element
16984      */
16985     getEl: function() {
16986         if (!this._domRef) {
16987             this._domRef = Roo.getDom(this.id);
16988         }
16989
16990         return this._domRef;
16991     },
16992
16993     /**
16994      * Returns a reference to the actual element to drag.  By default this is
16995      * the same as the html element, but it can be assigned to another
16996      * element. An example of this can be found in Roo.dd.DDProxy
16997      * @method getDragEl
16998      * @return {HTMLElement} the html element
16999      */
17000     getDragEl: function() {
17001         return Roo.getDom(this.dragElId);
17002     },
17003
17004     /**
17005      * Sets up the DragDrop object.  Must be called in the constructor of any
17006      * Roo.dd.DragDrop subclass
17007      * @method init
17008      * @param id the id of the linked element
17009      * @param {String} sGroup the group of related items
17010      * @param {object} config configuration attributes
17011      */
17012     init: function(id, sGroup, config) {
17013         this.initTarget(id, sGroup, config);
17014         if (!Roo.isTouch) {
17015             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17016         }
17017         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17018         // Event.on(this.id, "selectstart", Event.preventDefault);
17019     },
17020
17021     /**
17022      * Initializes Targeting functionality only... the object does not
17023      * get a mousedown handler.
17024      * @method initTarget
17025      * @param id the id of the linked element
17026      * @param {String} sGroup the group of related items
17027      * @param {object} config configuration attributes
17028      */
17029     initTarget: function(id, sGroup, config) {
17030
17031         // configuration attributes
17032         this.config = config || {};
17033
17034         // create a local reference to the drag and drop manager
17035         this.DDM = Roo.dd.DDM;
17036         // initialize the groups array
17037         this.groups = {};
17038
17039         // assume that we have an element reference instead of an id if the
17040         // parameter is not a string
17041         if (typeof id !== "string") {
17042             id = Roo.id(id);
17043         }
17044
17045         // set the id
17046         this.id = id;
17047
17048         // add to an interaction group
17049         this.addToGroup((sGroup) ? sGroup : "default");
17050
17051         // We don't want to register this as the handle with the manager
17052         // so we just set the id rather than calling the setter.
17053         this.handleElId = id;
17054
17055         // the linked element is the element that gets dragged by default
17056         this.setDragElId(id);
17057
17058         // by default, clicked anchors will not start drag operations.
17059         this.invalidHandleTypes = { A: "A" };
17060         this.invalidHandleIds = {};
17061         this.invalidHandleClasses = [];
17062
17063         this.applyConfig();
17064
17065         this.handleOnAvailable();
17066     },
17067
17068     /**
17069      * Applies the configuration parameters that were passed into the constructor.
17070      * This is supposed to happen at each level through the inheritance chain.  So
17071      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17072      * DragDrop in order to get all of the parameters that are available in
17073      * each object.
17074      * @method applyConfig
17075      */
17076     applyConfig: function() {
17077
17078         // configurable properties:
17079         //    padding, isTarget, maintainOffset, primaryButtonOnly
17080         this.padding           = this.config.padding || [0, 0, 0, 0];
17081         this.isTarget          = (this.config.isTarget !== false);
17082         this.maintainOffset    = (this.config.maintainOffset);
17083         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17084
17085     },
17086
17087     /**
17088      * Executed when the linked element is available
17089      * @method handleOnAvailable
17090      * @private
17091      */
17092     handleOnAvailable: function() {
17093         this.available = true;
17094         this.resetConstraints();
17095         this.onAvailable();
17096     },
17097
17098      /**
17099      * Configures the padding for the target zone in px.  Effectively expands
17100      * (or reduces) the virtual object size for targeting calculations.
17101      * Supports css-style shorthand; if only one parameter is passed, all sides
17102      * will have that padding, and if only two are passed, the top and bottom
17103      * will have the first param, the left and right the second.
17104      * @method setPadding
17105      * @param {int} iTop    Top pad
17106      * @param {int} iRight  Right pad
17107      * @param {int} iBot    Bot pad
17108      * @param {int} iLeft   Left pad
17109      */
17110     setPadding: function(iTop, iRight, iBot, iLeft) {
17111         // this.padding = [iLeft, iRight, iTop, iBot];
17112         if (!iRight && 0 !== iRight) {
17113             this.padding = [iTop, iTop, iTop, iTop];
17114         } else if (!iBot && 0 !== iBot) {
17115             this.padding = [iTop, iRight, iTop, iRight];
17116         } else {
17117             this.padding = [iTop, iRight, iBot, iLeft];
17118         }
17119     },
17120
17121     /**
17122      * Stores the initial placement of the linked element.
17123      * @method setInitialPosition
17124      * @param {int} diffX   the X offset, default 0
17125      * @param {int} diffY   the Y offset, default 0
17126      */
17127     setInitPosition: function(diffX, diffY) {
17128         var el = this.getEl();
17129
17130         if (!this.DDM.verifyEl(el)) {
17131             return;
17132         }
17133
17134         var dx = diffX || 0;
17135         var dy = diffY || 0;
17136
17137         var p = Dom.getXY( el );
17138
17139         this.initPageX = p[0] - dx;
17140         this.initPageY = p[1] - dy;
17141
17142         this.lastPageX = p[0];
17143         this.lastPageY = p[1];
17144
17145
17146         this.setStartPosition(p);
17147     },
17148
17149     /**
17150      * Sets the start position of the element.  This is set when the obj
17151      * is initialized, the reset when a drag is started.
17152      * @method setStartPosition
17153      * @param pos current position (from previous lookup)
17154      * @private
17155      */
17156     setStartPosition: function(pos) {
17157         var p = pos || Dom.getXY( this.getEl() );
17158         this.deltaSetXY = null;
17159
17160         this.startPageX = p[0];
17161         this.startPageY = p[1];
17162     },
17163
17164     /**
17165      * Add this instance to a group of related drag/drop objects.  All
17166      * instances belong to at least one group, and can belong to as many
17167      * groups as needed.
17168      * @method addToGroup
17169      * @param sGroup {string} the name of the group
17170      */
17171     addToGroup: function(sGroup) {
17172         this.groups[sGroup] = true;
17173         this.DDM.regDragDrop(this, sGroup);
17174     },
17175
17176     /**
17177      * Remove's this instance from the supplied interaction group
17178      * @method removeFromGroup
17179      * @param {string}  sGroup  The group to drop
17180      */
17181     removeFromGroup: function(sGroup) {
17182         if (this.groups[sGroup]) {
17183             delete this.groups[sGroup];
17184         }
17185
17186         this.DDM.removeDDFromGroup(this, sGroup);
17187     },
17188
17189     /**
17190      * Allows you to specify that an element other than the linked element
17191      * will be moved with the cursor during a drag
17192      * @method setDragElId
17193      * @param id {string} the id of the element that will be used to initiate the drag
17194      */
17195     setDragElId: function(id) {
17196         this.dragElId = id;
17197     },
17198
17199     /**
17200      * Allows you to specify a child of the linked element that should be
17201      * used to initiate the drag operation.  An example of this would be if
17202      * you have a content div with text and links.  Clicking anywhere in the
17203      * content area would normally start the drag operation.  Use this method
17204      * to specify that an element inside of the content div is the element
17205      * that starts the drag operation.
17206      * @method setHandleElId
17207      * @param id {string} the id of the element that will be used to
17208      * initiate the drag.
17209      */
17210     setHandleElId: function(id) {
17211         if (typeof id !== "string") {
17212             id = Roo.id(id);
17213         }
17214         this.handleElId = id;
17215         this.DDM.regHandle(this.id, id);
17216     },
17217
17218     /**
17219      * Allows you to set an element outside of the linked element as a drag
17220      * handle
17221      * @method setOuterHandleElId
17222      * @param id the id of the element that will be used to initiate the drag
17223      */
17224     setOuterHandleElId: function(id) {
17225         if (typeof id !== "string") {
17226             id = Roo.id(id);
17227         }
17228         Event.on(id, "mousedown",
17229                 this.handleMouseDown, this);
17230         this.setHandleElId(id);
17231
17232         this.hasOuterHandles = true;
17233     },
17234
17235     /**
17236      * Remove all drag and drop hooks for this element
17237      * @method unreg
17238      */
17239     unreg: function() {
17240         Event.un(this.id, "mousedown",
17241                 this.handleMouseDown);
17242         Event.un(this.id, "touchstart",
17243                 this.handleMouseDown);
17244         this._domRef = null;
17245         this.DDM._remove(this);
17246     },
17247
17248     destroy : function(){
17249         this.unreg();
17250     },
17251
17252     /**
17253      * Returns true if this instance is locked, or the drag drop mgr is locked
17254      * (meaning that all drag/drop is disabled on the page.)
17255      * @method isLocked
17256      * @return {boolean} true if this obj or all drag/drop is locked, else
17257      * false
17258      */
17259     isLocked: function() {
17260         return (this.DDM.isLocked() || this.locked);
17261     },
17262
17263     /**
17264      * Fired when this object is clicked
17265      * @method handleMouseDown
17266      * @param {Event} e
17267      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17268      * @private
17269      */
17270     handleMouseDown: function(e, oDD){
17271      
17272         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17273             //Roo.log('not touch/ button !=0');
17274             return;
17275         }
17276         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17277             return; // double touch..
17278         }
17279         
17280
17281         if (this.isLocked()) {
17282             //Roo.log('locked');
17283             return;
17284         }
17285
17286         this.DDM.refreshCache(this.groups);
17287 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17288         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17289         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17290             //Roo.log('no outer handes or not over target');
17291                 // do nothing.
17292         } else {
17293 //            Roo.log('check validator');
17294             if (this.clickValidator(e)) {
17295 //                Roo.log('validate success');
17296                 // set the initial element position
17297                 this.setStartPosition();
17298
17299
17300                 this.b4MouseDown(e);
17301                 this.onMouseDown(e);
17302
17303                 this.DDM.handleMouseDown(e, this);
17304
17305                 this.DDM.stopEvent(e);
17306             } else {
17307
17308
17309             }
17310         }
17311     },
17312
17313     clickValidator: function(e) {
17314         var target = e.getTarget();
17315         return ( this.isValidHandleChild(target) &&
17316                     (this.id == this.handleElId ||
17317                         this.DDM.handleWasClicked(target, this.id)) );
17318     },
17319
17320     /**
17321      * Allows you to specify a tag name that should not start a drag operation
17322      * when clicked.  This is designed to facilitate embedding links within a
17323      * drag handle that do something other than start the drag.
17324      * @method addInvalidHandleType
17325      * @param {string} tagName the type of element to exclude
17326      */
17327     addInvalidHandleType: function(tagName) {
17328         var type = tagName.toUpperCase();
17329         this.invalidHandleTypes[type] = type;
17330     },
17331
17332     /**
17333      * Lets you to specify an element id for a child of a drag handle
17334      * that should not initiate a drag
17335      * @method addInvalidHandleId
17336      * @param {string} id the element id of the element you wish to ignore
17337      */
17338     addInvalidHandleId: function(id) {
17339         if (typeof id !== "string") {
17340             id = Roo.id(id);
17341         }
17342         this.invalidHandleIds[id] = id;
17343     },
17344
17345     /**
17346      * Lets you specify a css class of elements that will not initiate a drag
17347      * @method addInvalidHandleClass
17348      * @param {string} cssClass the class of the elements you wish to ignore
17349      */
17350     addInvalidHandleClass: function(cssClass) {
17351         this.invalidHandleClasses.push(cssClass);
17352     },
17353
17354     /**
17355      * Unsets an excluded tag name set by addInvalidHandleType
17356      * @method removeInvalidHandleType
17357      * @param {string} tagName the type of element to unexclude
17358      */
17359     removeInvalidHandleType: function(tagName) {
17360         var type = tagName.toUpperCase();
17361         // this.invalidHandleTypes[type] = null;
17362         delete this.invalidHandleTypes[type];
17363     },
17364
17365     /**
17366      * Unsets an invalid handle id
17367      * @method removeInvalidHandleId
17368      * @param {string} id the id of the element to re-enable
17369      */
17370     removeInvalidHandleId: function(id) {
17371         if (typeof id !== "string") {
17372             id = Roo.id(id);
17373         }
17374         delete this.invalidHandleIds[id];
17375     },
17376
17377     /**
17378      * Unsets an invalid css class
17379      * @method removeInvalidHandleClass
17380      * @param {string} cssClass the class of the element(s) you wish to
17381      * re-enable
17382      */
17383     removeInvalidHandleClass: function(cssClass) {
17384         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17385             if (this.invalidHandleClasses[i] == cssClass) {
17386                 delete this.invalidHandleClasses[i];
17387             }
17388         }
17389     },
17390
17391     /**
17392      * Checks the tag exclusion list to see if this click should be ignored
17393      * @method isValidHandleChild
17394      * @param {HTMLElement} node the HTMLElement to evaluate
17395      * @return {boolean} true if this is a valid tag type, false if not
17396      */
17397     isValidHandleChild: function(node) {
17398
17399         var valid = true;
17400         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17401         var nodeName;
17402         try {
17403             nodeName = node.nodeName.toUpperCase();
17404         } catch(e) {
17405             nodeName = node.nodeName;
17406         }
17407         valid = valid && !this.invalidHandleTypes[nodeName];
17408         valid = valid && !this.invalidHandleIds[node.id];
17409
17410         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17411             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17412         }
17413
17414
17415         return valid;
17416
17417     },
17418
17419     /**
17420      * Create the array of horizontal tick marks if an interval was specified
17421      * in setXConstraint().
17422      * @method setXTicks
17423      * @private
17424      */
17425     setXTicks: function(iStartX, iTickSize) {
17426         this.xTicks = [];
17427         this.xTickSize = iTickSize;
17428
17429         var tickMap = {};
17430
17431         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17432             if (!tickMap[i]) {
17433                 this.xTicks[this.xTicks.length] = i;
17434                 tickMap[i] = true;
17435             }
17436         }
17437
17438         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17439             if (!tickMap[i]) {
17440                 this.xTicks[this.xTicks.length] = i;
17441                 tickMap[i] = true;
17442             }
17443         }
17444
17445         this.xTicks.sort(this.DDM.numericSort) ;
17446     },
17447
17448     /**
17449      * Create the array of vertical tick marks if an interval was specified in
17450      * setYConstraint().
17451      * @method setYTicks
17452      * @private
17453      */
17454     setYTicks: function(iStartY, iTickSize) {
17455         this.yTicks = [];
17456         this.yTickSize = iTickSize;
17457
17458         var tickMap = {};
17459
17460         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17461             if (!tickMap[i]) {
17462                 this.yTicks[this.yTicks.length] = i;
17463                 tickMap[i] = true;
17464             }
17465         }
17466
17467         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17468             if (!tickMap[i]) {
17469                 this.yTicks[this.yTicks.length] = i;
17470                 tickMap[i] = true;
17471             }
17472         }
17473
17474         this.yTicks.sort(this.DDM.numericSort) ;
17475     },
17476
17477     /**
17478      * By default, the element can be dragged any place on the screen.  Use
17479      * this method to limit the horizontal travel of the element.  Pass in
17480      * 0,0 for the parameters if you want to lock the drag to the y axis.
17481      * @method setXConstraint
17482      * @param {int} iLeft the number of pixels the element can move to the left
17483      * @param {int} iRight the number of pixels the element can move to the
17484      * right
17485      * @param {int} iTickSize optional parameter for specifying that the
17486      * element
17487      * should move iTickSize pixels at a time.
17488      */
17489     setXConstraint: function(iLeft, iRight, iTickSize) {
17490         this.leftConstraint = iLeft;
17491         this.rightConstraint = iRight;
17492
17493         this.minX = this.initPageX - iLeft;
17494         this.maxX = this.initPageX + iRight;
17495         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17496
17497         this.constrainX = true;
17498     },
17499
17500     /**
17501      * Clears any constraints applied to this instance.  Also clears ticks
17502      * since they can't exist independent of a constraint at this time.
17503      * @method clearConstraints
17504      */
17505     clearConstraints: function() {
17506         this.constrainX = false;
17507         this.constrainY = false;
17508         this.clearTicks();
17509     },
17510
17511     /**
17512      * Clears any tick interval defined for this instance
17513      * @method clearTicks
17514      */
17515     clearTicks: function() {
17516         this.xTicks = null;
17517         this.yTicks = null;
17518         this.xTickSize = 0;
17519         this.yTickSize = 0;
17520     },
17521
17522     /**
17523      * By default, the element can be dragged any place on the screen.  Set
17524      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17525      * parameters if you want to lock the drag to the x axis.
17526      * @method setYConstraint
17527      * @param {int} iUp the number of pixels the element can move up
17528      * @param {int} iDown the number of pixels the element can move down
17529      * @param {int} iTickSize optional parameter for specifying that the
17530      * element should move iTickSize pixels at a time.
17531      */
17532     setYConstraint: function(iUp, iDown, iTickSize) {
17533         this.topConstraint = iUp;
17534         this.bottomConstraint = iDown;
17535
17536         this.minY = this.initPageY - iUp;
17537         this.maxY = this.initPageY + iDown;
17538         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17539
17540         this.constrainY = true;
17541
17542     },
17543
17544     /**
17545      * resetConstraints must be called if you manually reposition a dd element.
17546      * @method resetConstraints
17547      * @param {boolean} maintainOffset
17548      */
17549     resetConstraints: function() {
17550
17551
17552         // Maintain offsets if necessary
17553         if (this.initPageX || this.initPageX === 0) {
17554             // figure out how much this thing has moved
17555             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17556             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17557
17558             this.setInitPosition(dx, dy);
17559
17560         // This is the first time we have detected the element's position
17561         } else {
17562             this.setInitPosition();
17563         }
17564
17565         if (this.constrainX) {
17566             this.setXConstraint( this.leftConstraint,
17567                                  this.rightConstraint,
17568                                  this.xTickSize        );
17569         }
17570
17571         if (this.constrainY) {
17572             this.setYConstraint( this.topConstraint,
17573                                  this.bottomConstraint,
17574                                  this.yTickSize         );
17575         }
17576     },
17577
17578     /**
17579      * Normally the drag element is moved pixel by pixel, but we can specify
17580      * that it move a number of pixels at a time.  This method resolves the
17581      * location when we have it set up like this.
17582      * @method getTick
17583      * @param {int} val where we want to place the object
17584      * @param {int[]} tickArray sorted array of valid points
17585      * @return {int} the closest tick
17586      * @private
17587      */
17588     getTick: function(val, tickArray) {
17589
17590         if (!tickArray) {
17591             // If tick interval is not defined, it is effectively 1 pixel,
17592             // so we return the value passed to us.
17593             return val;
17594         } else if (tickArray[0] >= val) {
17595             // The value is lower than the first tick, so we return the first
17596             // tick.
17597             return tickArray[0];
17598         } else {
17599             for (var i=0, len=tickArray.length; i<len; ++i) {
17600                 var next = i + 1;
17601                 if (tickArray[next] && tickArray[next] >= val) {
17602                     var diff1 = val - tickArray[i];
17603                     var diff2 = tickArray[next] - val;
17604                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17605                 }
17606             }
17607
17608             // The value is larger than the last tick, so we return the last
17609             // tick.
17610             return tickArray[tickArray.length - 1];
17611         }
17612     },
17613
17614     /**
17615      * toString method
17616      * @method toString
17617      * @return {string} string representation of the dd obj
17618      */
17619     toString: function() {
17620         return ("DragDrop " + this.id);
17621     }
17622
17623 });
17624
17625 })();
17626 /*
17627  * Based on:
17628  * Ext JS Library 1.1.1
17629  * Copyright(c) 2006-2007, Ext JS, LLC.
17630  *
17631  * Originally Released Under LGPL - original licence link has changed is not relivant.
17632  *
17633  * Fork - LGPL
17634  * <script type="text/javascript">
17635  */
17636
17637
17638 /**
17639  * The drag and drop utility provides a framework for building drag and drop
17640  * applications.  In addition to enabling drag and drop for specific elements,
17641  * the drag and drop elements are tracked by the manager class, and the
17642  * interactions between the various elements are tracked during the drag and
17643  * the implementing code is notified about these important moments.
17644  */
17645
17646 // Only load the library once.  Rewriting the manager class would orphan
17647 // existing drag and drop instances.
17648 if (!Roo.dd.DragDropMgr) {
17649
17650 /**
17651  * @class Roo.dd.DragDropMgr
17652  * DragDropMgr is a singleton that tracks the element interaction for
17653  * all DragDrop items in the window.  Generally, you will not call
17654  * this class directly, but it does have helper methods that could
17655  * be useful in your DragDrop implementations.
17656  * @singleton
17657  */
17658 Roo.dd.DragDropMgr = function() {
17659
17660     var Event = Roo.EventManager;
17661
17662     return {
17663
17664         /**
17665          * Two dimensional Array of registered DragDrop objects.  The first
17666          * dimension is the DragDrop item group, the second the DragDrop
17667          * object.
17668          * @property ids
17669          * @type {string: string}
17670          * @private
17671          * @static
17672          */
17673         ids: {},
17674
17675         /**
17676          * Array of element ids defined as drag handles.  Used to determine
17677          * if the element that generated the mousedown event is actually the
17678          * handle and not the html element itself.
17679          * @property handleIds
17680          * @type {string: string}
17681          * @private
17682          * @static
17683          */
17684         handleIds: {},
17685
17686         /**
17687          * the DragDrop object that is currently being dragged
17688          * @property dragCurrent
17689          * @type DragDrop
17690          * @private
17691          * @static
17692          **/
17693         dragCurrent: null,
17694
17695         /**
17696          * the DragDrop object(s) that are being hovered over
17697          * @property dragOvers
17698          * @type Array
17699          * @private
17700          * @static
17701          */
17702         dragOvers: {},
17703
17704         /**
17705          * the X distance between the cursor and the object being dragged
17706          * @property deltaX
17707          * @type int
17708          * @private
17709          * @static
17710          */
17711         deltaX: 0,
17712
17713         /**
17714          * the Y distance between the cursor and the object being dragged
17715          * @property deltaY
17716          * @type int
17717          * @private
17718          * @static
17719          */
17720         deltaY: 0,
17721
17722         /**
17723          * Flag to determine if we should prevent the default behavior of the
17724          * events we define. By default this is true, but this can be set to
17725          * false if you need the default behavior (not recommended)
17726          * @property preventDefault
17727          * @type boolean
17728          * @static
17729          */
17730         preventDefault: true,
17731
17732         /**
17733          * Flag to determine if we should stop the propagation of the events
17734          * we generate. This is true by default but you may want to set it to
17735          * false if the html element contains other features that require the
17736          * mouse click.
17737          * @property stopPropagation
17738          * @type boolean
17739          * @static
17740          */
17741         stopPropagation: true,
17742
17743         /**
17744          * Internal flag that is set to true when drag and drop has been
17745          * intialized
17746          * @property initialized
17747          * @private
17748          * @static
17749          */
17750         initalized: false,
17751
17752         /**
17753          * All drag and drop can be disabled.
17754          * @property locked
17755          * @private
17756          * @static
17757          */
17758         locked: false,
17759
17760         /**
17761          * Called the first time an element is registered.
17762          * @method init
17763          * @private
17764          * @static
17765          */
17766         init: function() {
17767             this.initialized = true;
17768         },
17769
17770         /**
17771          * In point mode, drag and drop interaction is defined by the
17772          * location of the cursor during the drag/drop
17773          * @property POINT
17774          * @type int
17775          * @static
17776          */
17777         POINT: 0,
17778
17779         /**
17780          * In intersect mode, drag and drop interactio nis defined by the
17781          * overlap of two or more drag and drop objects.
17782          * @property INTERSECT
17783          * @type int
17784          * @static
17785          */
17786         INTERSECT: 1,
17787
17788         /**
17789          * The current drag and drop mode.  Default: POINT
17790          * @property mode
17791          * @type int
17792          * @static
17793          */
17794         mode: 0,
17795
17796         /**
17797          * Runs method on all drag and drop objects
17798          * @method _execOnAll
17799          * @private
17800          * @static
17801          */
17802         _execOnAll: function(sMethod, args) {
17803             for (var i in this.ids) {
17804                 for (var j in this.ids[i]) {
17805                     var oDD = this.ids[i][j];
17806                     if (! this.isTypeOfDD(oDD)) {
17807                         continue;
17808                     }
17809                     oDD[sMethod].apply(oDD, args);
17810                 }
17811             }
17812         },
17813
17814         /**
17815          * Drag and drop initialization.  Sets up the global event handlers
17816          * @method _onLoad
17817          * @private
17818          * @static
17819          */
17820         _onLoad: function() {
17821
17822             this.init();
17823
17824             if (!Roo.isTouch) {
17825                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17826                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17827             }
17828             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17829             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17830             
17831             Event.on(window,   "unload",    this._onUnload, this, true);
17832             Event.on(window,   "resize",    this._onResize, this, true);
17833             // Event.on(window,   "mouseout",    this._test);
17834
17835         },
17836
17837         /**
17838          * Reset constraints on all drag and drop objs
17839          * @method _onResize
17840          * @private
17841          * @static
17842          */
17843         _onResize: function(e) {
17844             this._execOnAll("resetConstraints", []);
17845         },
17846
17847         /**
17848          * Lock all drag and drop functionality
17849          * @method lock
17850          * @static
17851          */
17852         lock: function() { this.locked = true; },
17853
17854         /**
17855          * Unlock all drag and drop functionality
17856          * @method unlock
17857          * @static
17858          */
17859         unlock: function() { this.locked = false; },
17860
17861         /**
17862          * Is drag and drop locked?
17863          * @method isLocked
17864          * @return {boolean} True if drag and drop is locked, false otherwise.
17865          * @static
17866          */
17867         isLocked: function() { return this.locked; },
17868
17869         /**
17870          * Location cache that is set for all drag drop objects when a drag is
17871          * initiated, cleared when the drag is finished.
17872          * @property locationCache
17873          * @private
17874          * @static
17875          */
17876         locationCache: {},
17877
17878         /**
17879          * Set useCache to false if you want to force object the lookup of each
17880          * drag and drop linked element constantly during a drag.
17881          * @property useCache
17882          * @type boolean
17883          * @static
17884          */
17885         useCache: true,
17886
17887         /**
17888          * The number of pixels that the mouse needs to move after the
17889          * mousedown before the drag is initiated.  Default=3;
17890          * @property clickPixelThresh
17891          * @type int
17892          * @static
17893          */
17894         clickPixelThresh: 3,
17895
17896         /**
17897          * The number of milliseconds after the mousedown event to initiate the
17898          * drag if we don't get a mouseup event. Default=1000
17899          * @property clickTimeThresh
17900          * @type int
17901          * @static
17902          */
17903         clickTimeThresh: 350,
17904
17905         /**
17906          * Flag that indicates that either the drag pixel threshold or the
17907          * mousdown time threshold has been met
17908          * @property dragThreshMet
17909          * @type boolean
17910          * @private
17911          * @static
17912          */
17913         dragThreshMet: false,
17914
17915         /**
17916          * Timeout used for the click time threshold
17917          * @property clickTimeout
17918          * @type Object
17919          * @private
17920          * @static
17921          */
17922         clickTimeout: null,
17923
17924         /**
17925          * The X position of the mousedown event stored for later use when a
17926          * drag threshold is met.
17927          * @property startX
17928          * @type int
17929          * @private
17930          * @static
17931          */
17932         startX: 0,
17933
17934         /**
17935          * The Y position of the mousedown event stored for later use when a
17936          * drag threshold is met.
17937          * @property startY
17938          * @type int
17939          * @private
17940          * @static
17941          */
17942         startY: 0,
17943
17944         /**
17945          * Each DragDrop instance must be registered with the DragDropMgr.
17946          * This is executed in DragDrop.init()
17947          * @method regDragDrop
17948          * @param {DragDrop} oDD the DragDrop object to register
17949          * @param {String} sGroup the name of the group this element belongs to
17950          * @static
17951          */
17952         regDragDrop: function(oDD, sGroup) {
17953             if (!this.initialized) { this.init(); }
17954
17955             if (!this.ids[sGroup]) {
17956                 this.ids[sGroup] = {};
17957             }
17958             this.ids[sGroup][oDD.id] = oDD;
17959         },
17960
17961         /**
17962          * Removes the supplied dd instance from the supplied group. Executed
17963          * by DragDrop.removeFromGroup, so don't call this function directly.
17964          * @method removeDDFromGroup
17965          * @private
17966          * @static
17967          */
17968         removeDDFromGroup: function(oDD, sGroup) {
17969             if (!this.ids[sGroup]) {
17970                 this.ids[sGroup] = {};
17971             }
17972
17973             var obj = this.ids[sGroup];
17974             if (obj && obj[oDD.id]) {
17975                 delete obj[oDD.id];
17976             }
17977         },
17978
17979         /**
17980          * Unregisters a drag and drop item.  This is executed in
17981          * DragDrop.unreg, use that method instead of calling this directly.
17982          * @method _remove
17983          * @private
17984          * @static
17985          */
17986         _remove: function(oDD) {
17987             for (var g in oDD.groups) {
17988                 if (g && this.ids[g][oDD.id]) {
17989                     delete this.ids[g][oDD.id];
17990                 }
17991             }
17992             delete this.handleIds[oDD.id];
17993         },
17994
17995         /**
17996          * Each DragDrop handle element must be registered.  This is done
17997          * automatically when executing DragDrop.setHandleElId()
17998          * @method regHandle
17999          * @param {String} sDDId the DragDrop id this element is a handle for
18000          * @param {String} sHandleId the id of the element that is the drag
18001          * handle
18002          * @static
18003          */
18004         regHandle: function(sDDId, sHandleId) {
18005             if (!this.handleIds[sDDId]) {
18006                 this.handleIds[sDDId] = {};
18007             }
18008             this.handleIds[sDDId][sHandleId] = sHandleId;
18009         },
18010
18011         /**
18012          * Utility function to determine if a given element has been
18013          * registered as a drag drop item.
18014          * @method isDragDrop
18015          * @param {String} id the element id to check
18016          * @return {boolean} true if this element is a DragDrop item,
18017          * false otherwise
18018          * @static
18019          */
18020         isDragDrop: function(id) {
18021             return ( this.getDDById(id) ) ? true : false;
18022         },
18023
18024         /**
18025          * Returns the drag and drop instances that are in all groups the
18026          * passed in instance belongs to.
18027          * @method getRelated
18028          * @param {DragDrop} p_oDD the obj to get related data for
18029          * @param {boolean} bTargetsOnly if true, only return targetable objs
18030          * @return {DragDrop[]} the related instances
18031          * @static
18032          */
18033         getRelated: function(p_oDD, bTargetsOnly) {
18034             var oDDs = [];
18035             for (var i in p_oDD.groups) {
18036                 for (j in this.ids[i]) {
18037                     var dd = this.ids[i][j];
18038                     if (! this.isTypeOfDD(dd)) {
18039                         continue;
18040                     }
18041                     if (!bTargetsOnly || dd.isTarget) {
18042                         oDDs[oDDs.length] = dd;
18043                     }
18044                 }
18045             }
18046
18047             return oDDs;
18048         },
18049
18050         /**
18051          * Returns true if the specified dd target is a legal target for
18052          * the specifice drag obj
18053          * @method isLegalTarget
18054          * @param {DragDrop} the drag obj
18055          * @param {DragDrop} the target
18056          * @return {boolean} true if the target is a legal target for the
18057          * dd obj
18058          * @static
18059          */
18060         isLegalTarget: function (oDD, oTargetDD) {
18061             var targets = this.getRelated(oDD, true);
18062             for (var i=0, len=targets.length;i<len;++i) {
18063                 if (targets[i].id == oTargetDD.id) {
18064                     return true;
18065                 }
18066             }
18067
18068             return false;
18069         },
18070
18071         /**
18072          * My goal is to be able to transparently determine if an object is
18073          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18074          * returns "object", oDD.constructor.toString() always returns
18075          * "DragDrop" and not the name of the subclass.  So for now it just
18076          * evaluates a well-known variable in DragDrop.
18077          * @method isTypeOfDD
18078          * @param {Object} the object to evaluate
18079          * @return {boolean} true if typeof oDD = DragDrop
18080          * @static
18081          */
18082         isTypeOfDD: function (oDD) {
18083             return (oDD && oDD.__ygDragDrop);
18084         },
18085
18086         /**
18087          * Utility function to determine if a given element has been
18088          * registered as a drag drop handle for the given Drag Drop object.
18089          * @method isHandle
18090          * @param {String} id the element id to check
18091          * @return {boolean} true if this element is a DragDrop handle, false
18092          * otherwise
18093          * @static
18094          */
18095         isHandle: function(sDDId, sHandleId) {
18096             return ( this.handleIds[sDDId] &&
18097                             this.handleIds[sDDId][sHandleId] );
18098         },
18099
18100         /**
18101          * Returns the DragDrop instance for a given id
18102          * @method getDDById
18103          * @param {String} id the id of the DragDrop object
18104          * @return {DragDrop} the drag drop object, null if it is not found
18105          * @static
18106          */
18107         getDDById: function(id) {
18108             for (var i in this.ids) {
18109                 if (this.ids[i][id]) {
18110                     return this.ids[i][id];
18111                 }
18112             }
18113             return null;
18114         },
18115
18116         /**
18117          * Fired after a registered DragDrop object gets the mousedown event.
18118          * Sets up the events required to track the object being dragged
18119          * @method handleMouseDown
18120          * @param {Event} e the event
18121          * @param oDD the DragDrop object being dragged
18122          * @private
18123          * @static
18124          */
18125         handleMouseDown: function(e, oDD) {
18126             if(Roo.QuickTips){
18127                 Roo.QuickTips.disable();
18128             }
18129             this.currentTarget = e.getTarget();
18130
18131             this.dragCurrent = oDD;
18132
18133             var el = oDD.getEl();
18134
18135             // track start position
18136             this.startX = e.getPageX();
18137             this.startY = e.getPageY();
18138
18139             this.deltaX = this.startX - el.offsetLeft;
18140             this.deltaY = this.startY - el.offsetTop;
18141
18142             this.dragThreshMet = false;
18143
18144             this.clickTimeout = setTimeout(
18145                     function() {
18146                         var DDM = Roo.dd.DDM;
18147                         DDM.startDrag(DDM.startX, DDM.startY);
18148                     },
18149                     this.clickTimeThresh );
18150         },
18151
18152         /**
18153          * Fired when either the drag pixel threshol or the mousedown hold
18154          * time threshold has been met.
18155          * @method startDrag
18156          * @param x {int} the X position of the original mousedown
18157          * @param y {int} the Y position of the original mousedown
18158          * @static
18159          */
18160         startDrag: function(x, y) {
18161             clearTimeout(this.clickTimeout);
18162             if (this.dragCurrent) {
18163                 this.dragCurrent.b4StartDrag(x, y);
18164                 this.dragCurrent.startDrag(x, y);
18165             }
18166             this.dragThreshMet = true;
18167         },
18168
18169         /**
18170          * Internal function to handle the mouseup event.  Will be invoked
18171          * from the context of the document.
18172          * @method handleMouseUp
18173          * @param {Event} e the event
18174          * @private
18175          * @static
18176          */
18177         handleMouseUp: function(e) {
18178
18179             if(Roo.QuickTips){
18180                 Roo.QuickTips.enable();
18181             }
18182             if (! this.dragCurrent) {
18183                 return;
18184             }
18185
18186             clearTimeout(this.clickTimeout);
18187
18188             if (this.dragThreshMet) {
18189                 this.fireEvents(e, true);
18190             } else {
18191             }
18192
18193             this.stopDrag(e);
18194
18195             this.stopEvent(e);
18196         },
18197
18198         /**
18199          * Utility to stop event propagation and event default, if these
18200          * features are turned on.
18201          * @method stopEvent
18202          * @param {Event} e the event as returned by this.getEvent()
18203          * @static
18204          */
18205         stopEvent: function(e){
18206             if(this.stopPropagation) {
18207                 e.stopPropagation();
18208             }
18209
18210             if (this.preventDefault) {
18211                 e.preventDefault();
18212             }
18213         },
18214
18215         /**
18216          * Internal function to clean up event handlers after the drag
18217          * operation is complete
18218          * @method stopDrag
18219          * @param {Event} e the event
18220          * @private
18221          * @static
18222          */
18223         stopDrag: function(e) {
18224             // Fire the drag end event for the item that was dragged
18225             if (this.dragCurrent) {
18226                 if (this.dragThreshMet) {
18227                     this.dragCurrent.b4EndDrag(e);
18228                     this.dragCurrent.endDrag(e);
18229                 }
18230
18231                 this.dragCurrent.onMouseUp(e);
18232             }
18233
18234             this.dragCurrent = null;
18235             this.dragOvers = {};
18236         },
18237
18238         /**
18239          * Internal function to handle the mousemove event.  Will be invoked
18240          * from the context of the html element.
18241          *
18242          * @TODO figure out what we can do about mouse events lost when the
18243          * user drags objects beyond the window boundary.  Currently we can
18244          * detect this in internet explorer by verifying that the mouse is
18245          * down during the mousemove event.  Firefox doesn't give us the
18246          * button state on the mousemove event.
18247          * @method handleMouseMove
18248          * @param {Event} e the event
18249          * @private
18250          * @static
18251          */
18252         handleMouseMove: function(e) {
18253             if (! this.dragCurrent) {
18254                 return true;
18255             }
18256
18257             // var button = e.which || e.button;
18258
18259             // check for IE mouseup outside of page boundary
18260             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18261                 this.stopEvent(e);
18262                 return this.handleMouseUp(e);
18263             }
18264
18265             if (!this.dragThreshMet) {
18266                 var diffX = Math.abs(this.startX - e.getPageX());
18267                 var diffY = Math.abs(this.startY - e.getPageY());
18268                 if (diffX > this.clickPixelThresh ||
18269                             diffY > this.clickPixelThresh) {
18270                     this.startDrag(this.startX, this.startY);
18271                 }
18272             }
18273
18274             if (this.dragThreshMet) {
18275                 this.dragCurrent.b4Drag(e);
18276                 this.dragCurrent.onDrag(e);
18277                 if(!this.dragCurrent.moveOnly){
18278                     this.fireEvents(e, false);
18279                 }
18280             }
18281
18282             this.stopEvent(e);
18283
18284             return true;
18285         },
18286
18287         /**
18288          * Iterates over all of the DragDrop elements to find ones we are
18289          * hovering over or dropping on
18290          * @method fireEvents
18291          * @param {Event} e the event
18292          * @param {boolean} isDrop is this a drop op or a mouseover op?
18293          * @private
18294          * @static
18295          */
18296         fireEvents: function(e, isDrop) {
18297             var dc = this.dragCurrent;
18298
18299             // If the user did the mouse up outside of the window, we could
18300             // get here even though we have ended the drag.
18301             if (!dc || dc.isLocked()) {
18302                 return;
18303             }
18304
18305             var pt = e.getPoint();
18306
18307             // cache the previous dragOver array
18308             var oldOvers = [];
18309
18310             var outEvts   = [];
18311             var overEvts  = [];
18312             var dropEvts  = [];
18313             var enterEvts = [];
18314
18315             // Check to see if the object(s) we were hovering over is no longer
18316             // being hovered over so we can fire the onDragOut event
18317             for (var i in this.dragOvers) {
18318
18319                 var ddo = this.dragOvers[i];
18320
18321                 if (! this.isTypeOfDD(ddo)) {
18322                     continue;
18323                 }
18324
18325                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18326                     outEvts.push( ddo );
18327                 }
18328
18329                 oldOvers[i] = true;
18330                 delete this.dragOvers[i];
18331             }
18332
18333             for (var sGroup in dc.groups) {
18334
18335                 if ("string" != typeof sGroup) {
18336                     continue;
18337                 }
18338
18339                 for (i in this.ids[sGroup]) {
18340                     var oDD = this.ids[sGroup][i];
18341                     if (! this.isTypeOfDD(oDD)) {
18342                         continue;
18343                     }
18344
18345                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18346                         if (this.isOverTarget(pt, oDD, this.mode)) {
18347                             // look for drop interactions
18348                             if (isDrop) {
18349                                 dropEvts.push( oDD );
18350                             // look for drag enter and drag over interactions
18351                             } else {
18352
18353                                 // initial drag over: dragEnter fires
18354                                 if (!oldOvers[oDD.id]) {
18355                                     enterEvts.push( oDD );
18356                                 // subsequent drag overs: dragOver fires
18357                                 } else {
18358                                     overEvts.push( oDD );
18359                                 }
18360
18361                                 this.dragOvers[oDD.id] = oDD;
18362                             }
18363                         }
18364                     }
18365                 }
18366             }
18367
18368             if (this.mode) {
18369                 if (outEvts.length) {
18370                     dc.b4DragOut(e, outEvts);
18371                     dc.onDragOut(e, outEvts);
18372                 }
18373
18374                 if (enterEvts.length) {
18375                     dc.onDragEnter(e, enterEvts);
18376                 }
18377
18378                 if (overEvts.length) {
18379                     dc.b4DragOver(e, overEvts);
18380                     dc.onDragOver(e, overEvts);
18381                 }
18382
18383                 if (dropEvts.length) {
18384                     dc.b4DragDrop(e, dropEvts);
18385                     dc.onDragDrop(e, dropEvts);
18386                 }
18387
18388             } else {
18389                 // fire dragout events
18390                 var len = 0;
18391                 for (i=0, len=outEvts.length; i<len; ++i) {
18392                     dc.b4DragOut(e, outEvts[i].id);
18393                     dc.onDragOut(e, outEvts[i].id);
18394                 }
18395
18396                 // fire enter events
18397                 for (i=0,len=enterEvts.length; i<len; ++i) {
18398                     // dc.b4DragEnter(e, oDD.id);
18399                     dc.onDragEnter(e, enterEvts[i].id);
18400                 }
18401
18402                 // fire over events
18403                 for (i=0,len=overEvts.length; i<len; ++i) {
18404                     dc.b4DragOver(e, overEvts[i].id);
18405                     dc.onDragOver(e, overEvts[i].id);
18406                 }
18407
18408                 // fire drop events
18409                 for (i=0, len=dropEvts.length; i<len; ++i) {
18410                     dc.b4DragDrop(e, dropEvts[i].id);
18411                     dc.onDragDrop(e, dropEvts[i].id);
18412                 }
18413
18414             }
18415
18416             // notify about a drop that did not find a target
18417             if (isDrop && !dropEvts.length) {
18418                 dc.onInvalidDrop(e);
18419             }
18420
18421         },
18422
18423         /**
18424          * Helper function for getting the best match from the list of drag
18425          * and drop objects returned by the drag and drop events when we are
18426          * in INTERSECT mode.  It returns either the first object that the
18427          * cursor is over, or the object that has the greatest overlap with
18428          * the dragged element.
18429          * @method getBestMatch
18430          * @param  {DragDrop[]} dds The array of drag and drop objects
18431          * targeted
18432          * @return {DragDrop}       The best single match
18433          * @static
18434          */
18435         getBestMatch: function(dds) {
18436             var winner = null;
18437             // Return null if the input is not what we expect
18438             //if (!dds || !dds.length || dds.length == 0) {
18439                // winner = null;
18440             // If there is only one item, it wins
18441             //} else if (dds.length == 1) {
18442
18443             var len = dds.length;
18444
18445             if (len == 1) {
18446                 winner = dds[0];
18447             } else {
18448                 // Loop through the targeted items
18449                 for (var i=0; i<len; ++i) {
18450                     var dd = dds[i];
18451                     // If the cursor is over the object, it wins.  If the
18452                     // cursor is over multiple matches, the first one we come
18453                     // to wins.
18454                     if (dd.cursorIsOver) {
18455                         winner = dd;
18456                         break;
18457                     // Otherwise the object with the most overlap wins
18458                     } else {
18459                         if (!winner ||
18460                             winner.overlap.getArea() < dd.overlap.getArea()) {
18461                             winner = dd;
18462                         }
18463                     }
18464                 }
18465             }
18466
18467             return winner;
18468         },
18469
18470         /**
18471          * Refreshes the cache of the top-left and bottom-right points of the
18472          * drag and drop objects in the specified group(s).  This is in the
18473          * format that is stored in the drag and drop instance, so typical
18474          * usage is:
18475          * <code>
18476          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18477          * </code>
18478          * Alternatively:
18479          * <code>
18480          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18481          * </code>
18482          * @TODO this really should be an indexed array.  Alternatively this
18483          * method could accept both.
18484          * @method refreshCache
18485          * @param {Object} groups an associative array of groups to refresh
18486          * @static
18487          */
18488         refreshCache: function(groups) {
18489             for (var sGroup in groups) {
18490                 if ("string" != typeof sGroup) {
18491                     continue;
18492                 }
18493                 for (var i in this.ids[sGroup]) {
18494                     var oDD = this.ids[sGroup][i];
18495
18496                     if (this.isTypeOfDD(oDD)) {
18497                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18498                         var loc = this.getLocation(oDD);
18499                         if (loc) {
18500                             this.locationCache[oDD.id] = loc;
18501                         } else {
18502                             delete this.locationCache[oDD.id];
18503                             // this will unregister the drag and drop object if
18504                             // the element is not in a usable state
18505                             // oDD.unreg();
18506                         }
18507                     }
18508                 }
18509             }
18510         },
18511
18512         /**
18513          * This checks to make sure an element exists and is in the DOM.  The
18514          * main purpose is to handle cases where innerHTML is used to remove
18515          * drag and drop objects from the DOM.  IE provides an 'unspecified
18516          * error' when trying to access the offsetParent of such an element
18517          * @method verifyEl
18518          * @param {HTMLElement} el the element to check
18519          * @return {boolean} true if the element looks usable
18520          * @static
18521          */
18522         verifyEl: function(el) {
18523             if (el) {
18524                 var parent;
18525                 if(Roo.isIE){
18526                     try{
18527                         parent = el.offsetParent;
18528                     }catch(e){}
18529                 }else{
18530                     parent = el.offsetParent;
18531                 }
18532                 if (parent) {
18533                     return true;
18534                 }
18535             }
18536
18537             return false;
18538         },
18539
18540         /**
18541          * Returns a Region object containing the drag and drop element's position
18542          * and size, including the padding configured for it
18543          * @method getLocation
18544          * @param {DragDrop} oDD the drag and drop object to get the
18545          *                       location for
18546          * @return {Roo.lib.Region} a Region object representing the total area
18547          *                             the element occupies, including any padding
18548          *                             the instance is configured for.
18549          * @static
18550          */
18551         getLocation: function(oDD) {
18552             if (! this.isTypeOfDD(oDD)) {
18553                 return null;
18554             }
18555
18556             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18557
18558             try {
18559                 pos= Roo.lib.Dom.getXY(el);
18560             } catch (e) { }
18561
18562             if (!pos) {
18563                 return null;
18564             }
18565
18566             x1 = pos[0];
18567             x2 = x1 + el.offsetWidth;
18568             y1 = pos[1];
18569             y2 = y1 + el.offsetHeight;
18570
18571             t = y1 - oDD.padding[0];
18572             r = x2 + oDD.padding[1];
18573             b = y2 + oDD.padding[2];
18574             l = x1 - oDD.padding[3];
18575
18576             return new Roo.lib.Region( t, r, b, l );
18577         },
18578
18579         /**
18580          * Checks the cursor location to see if it over the target
18581          * @method isOverTarget
18582          * @param {Roo.lib.Point} pt The point to evaluate
18583          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18584          * @return {boolean} true if the mouse is over the target
18585          * @private
18586          * @static
18587          */
18588         isOverTarget: function(pt, oTarget, intersect) {
18589             // use cache if available
18590             var loc = this.locationCache[oTarget.id];
18591             if (!loc || !this.useCache) {
18592                 loc = this.getLocation(oTarget);
18593                 this.locationCache[oTarget.id] = loc;
18594
18595             }
18596
18597             if (!loc) {
18598                 return false;
18599             }
18600
18601             oTarget.cursorIsOver = loc.contains( pt );
18602
18603             // DragDrop is using this as a sanity check for the initial mousedown
18604             // in this case we are done.  In POINT mode, if the drag obj has no
18605             // contraints, we are also done. Otherwise we need to evaluate the
18606             // location of the target as related to the actual location of the
18607             // dragged element.
18608             var dc = this.dragCurrent;
18609             if (!dc || !dc.getTargetCoord ||
18610                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18611                 return oTarget.cursorIsOver;
18612             }
18613
18614             oTarget.overlap = null;
18615
18616             // Get the current location of the drag element, this is the
18617             // location of the mouse event less the delta that represents
18618             // where the original mousedown happened on the element.  We
18619             // need to consider constraints and ticks as well.
18620             var pos = dc.getTargetCoord(pt.x, pt.y);
18621
18622             var el = dc.getDragEl();
18623             var curRegion = new Roo.lib.Region( pos.y,
18624                                                    pos.x + el.offsetWidth,
18625                                                    pos.y + el.offsetHeight,
18626                                                    pos.x );
18627
18628             var overlap = curRegion.intersect(loc);
18629
18630             if (overlap) {
18631                 oTarget.overlap = overlap;
18632                 return (intersect) ? true : oTarget.cursorIsOver;
18633             } else {
18634                 return false;
18635             }
18636         },
18637
18638         /**
18639          * unload event handler
18640          * @method _onUnload
18641          * @private
18642          * @static
18643          */
18644         _onUnload: function(e, me) {
18645             Roo.dd.DragDropMgr.unregAll();
18646         },
18647
18648         /**
18649          * Cleans up the drag and drop events and objects.
18650          * @method unregAll
18651          * @private
18652          * @static
18653          */
18654         unregAll: function() {
18655
18656             if (this.dragCurrent) {
18657                 this.stopDrag();
18658                 this.dragCurrent = null;
18659             }
18660
18661             this._execOnAll("unreg", []);
18662
18663             for (i in this.elementCache) {
18664                 delete this.elementCache[i];
18665             }
18666
18667             this.elementCache = {};
18668             this.ids = {};
18669         },
18670
18671         /**
18672          * A cache of DOM elements
18673          * @property elementCache
18674          * @private
18675          * @static
18676          */
18677         elementCache: {},
18678
18679         /**
18680          * Get the wrapper for the DOM element specified
18681          * @method getElWrapper
18682          * @param {String} id the id of the element to get
18683          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18684          * @private
18685          * @deprecated This wrapper isn't that useful
18686          * @static
18687          */
18688         getElWrapper: function(id) {
18689             var oWrapper = this.elementCache[id];
18690             if (!oWrapper || !oWrapper.el) {
18691                 oWrapper = this.elementCache[id] =
18692                     new this.ElementWrapper(Roo.getDom(id));
18693             }
18694             return oWrapper;
18695         },
18696
18697         /**
18698          * Returns the actual DOM element
18699          * @method getElement
18700          * @param {String} id the id of the elment to get
18701          * @return {Object} The element
18702          * @deprecated use Roo.getDom instead
18703          * @static
18704          */
18705         getElement: function(id) {
18706             return Roo.getDom(id);
18707         },
18708
18709         /**
18710          * Returns the style property for the DOM element (i.e.,
18711          * document.getElById(id).style)
18712          * @method getCss
18713          * @param {String} id the id of the elment to get
18714          * @return {Object} The style property of the element
18715          * @deprecated use Roo.getDom instead
18716          * @static
18717          */
18718         getCss: function(id) {
18719             var el = Roo.getDom(id);
18720             return (el) ? el.style : null;
18721         },
18722
18723         /**
18724          * Inner class for cached elements
18725          * @class DragDropMgr.ElementWrapper
18726          * @for DragDropMgr
18727          * @private
18728          * @deprecated
18729          */
18730         ElementWrapper: function(el) {
18731                 /**
18732                  * The element
18733                  * @property el
18734                  */
18735                 this.el = el || null;
18736                 /**
18737                  * The element id
18738                  * @property id
18739                  */
18740                 this.id = this.el && el.id;
18741                 /**
18742                  * A reference to the style property
18743                  * @property css
18744                  */
18745                 this.css = this.el && el.style;
18746             },
18747
18748         /**
18749          * Returns the X position of an html element
18750          * @method getPosX
18751          * @param el the element for which to get the position
18752          * @return {int} the X coordinate
18753          * @for DragDropMgr
18754          * @deprecated use Roo.lib.Dom.getX instead
18755          * @static
18756          */
18757         getPosX: function(el) {
18758             return Roo.lib.Dom.getX(el);
18759         },
18760
18761         /**
18762          * Returns the Y position of an html element
18763          * @method getPosY
18764          * @param el the element for which to get the position
18765          * @return {int} the Y coordinate
18766          * @deprecated use Roo.lib.Dom.getY instead
18767          * @static
18768          */
18769         getPosY: function(el) {
18770             return Roo.lib.Dom.getY(el);
18771         },
18772
18773         /**
18774          * Swap two nodes.  In IE, we use the native method, for others we
18775          * emulate the IE behavior
18776          * @method swapNode
18777          * @param n1 the first node to swap
18778          * @param n2 the other node to swap
18779          * @static
18780          */
18781         swapNode: function(n1, n2) {
18782             if (n1.swapNode) {
18783                 n1.swapNode(n2);
18784             } else {
18785                 var p = n2.parentNode;
18786                 var s = n2.nextSibling;
18787
18788                 if (s == n1) {
18789                     p.insertBefore(n1, n2);
18790                 } else if (n2 == n1.nextSibling) {
18791                     p.insertBefore(n2, n1);
18792                 } else {
18793                     n1.parentNode.replaceChild(n2, n1);
18794                     p.insertBefore(n1, s);
18795                 }
18796             }
18797         },
18798
18799         /**
18800          * Returns the current scroll position
18801          * @method getScroll
18802          * @private
18803          * @static
18804          */
18805         getScroll: function () {
18806             var t, l, dde=document.documentElement, db=document.body;
18807             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18808                 t = dde.scrollTop;
18809                 l = dde.scrollLeft;
18810             } else if (db) {
18811                 t = db.scrollTop;
18812                 l = db.scrollLeft;
18813             } else {
18814
18815             }
18816             return { top: t, left: l };
18817         },
18818
18819         /**
18820          * Returns the specified element style property
18821          * @method getStyle
18822          * @param {HTMLElement} el          the element
18823          * @param {string}      styleProp   the style property
18824          * @return {string} The value of the style property
18825          * @deprecated use Roo.lib.Dom.getStyle
18826          * @static
18827          */
18828         getStyle: function(el, styleProp) {
18829             return Roo.fly(el).getStyle(styleProp);
18830         },
18831
18832         /**
18833          * Gets the scrollTop
18834          * @method getScrollTop
18835          * @return {int} the document's scrollTop
18836          * @static
18837          */
18838         getScrollTop: function () { return this.getScroll().top; },
18839
18840         /**
18841          * Gets the scrollLeft
18842          * @method getScrollLeft
18843          * @return {int} the document's scrollTop
18844          * @static
18845          */
18846         getScrollLeft: function () { return this.getScroll().left; },
18847
18848         /**
18849          * Sets the x/y position of an element to the location of the
18850          * target element.
18851          * @method moveToEl
18852          * @param {HTMLElement} moveEl      The element to move
18853          * @param {HTMLElement} targetEl    The position reference element
18854          * @static
18855          */
18856         moveToEl: function (moveEl, targetEl) {
18857             var aCoord = Roo.lib.Dom.getXY(targetEl);
18858             Roo.lib.Dom.setXY(moveEl, aCoord);
18859         },
18860
18861         /**
18862          * Numeric array sort function
18863          * @method numericSort
18864          * @static
18865          */
18866         numericSort: function(a, b) { return (a - b); },
18867
18868         /**
18869          * Internal counter
18870          * @property _timeoutCount
18871          * @private
18872          * @static
18873          */
18874         _timeoutCount: 0,
18875
18876         /**
18877          * Trying to make the load order less important.  Without this we get
18878          * an error if this file is loaded before the Event Utility.
18879          * @method _addListeners
18880          * @private
18881          * @static
18882          */
18883         _addListeners: function() {
18884             var DDM = Roo.dd.DDM;
18885             if ( Roo.lib.Event && document ) {
18886                 DDM._onLoad();
18887             } else {
18888                 if (DDM._timeoutCount > 2000) {
18889                 } else {
18890                     setTimeout(DDM._addListeners, 10);
18891                     if (document && document.body) {
18892                         DDM._timeoutCount += 1;
18893                     }
18894                 }
18895             }
18896         },
18897
18898         /**
18899          * Recursively searches the immediate parent and all child nodes for
18900          * the handle element in order to determine wheter or not it was
18901          * clicked.
18902          * @method handleWasClicked
18903          * @param node the html element to inspect
18904          * @static
18905          */
18906         handleWasClicked: function(node, id) {
18907             if (this.isHandle(id, node.id)) {
18908                 return true;
18909             } else {
18910                 // check to see if this is a text node child of the one we want
18911                 var p = node.parentNode;
18912
18913                 while (p) {
18914                     if (this.isHandle(id, p.id)) {
18915                         return true;
18916                     } else {
18917                         p = p.parentNode;
18918                     }
18919                 }
18920             }
18921
18922             return false;
18923         }
18924
18925     };
18926
18927 }();
18928
18929 // shorter alias, save a few bytes
18930 Roo.dd.DDM = Roo.dd.DragDropMgr;
18931 Roo.dd.DDM._addListeners();
18932
18933 }/*
18934  * Based on:
18935  * Ext JS Library 1.1.1
18936  * Copyright(c) 2006-2007, Ext JS, LLC.
18937  *
18938  * Originally Released Under LGPL - original licence link has changed is not relivant.
18939  *
18940  * Fork - LGPL
18941  * <script type="text/javascript">
18942  */
18943
18944 /**
18945  * @class Roo.dd.DD
18946  * A DragDrop implementation where the linked element follows the
18947  * mouse cursor during a drag.
18948  * @extends Roo.dd.DragDrop
18949  * @constructor
18950  * @param {String} id the id of the linked element
18951  * @param {String} sGroup the group of related DragDrop items
18952  * @param {object} config an object containing configurable attributes
18953  *                Valid properties for DD:
18954  *                    scroll
18955  */
18956 Roo.dd.DD = function(id, sGroup, config) {
18957     if (id) {
18958         this.init(id, sGroup, config);
18959     }
18960 };
18961
18962 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18963
18964     /**
18965      * When set to true, the utility automatically tries to scroll the browser
18966      * window wehn a drag and drop element is dragged near the viewport boundary.
18967      * Defaults to true.
18968      * @property scroll
18969      * @type boolean
18970      */
18971     scroll: true,
18972
18973     /**
18974      * Sets the pointer offset to the distance between the linked element's top
18975      * left corner and the location the element was clicked
18976      * @method autoOffset
18977      * @param {int} iPageX the X coordinate of the click
18978      * @param {int} iPageY the Y coordinate of the click
18979      */
18980     autoOffset: function(iPageX, iPageY) {
18981         var x = iPageX - this.startPageX;
18982         var y = iPageY - this.startPageY;
18983         this.setDelta(x, y);
18984     },
18985
18986     /**
18987      * Sets the pointer offset.  You can call this directly to force the
18988      * offset to be in a particular location (e.g., pass in 0,0 to set it
18989      * to the center of the object)
18990      * @method setDelta
18991      * @param {int} iDeltaX the distance from the left
18992      * @param {int} iDeltaY the distance from the top
18993      */
18994     setDelta: function(iDeltaX, iDeltaY) {
18995         this.deltaX = iDeltaX;
18996         this.deltaY = iDeltaY;
18997     },
18998
18999     /**
19000      * Sets the drag element to the location of the mousedown or click event,
19001      * maintaining the cursor location relative to the location on the element
19002      * that was clicked.  Override this if you want to place the element in a
19003      * location other than where the cursor is.
19004      * @method setDragElPos
19005      * @param {int} iPageX the X coordinate of the mousedown or drag event
19006      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19007      */
19008     setDragElPos: function(iPageX, iPageY) {
19009         // the first time we do this, we are going to check to make sure
19010         // the element has css positioning
19011
19012         var el = this.getDragEl();
19013         this.alignElWithMouse(el, iPageX, iPageY);
19014     },
19015
19016     /**
19017      * Sets the element to the location of the mousedown or click event,
19018      * maintaining the cursor location relative to the location on the element
19019      * that was clicked.  Override this if you want to place the element in a
19020      * location other than where the cursor is.
19021      * @method alignElWithMouse
19022      * @param {HTMLElement} el the element to move
19023      * @param {int} iPageX the X coordinate of the mousedown or drag event
19024      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19025      */
19026     alignElWithMouse: function(el, iPageX, iPageY) {
19027         var oCoord = this.getTargetCoord(iPageX, iPageY);
19028         var fly = el.dom ? el : Roo.fly(el);
19029         if (!this.deltaSetXY) {
19030             var aCoord = [oCoord.x, oCoord.y];
19031             fly.setXY(aCoord);
19032             var newLeft = fly.getLeft(true);
19033             var newTop  = fly.getTop(true);
19034             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19035         } else {
19036             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19037         }
19038
19039         this.cachePosition(oCoord.x, oCoord.y);
19040         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19041         return oCoord;
19042     },
19043
19044     /**
19045      * Saves the most recent position so that we can reset the constraints and
19046      * tick marks on-demand.  We need to know this so that we can calculate the
19047      * number of pixels the element is offset from its original position.
19048      * @method cachePosition
19049      * @param iPageX the current x position (optional, this just makes it so we
19050      * don't have to look it up again)
19051      * @param iPageY the current y position (optional, this just makes it so we
19052      * don't have to look it up again)
19053      */
19054     cachePosition: function(iPageX, iPageY) {
19055         if (iPageX) {
19056             this.lastPageX = iPageX;
19057             this.lastPageY = iPageY;
19058         } else {
19059             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19060             this.lastPageX = aCoord[0];
19061             this.lastPageY = aCoord[1];
19062         }
19063     },
19064
19065     /**
19066      * Auto-scroll the window if the dragged object has been moved beyond the
19067      * visible window boundary.
19068      * @method autoScroll
19069      * @param {int} x the drag element's x position
19070      * @param {int} y the drag element's y position
19071      * @param {int} h the height of the drag element
19072      * @param {int} w the width of the drag element
19073      * @private
19074      */
19075     autoScroll: function(x, y, h, w) {
19076
19077         if (this.scroll) {
19078             // The client height
19079             var clientH = Roo.lib.Dom.getViewWidth();
19080
19081             // The client width
19082             var clientW = Roo.lib.Dom.getViewHeight();
19083
19084             // The amt scrolled down
19085             var st = this.DDM.getScrollTop();
19086
19087             // The amt scrolled right
19088             var sl = this.DDM.getScrollLeft();
19089
19090             // Location of the bottom of the element
19091             var bot = h + y;
19092
19093             // Location of the right of the element
19094             var right = w + x;
19095
19096             // The distance from the cursor to the bottom of the visible area,
19097             // adjusted so that we don't scroll if the cursor is beyond the
19098             // element drag constraints
19099             var toBot = (clientH + st - y - this.deltaY);
19100
19101             // The distance from the cursor to the right of the visible area
19102             var toRight = (clientW + sl - x - this.deltaX);
19103
19104
19105             // How close to the edge the cursor must be before we scroll
19106             // var thresh = (document.all) ? 100 : 40;
19107             var thresh = 40;
19108
19109             // How many pixels to scroll per autoscroll op.  This helps to reduce
19110             // clunky scrolling. IE is more sensitive about this ... it needs this
19111             // value to be higher.
19112             var scrAmt = (document.all) ? 80 : 30;
19113
19114             // Scroll down if we are near the bottom of the visible page and the
19115             // obj extends below the crease
19116             if ( bot > clientH && toBot < thresh ) {
19117                 window.scrollTo(sl, st + scrAmt);
19118             }
19119
19120             // Scroll up if the window is scrolled down and the top of the object
19121             // goes above the top border
19122             if ( y < st && st > 0 && y - st < thresh ) {
19123                 window.scrollTo(sl, st - scrAmt);
19124             }
19125
19126             // Scroll right if the obj is beyond the right border and the cursor is
19127             // near the border.
19128             if ( right > clientW && toRight < thresh ) {
19129                 window.scrollTo(sl + scrAmt, st);
19130             }
19131
19132             // Scroll left if the window has been scrolled to the right and the obj
19133             // extends past the left border
19134             if ( x < sl && sl > 0 && x - sl < thresh ) {
19135                 window.scrollTo(sl - scrAmt, st);
19136             }
19137         }
19138     },
19139
19140     /**
19141      * Finds the location the element should be placed if we want to move
19142      * it to where the mouse location less the click offset would place us.
19143      * @method getTargetCoord
19144      * @param {int} iPageX the X coordinate of the click
19145      * @param {int} iPageY the Y coordinate of the click
19146      * @return an object that contains the coordinates (Object.x and Object.y)
19147      * @private
19148      */
19149     getTargetCoord: function(iPageX, iPageY) {
19150
19151
19152         var x = iPageX - this.deltaX;
19153         var y = iPageY - this.deltaY;
19154
19155         if (this.constrainX) {
19156             if (x < this.minX) { x = this.minX; }
19157             if (x > this.maxX) { x = this.maxX; }
19158         }
19159
19160         if (this.constrainY) {
19161             if (y < this.minY) { y = this.minY; }
19162             if (y > this.maxY) { y = this.maxY; }
19163         }
19164
19165         x = this.getTick(x, this.xTicks);
19166         y = this.getTick(y, this.yTicks);
19167
19168
19169         return {x:x, y:y};
19170     },
19171
19172     /*
19173      * Sets up config options specific to this class. Overrides
19174      * Roo.dd.DragDrop, but all versions of this method through the
19175      * inheritance chain are called
19176      */
19177     applyConfig: function() {
19178         Roo.dd.DD.superclass.applyConfig.call(this);
19179         this.scroll = (this.config.scroll !== false);
19180     },
19181
19182     /*
19183      * Event that fires prior to the onMouseDown event.  Overrides
19184      * Roo.dd.DragDrop.
19185      */
19186     b4MouseDown: function(e) {
19187         // this.resetConstraints();
19188         this.autoOffset(e.getPageX(),
19189                             e.getPageY());
19190     },
19191
19192     /*
19193      * Event that fires prior to the onDrag event.  Overrides
19194      * Roo.dd.DragDrop.
19195      */
19196     b4Drag: function(e) {
19197         this.setDragElPos(e.getPageX(),
19198                             e.getPageY());
19199     },
19200
19201     toString: function() {
19202         return ("DD " + this.id);
19203     }
19204
19205     //////////////////////////////////////////////////////////////////////////
19206     // Debugging ygDragDrop events that can be overridden
19207     //////////////////////////////////////////////////////////////////////////
19208     /*
19209     startDrag: function(x, y) {
19210     },
19211
19212     onDrag: function(e) {
19213     },
19214
19215     onDragEnter: function(e, id) {
19216     },
19217
19218     onDragOver: function(e, id) {
19219     },
19220
19221     onDragOut: function(e, id) {
19222     },
19223
19224     onDragDrop: function(e, id) {
19225     },
19226
19227     endDrag: function(e) {
19228     }
19229
19230     */
19231
19232 });/*
19233  * Based on:
19234  * Ext JS Library 1.1.1
19235  * Copyright(c) 2006-2007, Ext JS, LLC.
19236  *
19237  * Originally Released Under LGPL - original licence link has changed is not relivant.
19238  *
19239  * Fork - LGPL
19240  * <script type="text/javascript">
19241  */
19242
19243 /**
19244  * @class Roo.dd.DDProxy
19245  * A DragDrop implementation that inserts an empty, bordered div into
19246  * the document that follows the cursor during drag operations.  At the time of
19247  * the click, the frame div is resized to the dimensions of the linked html
19248  * element, and moved to the exact location of the linked element.
19249  *
19250  * References to the "frame" element refer to the single proxy element that
19251  * was created to be dragged in place of all DDProxy elements on the
19252  * page.
19253  *
19254  * @extends Roo.dd.DD
19255  * @constructor
19256  * @param {String} id the id of the linked html element
19257  * @param {String} sGroup the group of related DragDrop objects
19258  * @param {object} config an object containing configurable attributes
19259  *                Valid properties for DDProxy in addition to those in DragDrop:
19260  *                   resizeFrame, centerFrame, dragElId
19261  */
19262 Roo.dd.DDProxy = function(id, sGroup, config) {
19263     if (id) {
19264         this.init(id, sGroup, config);
19265         this.initFrame();
19266     }
19267 };
19268
19269 /**
19270  * The default drag frame div id
19271  * @property Roo.dd.DDProxy.dragElId
19272  * @type String
19273  * @static
19274  */
19275 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19276
19277 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19278
19279     /**
19280      * By default we resize the drag frame to be the same size as the element
19281      * we want to drag (this is to get the frame effect).  We can turn it off
19282      * if we want a different behavior.
19283      * @property resizeFrame
19284      * @type boolean
19285      */
19286     resizeFrame: true,
19287
19288     /**
19289      * By default the frame is positioned exactly where the drag element is, so
19290      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19291      * you do not have constraints on the obj is to have the drag frame centered
19292      * around the cursor.  Set centerFrame to true for this effect.
19293      * @property centerFrame
19294      * @type boolean
19295      */
19296     centerFrame: false,
19297
19298     /**
19299      * Creates the proxy element if it does not yet exist
19300      * @method createFrame
19301      */
19302     createFrame: function() {
19303         var self = this;
19304         var body = document.body;
19305
19306         if (!body || !body.firstChild) {
19307             setTimeout( function() { self.createFrame(); }, 50 );
19308             return;
19309         }
19310
19311         var div = this.getDragEl();
19312
19313         if (!div) {
19314             div    = document.createElement("div");
19315             div.id = this.dragElId;
19316             var s  = div.style;
19317
19318             s.position   = "absolute";
19319             s.visibility = "hidden";
19320             s.cursor     = "move";
19321             s.border     = "2px solid #aaa";
19322             s.zIndex     = 999;
19323
19324             // appendChild can blow up IE if invoked prior to the window load event
19325             // while rendering a table.  It is possible there are other scenarios
19326             // that would cause this to happen as well.
19327             body.insertBefore(div, body.firstChild);
19328         }
19329     },
19330
19331     /**
19332      * Initialization for the drag frame element.  Must be called in the
19333      * constructor of all subclasses
19334      * @method initFrame
19335      */
19336     initFrame: function() {
19337         this.createFrame();
19338     },
19339
19340     applyConfig: function() {
19341         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19342
19343         this.resizeFrame = (this.config.resizeFrame !== false);
19344         this.centerFrame = (this.config.centerFrame);
19345         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19346     },
19347
19348     /**
19349      * Resizes the drag frame to the dimensions of the clicked object, positions
19350      * it over the object, and finally displays it
19351      * @method showFrame
19352      * @param {int} iPageX X click position
19353      * @param {int} iPageY Y click position
19354      * @private
19355      */
19356     showFrame: function(iPageX, iPageY) {
19357         var el = this.getEl();
19358         var dragEl = this.getDragEl();
19359         var s = dragEl.style;
19360
19361         this._resizeProxy();
19362
19363         if (this.centerFrame) {
19364             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19365                            Math.round(parseInt(s.height, 10)/2) );
19366         }
19367
19368         this.setDragElPos(iPageX, iPageY);
19369
19370         Roo.fly(dragEl).show();
19371     },
19372
19373     /**
19374      * The proxy is automatically resized to the dimensions of the linked
19375      * element when a drag is initiated, unless resizeFrame is set to false
19376      * @method _resizeProxy
19377      * @private
19378      */
19379     _resizeProxy: function() {
19380         if (this.resizeFrame) {
19381             var el = this.getEl();
19382             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19383         }
19384     },
19385
19386     // overrides Roo.dd.DragDrop
19387     b4MouseDown: function(e) {
19388         var x = e.getPageX();
19389         var y = e.getPageY();
19390         this.autoOffset(x, y);
19391         this.setDragElPos(x, y);
19392     },
19393
19394     // overrides Roo.dd.DragDrop
19395     b4StartDrag: function(x, y) {
19396         // show the drag frame
19397         this.showFrame(x, y);
19398     },
19399
19400     // overrides Roo.dd.DragDrop
19401     b4EndDrag: function(e) {
19402         Roo.fly(this.getDragEl()).hide();
19403     },
19404
19405     // overrides Roo.dd.DragDrop
19406     // By default we try to move the element to the last location of the frame.
19407     // This is so that the default behavior mirrors that of Roo.dd.DD.
19408     endDrag: function(e) {
19409
19410         var lel = this.getEl();
19411         var del = this.getDragEl();
19412
19413         // Show the drag frame briefly so we can get its position
19414         del.style.visibility = "";
19415
19416         this.beforeMove();
19417         // Hide the linked element before the move to get around a Safari
19418         // rendering bug.
19419         lel.style.visibility = "hidden";
19420         Roo.dd.DDM.moveToEl(lel, del);
19421         del.style.visibility = "hidden";
19422         lel.style.visibility = "";
19423
19424         this.afterDrag();
19425     },
19426
19427     beforeMove : function(){
19428
19429     },
19430
19431     afterDrag : function(){
19432
19433     },
19434
19435     toString: function() {
19436         return ("DDProxy " + this.id);
19437     }
19438
19439 });
19440 /*
19441  * Based on:
19442  * Ext JS Library 1.1.1
19443  * Copyright(c) 2006-2007, Ext JS, LLC.
19444  *
19445  * Originally Released Under LGPL - original licence link has changed is not relivant.
19446  *
19447  * Fork - LGPL
19448  * <script type="text/javascript">
19449  */
19450
19451  /**
19452  * @class Roo.dd.DDTarget
19453  * A DragDrop implementation that does not move, but can be a drop
19454  * target.  You would get the same result by simply omitting implementation
19455  * for the event callbacks, but this way we reduce the processing cost of the
19456  * event listener and the callbacks.
19457  * @extends Roo.dd.DragDrop
19458  * @constructor
19459  * @param {String} id the id of the element that is a drop target
19460  * @param {String} sGroup the group of related DragDrop objects
19461  * @param {object} config an object containing configurable attributes
19462  *                 Valid properties for DDTarget in addition to those in
19463  *                 DragDrop:
19464  *                    none
19465  */
19466 Roo.dd.DDTarget = function(id, sGroup, config) {
19467     if (id) {
19468         this.initTarget(id, sGroup, config);
19469     }
19470     if (config.listeners || config.events) { 
19471        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19472             listeners : config.listeners || {}, 
19473             events : config.events || {} 
19474         });    
19475     }
19476 };
19477
19478 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19479 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19480     toString: function() {
19481         return ("DDTarget " + this.id);
19482     }
19483 });
19484 /*
19485  * Based on:
19486  * Ext JS Library 1.1.1
19487  * Copyright(c) 2006-2007, Ext JS, LLC.
19488  *
19489  * Originally Released Under LGPL - original licence link has changed is not relivant.
19490  *
19491  * Fork - LGPL
19492  * <script type="text/javascript">
19493  */
19494  
19495
19496 /**
19497  * @class Roo.dd.ScrollManager
19498  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19499  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19500  * @singleton
19501  */
19502 Roo.dd.ScrollManager = function(){
19503     var ddm = Roo.dd.DragDropMgr;
19504     var els = {};
19505     var dragEl = null;
19506     var proc = {};
19507     
19508     
19509     
19510     var onStop = function(e){
19511         dragEl = null;
19512         clearProc();
19513     };
19514     
19515     var triggerRefresh = function(){
19516         if(ddm.dragCurrent){
19517              ddm.refreshCache(ddm.dragCurrent.groups);
19518         }
19519     };
19520     
19521     var doScroll = function(){
19522         if(ddm.dragCurrent){
19523             var dds = Roo.dd.ScrollManager;
19524             if(!dds.animate){
19525                 if(proc.el.scroll(proc.dir, dds.increment)){
19526                     triggerRefresh();
19527                 }
19528             }else{
19529                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19530             }
19531         }
19532     };
19533     
19534     var clearProc = function(){
19535         if(proc.id){
19536             clearInterval(proc.id);
19537         }
19538         proc.id = 0;
19539         proc.el = null;
19540         proc.dir = "";
19541     };
19542     
19543     var startProc = function(el, dir){
19544          Roo.log('scroll startproc');
19545         clearProc();
19546         proc.el = el;
19547         proc.dir = dir;
19548         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19549     };
19550     
19551     var onFire = function(e, isDrop){
19552        
19553         if(isDrop || !ddm.dragCurrent){ return; }
19554         var dds = Roo.dd.ScrollManager;
19555         if(!dragEl || dragEl != ddm.dragCurrent){
19556             dragEl = ddm.dragCurrent;
19557             // refresh regions on drag start
19558             dds.refreshCache();
19559         }
19560         
19561         var xy = Roo.lib.Event.getXY(e);
19562         var pt = new Roo.lib.Point(xy[0], xy[1]);
19563         for(var id in els){
19564             var el = els[id], r = el._region;
19565             if(r && r.contains(pt) && el.isScrollable()){
19566                 if(r.bottom - pt.y <= dds.thresh){
19567                     if(proc.el != el){
19568                         startProc(el, "down");
19569                     }
19570                     return;
19571                 }else if(r.right - pt.x <= dds.thresh){
19572                     if(proc.el != el){
19573                         startProc(el, "left");
19574                     }
19575                     return;
19576                 }else if(pt.y - r.top <= dds.thresh){
19577                     if(proc.el != el){
19578                         startProc(el, "up");
19579                     }
19580                     return;
19581                 }else if(pt.x - r.left <= dds.thresh){
19582                     if(proc.el != el){
19583                         startProc(el, "right");
19584                     }
19585                     return;
19586                 }
19587             }
19588         }
19589         clearProc();
19590     };
19591     
19592     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19593     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19594     
19595     return {
19596         /**
19597          * Registers new overflow element(s) to auto scroll
19598          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19599          */
19600         register : function(el){
19601             if(el instanceof Array){
19602                 for(var i = 0, len = el.length; i < len; i++) {
19603                         this.register(el[i]);
19604                 }
19605             }else{
19606                 el = Roo.get(el);
19607                 els[el.id] = el;
19608             }
19609             Roo.dd.ScrollManager.els = els;
19610         },
19611         
19612         /**
19613          * Unregisters overflow element(s) so they are no longer scrolled
19614          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19615          */
19616         unregister : function(el){
19617             if(el instanceof Array){
19618                 for(var i = 0, len = el.length; i < len; i++) {
19619                         this.unregister(el[i]);
19620                 }
19621             }else{
19622                 el = Roo.get(el);
19623                 delete els[el.id];
19624             }
19625         },
19626         
19627         /**
19628          * The number of pixels from the edge of a container the pointer needs to be to 
19629          * trigger scrolling (defaults to 25)
19630          * @type Number
19631          */
19632         thresh : 25,
19633         
19634         /**
19635          * The number of pixels to scroll in each scroll increment (defaults to 50)
19636          * @type Number
19637          */
19638         increment : 100,
19639         
19640         /**
19641          * The frequency of scrolls in milliseconds (defaults to 500)
19642          * @type Number
19643          */
19644         frequency : 500,
19645         
19646         /**
19647          * True to animate the scroll (defaults to true)
19648          * @type Boolean
19649          */
19650         animate: true,
19651         
19652         /**
19653          * The animation duration in seconds - 
19654          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19655          * @type Number
19656          */
19657         animDuration: .4,
19658         
19659         /**
19660          * Manually trigger a cache refresh.
19661          */
19662         refreshCache : function(){
19663             for(var id in els){
19664                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19665                     els[id]._region = els[id].getRegion();
19666                 }
19667             }
19668         }
19669     };
19670 }();/*
19671  * Based on:
19672  * Ext JS Library 1.1.1
19673  * Copyright(c) 2006-2007, Ext JS, LLC.
19674  *
19675  * Originally Released Under LGPL - original licence link has changed is not relivant.
19676  *
19677  * Fork - LGPL
19678  * <script type="text/javascript">
19679  */
19680  
19681
19682 /**
19683  * @class Roo.dd.Registry
19684  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19685  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19686  * @singleton
19687  */
19688 Roo.dd.Registry = function(){
19689     var elements = {}; 
19690     var handles = {}; 
19691     var autoIdSeed = 0;
19692
19693     var getId = function(el, autogen){
19694         if(typeof el == "string"){
19695             return el;
19696         }
19697         var id = el.id;
19698         if(!id && autogen !== false){
19699             id = "roodd-" + (++autoIdSeed);
19700             el.id = id;
19701         }
19702         return id;
19703     };
19704     
19705     return {
19706     /**
19707      * Register a drag drop element
19708      * @param {String|HTMLElement} element The id or DOM node to register
19709      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19710      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19711      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19712      * populated in the data object (if applicable):
19713      * <pre>
19714 Value      Description<br />
19715 ---------  ------------------------------------------<br />
19716 handles    Array of DOM nodes that trigger dragging<br />
19717            for the element being registered<br />
19718 isHandle   True if the element passed in triggers<br />
19719            dragging itself, else false
19720 </pre>
19721      */
19722         register : function(el, data){
19723             data = data || {};
19724             if(typeof el == "string"){
19725                 el = document.getElementById(el);
19726             }
19727             data.ddel = el;
19728             elements[getId(el)] = data;
19729             if(data.isHandle !== false){
19730                 handles[data.ddel.id] = data;
19731             }
19732             if(data.handles){
19733                 var hs = data.handles;
19734                 for(var i = 0, len = hs.length; i < len; i++){
19735                         handles[getId(hs[i])] = data;
19736                 }
19737             }
19738         },
19739
19740     /**
19741      * Unregister a drag drop element
19742      * @param {String|HTMLElement}  element The id or DOM node to unregister
19743      */
19744         unregister : function(el){
19745             var id = getId(el, false);
19746             var data = elements[id];
19747             if(data){
19748                 delete elements[id];
19749                 if(data.handles){
19750                     var hs = data.handles;
19751                     for(var i = 0, len = hs.length; i < len; i++){
19752                         delete handles[getId(hs[i], false)];
19753                     }
19754                 }
19755             }
19756         },
19757
19758     /**
19759      * Returns the handle registered for a DOM Node by id
19760      * @param {String|HTMLElement} id The DOM node or id to look up
19761      * @return {Object} handle The custom handle data
19762      */
19763         getHandle : function(id){
19764             if(typeof id != "string"){ // must be element?
19765                 id = id.id;
19766             }
19767             return handles[id];
19768         },
19769
19770     /**
19771      * Returns the handle that is registered for the DOM node that is the target of the event
19772      * @param {Event} e The event
19773      * @return {Object} handle The custom handle data
19774      */
19775         getHandleFromEvent : function(e){
19776             var t = Roo.lib.Event.getTarget(e);
19777             return t ? handles[t.id] : null;
19778         },
19779
19780     /**
19781      * Returns a custom data object that is registered for a DOM node by id
19782      * @param {String|HTMLElement} id The DOM node or id to look up
19783      * @return {Object} data The custom data
19784      */
19785         getTarget : function(id){
19786             if(typeof id != "string"){ // must be element?
19787                 id = id.id;
19788             }
19789             return elements[id];
19790         },
19791
19792     /**
19793      * Returns a custom data object that is registered for the DOM node that is the target of the event
19794      * @param {Event} e The event
19795      * @return {Object} data The custom data
19796      */
19797         getTargetFromEvent : function(e){
19798             var t = Roo.lib.Event.getTarget(e);
19799             return t ? elements[t.id] || handles[t.id] : null;
19800         }
19801     };
19802 }();/*
19803  * Based on:
19804  * Ext JS Library 1.1.1
19805  * Copyright(c) 2006-2007, Ext JS, LLC.
19806  *
19807  * Originally Released Under LGPL - original licence link has changed is not relivant.
19808  *
19809  * Fork - LGPL
19810  * <script type="text/javascript">
19811  */
19812  
19813
19814 /**
19815  * @class Roo.dd.StatusProxy
19816  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19817  * default drag proxy used by all Roo.dd components.
19818  * @constructor
19819  * @param {Object} config
19820  */
19821 Roo.dd.StatusProxy = function(config){
19822     Roo.apply(this, config);
19823     this.id = this.id || Roo.id();
19824     this.el = new Roo.Layer({
19825         dh: {
19826             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19827                 {tag: "div", cls: "x-dd-drop-icon"},
19828                 {tag: "div", cls: "x-dd-drag-ghost"}
19829             ]
19830         }, 
19831         shadow: !config || config.shadow !== false
19832     });
19833     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19834     this.dropStatus = this.dropNotAllowed;
19835 };
19836
19837 Roo.dd.StatusProxy.prototype = {
19838     /**
19839      * @cfg {String} dropAllowed
19840      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19841      */
19842     dropAllowed : "x-dd-drop-ok",
19843     /**
19844      * @cfg {String} dropNotAllowed
19845      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19846      */
19847     dropNotAllowed : "x-dd-drop-nodrop",
19848
19849     /**
19850      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19851      * over the current target element.
19852      * @param {String} cssClass The css class for the new drop status indicator image
19853      */
19854     setStatus : function(cssClass){
19855         cssClass = cssClass || this.dropNotAllowed;
19856         if(this.dropStatus != cssClass){
19857             this.el.replaceClass(this.dropStatus, cssClass);
19858             this.dropStatus = cssClass;
19859         }
19860     },
19861
19862     /**
19863      * Resets the status indicator to the default dropNotAllowed value
19864      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19865      */
19866     reset : function(clearGhost){
19867         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19868         this.dropStatus = this.dropNotAllowed;
19869         if(clearGhost){
19870             this.ghost.update("");
19871         }
19872     },
19873
19874     /**
19875      * Updates the contents of the ghost element
19876      * @param {String} html The html that will replace the current innerHTML of the ghost element
19877      */
19878     update : function(html){
19879         if(typeof html == "string"){
19880             this.ghost.update(html);
19881         }else{
19882             this.ghost.update("");
19883             html.style.margin = "0";
19884             this.ghost.dom.appendChild(html);
19885         }
19886         // ensure float = none set?? cant remember why though.
19887         var el = this.ghost.dom.firstChild;
19888                 if(el){
19889                         Roo.fly(el).setStyle('float', 'none');
19890                 }
19891     },
19892     
19893     /**
19894      * Returns the underlying proxy {@link Roo.Layer}
19895      * @return {Roo.Layer} el
19896     */
19897     getEl : function(){
19898         return this.el;
19899     },
19900
19901     /**
19902      * Returns the ghost element
19903      * @return {Roo.Element} el
19904      */
19905     getGhost : function(){
19906         return this.ghost;
19907     },
19908
19909     /**
19910      * Hides the proxy
19911      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19912      */
19913     hide : function(clear){
19914         this.el.hide();
19915         if(clear){
19916             this.reset(true);
19917         }
19918     },
19919
19920     /**
19921      * Stops the repair animation if it's currently running
19922      */
19923     stop : function(){
19924         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19925             this.anim.stop();
19926         }
19927     },
19928
19929     /**
19930      * Displays this proxy
19931      */
19932     show : function(){
19933         this.el.show();
19934     },
19935
19936     /**
19937      * Force the Layer to sync its shadow and shim positions to the element
19938      */
19939     sync : function(){
19940         this.el.sync();
19941     },
19942
19943     /**
19944      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19945      * invalid drop operation by the item being dragged.
19946      * @param {Array} xy The XY position of the element ([x, y])
19947      * @param {Function} callback The function to call after the repair is complete
19948      * @param {Object} scope The scope in which to execute the callback
19949      */
19950     repair : function(xy, callback, scope){
19951         this.callback = callback;
19952         this.scope = scope;
19953         if(xy && this.animRepair !== false){
19954             this.el.addClass("x-dd-drag-repair");
19955             this.el.hideUnders(true);
19956             this.anim = this.el.shift({
19957                 duration: this.repairDuration || .5,
19958                 easing: 'easeOut',
19959                 xy: xy,
19960                 stopFx: true,
19961                 callback: this.afterRepair,
19962                 scope: this
19963             });
19964         }else{
19965             this.afterRepair();
19966         }
19967     },
19968
19969     // private
19970     afterRepair : function(){
19971         this.hide(true);
19972         if(typeof this.callback == "function"){
19973             this.callback.call(this.scope || this);
19974         }
19975         this.callback = null;
19976         this.scope = null;
19977     }
19978 };/*
19979  * Based on:
19980  * Ext JS Library 1.1.1
19981  * Copyright(c) 2006-2007, Ext JS, LLC.
19982  *
19983  * Originally Released Under LGPL - original licence link has changed is not relivant.
19984  *
19985  * Fork - LGPL
19986  * <script type="text/javascript">
19987  */
19988
19989 /**
19990  * @class Roo.dd.DragSource
19991  * @extends Roo.dd.DDProxy
19992  * A simple class that provides the basic implementation needed to make any element draggable.
19993  * @constructor
19994  * @param {String/HTMLElement/Element} el The container element
19995  * @param {Object} config
19996  */
19997 Roo.dd.DragSource = function(el, config){
19998     this.el = Roo.get(el);
19999     this.dragData = {};
20000     
20001     Roo.apply(this, config);
20002     
20003     if(!this.proxy){
20004         this.proxy = new Roo.dd.StatusProxy();
20005     }
20006
20007     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20008           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20009     
20010     this.dragging = false;
20011 };
20012
20013 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20014     /**
20015      * @cfg {String} dropAllowed
20016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20017      */
20018     dropAllowed : "x-dd-drop-ok",
20019     /**
20020      * @cfg {String} dropNotAllowed
20021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20022      */
20023     dropNotAllowed : "x-dd-drop-nodrop",
20024
20025     /**
20026      * Returns the data object associated with this drag source
20027      * @return {Object} data An object containing arbitrary data
20028      */
20029     getDragData : function(e){
20030         return this.dragData;
20031     },
20032
20033     // private
20034     onDragEnter : function(e, id){
20035         var target = Roo.dd.DragDropMgr.getDDById(id);
20036         this.cachedTarget = target;
20037         if(this.beforeDragEnter(target, e, id) !== false){
20038             if(target.isNotifyTarget){
20039                 var status = target.notifyEnter(this, e, this.dragData);
20040                 this.proxy.setStatus(status);
20041             }else{
20042                 this.proxy.setStatus(this.dropAllowed);
20043             }
20044             
20045             if(this.afterDragEnter){
20046                 /**
20047                  * An empty function by default, but provided so that you can perform a custom action
20048                  * when the dragged item enters the drop target by providing an implementation.
20049                  * @param {Roo.dd.DragDrop} target The drop target
20050                  * @param {Event} e The event object
20051                  * @param {String} id The id of the dragged element
20052                  * @method afterDragEnter
20053                  */
20054                 this.afterDragEnter(target, e, id);
20055             }
20056         }
20057     },
20058
20059     /**
20060      * An empty function by default, but provided so that you can perform a custom action
20061      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20062      * @param {Roo.dd.DragDrop} target The drop target
20063      * @param {Event} e The event object
20064      * @param {String} id The id of the dragged element
20065      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20066      */
20067     beforeDragEnter : function(target, e, id){
20068         return true;
20069     },
20070
20071     // private
20072     alignElWithMouse: function() {
20073         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20074         this.proxy.sync();
20075     },
20076
20077     // private
20078     onDragOver : function(e, id){
20079         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20080         if(this.beforeDragOver(target, e, id) !== false){
20081             if(target.isNotifyTarget){
20082                 var status = target.notifyOver(this, e, this.dragData);
20083                 this.proxy.setStatus(status);
20084             }
20085
20086             if(this.afterDragOver){
20087                 /**
20088                  * An empty function by default, but provided so that you can perform a custom action
20089                  * while the dragged item is over the drop target by providing an implementation.
20090                  * @param {Roo.dd.DragDrop} target The drop target
20091                  * @param {Event} e The event object
20092                  * @param {String} id The id of the dragged element
20093                  * @method afterDragOver
20094                  */
20095                 this.afterDragOver(target, e, id);
20096             }
20097         }
20098     },
20099
20100     /**
20101      * An empty function by default, but provided so that you can perform a custom action
20102      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20103      * @param {Roo.dd.DragDrop} target The drop target
20104      * @param {Event} e The event object
20105      * @param {String} id The id of the dragged element
20106      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20107      */
20108     beforeDragOver : function(target, e, id){
20109         return true;
20110     },
20111
20112     // private
20113     onDragOut : function(e, id){
20114         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20115         if(this.beforeDragOut(target, e, id) !== false){
20116             if(target.isNotifyTarget){
20117                 target.notifyOut(this, e, this.dragData);
20118             }
20119             this.proxy.reset();
20120             if(this.afterDragOut){
20121                 /**
20122                  * An empty function by default, but provided so that you can perform a custom action
20123                  * after the dragged item is dragged out of the target without dropping.
20124                  * @param {Roo.dd.DragDrop} target The drop target
20125                  * @param {Event} e The event object
20126                  * @param {String} id The id of the dragged element
20127                  * @method afterDragOut
20128                  */
20129                 this.afterDragOut(target, e, id);
20130             }
20131         }
20132         this.cachedTarget = null;
20133     },
20134
20135     /**
20136      * An empty function by default, but provided so that you can perform a custom action before the dragged
20137      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20138      * @param {Roo.dd.DragDrop} target The drop target
20139      * @param {Event} e The event object
20140      * @param {String} id The id of the dragged element
20141      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20142      */
20143     beforeDragOut : function(target, e, id){
20144         return true;
20145     },
20146     
20147     // private
20148     onDragDrop : function(e, id){
20149         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20150         if(this.beforeDragDrop(target, e, id) !== false){
20151             if(target.isNotifyTarget){
20152                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20153                     this.onValidDrop(target, e, id);
20154                 }else{
20155                     this.onInvalidDrop(target, e, id);
20156                 }
20157             }else{
20158                 this.onValidDrop(target, e, id);
20159             }
20160             
20161             if(this.afterDragDrop){
20162                 /**
20163                  * An empty function by default, but provided so that you can perform a custom action
20164                  * after a valid drag drop has occurred by providing an implementation.
20165                  * @param {Roo.dd.DragDrop} target The drop target
20166                  * @param {Event} e The event object
20167                  * @param {String} id The id of the dropped element
20168                  * @method afterDragDrop
20169                  */
20170                 this.afterDragDrop(target, e, id);
20171             }
20172         }
20173         delete this.cachedTarget;
20174     },
20175
20176     /**
20177      * An empty function by default, but provided so that you can perform a custom action before the dragged
20178      * item is dropped onto the target and optionally cancel the onDragDrop.
20179      * @param {Roo.dd.DragDrop} target The drop target
20180      * @param {Event} e The event object
20181      * @param {String} id The id of the dragged element
20182      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20183      */
20184     beforeDragDrop : function(target, e, id){
20185         return true;
20186     },
20187
20188     // private
20189     onValidDrop : function(target, e, id){
20190         this.hideProxy();
20191         if(this.afterValidDrop){
20192             /**
20193              * An empty function by default, but provided so that you can perform a custom action
20194              * after a valid drop has occurred by providing an implementation.
20195              * @param {Object} target The target DD 
20196              * @param {Event} e The event object
20197              * @param {String} id The id of the dropped element
20198              * @method afterInvalidDrop
20199              */
20200             this.afterValidDrop(target, e, id);
20201         }
20202     },
20203
20204     // private
20205     getRepairXY : function(e, data){
20206         return this.el.getXY();  
20207     },
20208
20209     // private
20210     onInvalidDrop : function(target, e, id){
20211         this.beforeInvalidDrop(target, e, id);
20212         if(this.cachedTarget){
20213             if(this.cachedTarget.isNotifyTarget){
20214                 this.cachedTarget.notifyOut(this, e, this.dragData);
20215             }
20216             this.cacheTarget = null;
20217         }
20218         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20219
20220         if(this.afterInvalidDrop){
20221             /**
20222              * An empty function by default, but provided so that you can perform a custom action
20223              * after an invalid drop has occurred by providing an implementation.
20224              * @param {Event} e The event object
20225              * @param {String} id The id of the dropped element
20226              * @method afterInvalidDrop
20227              */
20228             this.afterInvalidDrop(e, id);
20229         }
20230     },
20231
20232     // private
20233     afterRepair : function(){
20234         if(Roo.enableFx){
20235             this.el.highlight(this.hlColor || "c3daf9");
20236         }
20237         this.dragging = false;
20238     },
20239
20240     /**
20241      * An empty function by default, but provided so that you can perform a custom action after an invalid
20242      * drop has occurred.
20243      * @param {Roo.dd.DragDrop} target The drop target
20244      * @param {Event} e The event object
20245      * @param {String} id The id of the dragged element
20246      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20247      */
20248     beforeInvalidDrop : function(target, e, id){
20249         return true;
20250     },
20251
20252     // private
20253     handleMouseDown : function(e){
20254         if(this.dragging) {
20255             return;
20256         }
20257         var data = this.getDragData(e);
20258         if(data && this.onBeforeDrag(data, e) !== false){
20259             this.dragData = data;
20260             this.proxy.stop();
20261             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20262         } 
20263     },
20264
20265     /**
20266      * An empty function by default, but provided so that you can perform a custom action before the initial
20267      * drag event begins and optionally cancel it.
20268      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20269      * @param {Event} e The event object
20270      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20271      */
20272     onBeforeDrag : function(data, e){
20273         return true;
20274     },
20275
20276     /**
20277      * An empty function by default, but provided so that you can perform a custom action once the initial
20278      * drag event has begun.  The drag cannot be canceled from this function.
20279      * @param {Number} x The x position of the click on the dragged object
20280      * @param {Number} y The y position of the click on the dragged object
20281      */
20282     onStartDrag : Roo.emptyFn,
20283
20284     // private - YUI override
20285     startDrag : function(x, y){
20286         this.proxy.reset();
20287         this.dragging = true;
20288         this.proxy.update("");
20289         this.onInitDrag(x, y);
20290         this.proxy.show();
20291     },
20292
20293     // private
20294     onInitDrag : function(x, y){
20295         var clone = this.el.dom.cloneNode(true);
20296         clone.id = Roo.id(); // prevent duplicate ids
20297         this.proxy.update(clone);
20298         this.onStartDrag(x, y);
20299         return true;
20300     },
20301
20302     /**
20303      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20304      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20305      */
20306     getProxy : function(){
20307         return this.proxy;  
20308     },
20309
20310     /**
20311      * Hides the drag source's {@link Roo.dd.StatusProxy}
20312      */
20313     hideProxy : function(){
20314         this.proxy.hide();  
20315         this.proxy.reset(true);
20316         this.dragging = false;
20317     },
20318
20319     // private
20320     triggerCacheRefresh : function(){
20321         Roo.dd.DDM.refreshCache(this.groups);
20322     },
20323
20324     // private - override to prevent hiding
20325     b4EndDrag: function(e) {
20326     },
20327
20328     // private - override to prevent moving
20329     endDrag : function(e){
20330         this.onEndDrag(this.dragData, e);
20331     },
20332
20333     // private
20334     onEndDrag : function(data, e){
20335     },
20336     
20337     // private - pin to cursor
20338     autoOffset : function(x, y) {
20339         this.setDelta(-12, -20);
20340     }    
20341 });/*
20342  * Based on:
20343  * Ext JS Library 1.1.1
20344  * Copyright(c) 2006-2007, Ext JS, LLC.
20345  *
20346  * Originally Released Under LGPL - original licence link has changed is not relivant.
20347  *
20348  * Fork - LGPL
20349  * <script type="text/javascript">
20350  */
20351
20352
20353 /**
20354  * @class Roo.dd.DropTarget
20355  * @extends Roo.dd.DDTarget
20356  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20357  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20358  * @constructor
20359  * @param {String/HTMLElement/Element} el The container element
20360  * @param {Object} config
20361  */
20362 Roo.dd.DropTarget = function(el, config){
20363     this.el = Roo.get(el);
20364     
20365     var listeners = false; ;
20366     if (config && config.listeners) {
20367         listeners= config.listeners;
20368         delete config.listeners;
20369     }
20370     Roo.apply(this, config);
20371     
20372     if(this.containerScroll){
20373         Roo.dd.ScrollManager.register(this.el);
20374     }
20375     this.addEvents( {
20376          /**
20377          * @scope Roo.dd.DropTarget
20378          */
20379          
20380          /**
20381          * @event enter
20382          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20383          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20384          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20385          * 
20386          * IMPORTANT : it should set this.overClass and this.dropAllowed
20387          * 
20388          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20389          * @param {Event} e The event
20390          * @param {Object} data An object containing arbitrary data supplied by the drag source
20391          */
20392         "enter" : true,
20393         
20394          /**
20395          * @event over
20396          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20397          * This method will be called on every mouse movement while the drag source is over the drop target.
20398          * This default implementation simply returns the dropAllowed config value.
20399          * 
20400          * IMPORTANT : it should set this.dropAllowed
20401          * 
20402          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20403          * @param {Event} e The event
20404          * @param {Object} data An object containing arbitrary data supplied by the drag source
20405          
20406          */
20407         "over" : true,
20408         /**
20409          * @event out
20410          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20411          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20412          * overClass (if any) from the drop element.
20413          * 
20414          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20415          * @param {Event} e The event
20416          * @param {Object} data An object containing arbitrary data supplied by the drag source
20417          */
20418          "out" : true,
20419          
20420         /**
20421          * @event drop
20422          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20423          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20424          * implementation that does something to process the drop event and returns true so that the drag source's
20425          * repair action does not run.
20426          * 
20427          * IMPORTANT : it should set this.success
20428          * 
20429          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20430          * @param {Event} e The event
20431          * @param {Object} data An object containing arbitrary data supplied by the drag source
20432         */
20433          "drop" : true
20434     });
20435             
20436      
20437     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20438         this.el.dom, 
20439         this.ddGroup || this.group,
20440         {
20441             isTarget: true,
20442             listeners : listeners || {} 
20443            
20444         
20445         }
20446     );
20447
20448 };
20449
20450 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20451     /**
20452      * @cfg {String} overClass
20453      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20454      */
20455      /**
20456      * @cfg {String} ddGroup
20457      * The drag drop group to handle drop events for
20458      */
20459      
20460     /**
20461      * @cfg {String} dropAllowed
20462      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20463      */
20464     dropAllowed : "x-dd-drop-ok",
20465     /**
20466      * @cfg {String} dropNotAllowed
20467      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20468      */
20469     dropNotAllowed : "x-dd-drop-nodrop",
20470     /**
20471      * @cfg {boolean} success
20472      * set this after drop listener.. 
20473      */
20474     success : false,
20475     /**
20476      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20477      * if the drop point is valid for over/enter..
20478      */
20479     valid : false,
20480     // private
20481     isTarget : true,
20482
20483     // private
20484     isNotifyTarget : true,
20485     
20486     /**
20487      * @hide
20488      */
20489     notifyEnter : function(dd, e, data)
20490     {
20491         this.valid = true;
20492         this.fireEvent('enter', dd, e, data);
20493         if(this.overClass){
20494             this.el.addClass(this.overClass);
20495         }
20496         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20497             this.valid ? this.dropAllowed : this.dropNotAllowed
20498         );
20499     },
20500
20501     /**
20502      * @hide
20503      */
20504     notifyOver : function(dd, e, data)
20505     {
20506         this.valid = true;
20507         this.fireEvent('over', dd, e, data);
20508         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20509             this.valid ? this.dropAllowed : this.dropNotAllowed
20510         );
20511     },
20512
20513     /**
20514      * @hide
20515      */
20516     notifyOut : function(dd, e, data)
20517     {
20518         this.fireEvent('out', dd, e, data);
20519         if(this.overClass){
20520             this.el.removeClass(this.overClass);
20521         }
20522     },
20523
20524     /**
20525      * @hide
20526      */
20527     notifyDrop : function(dd, e, data)
20528     {
20529         this.success = false;
20530         this.fireEvent('drop', dd, e, data);
20531         return this.success;
20532     }
20533 });/*
20534  * Based on:
20535  * Ext JS Library 1.1.1
20536  * Copyright(c) 2006-2007, Ext JS, LLC.
20537  *
20538  * Originally Released Under LGPL - original licence link has changed is not relivant.
20539  *
20540  * Fork - LGPL
20541  * <script type="text/javascript">
20542  */
20543
20544
20545 /**
20546  * @class Roo.dd.DragZone
20547  * @extends Roo.dd.DragSource
20548  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20549  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20550  * @constructor
20551  * @param {String/HTMLElement/Element} el The container element
20552  * @param {Object} config
20553  */
20554 Roo.dd.DragZone = function(el, config){
20555     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20556     if(this.containerScroll){
20557         Roo.dd.ScrollManager.register(this.el);
20558     }
20559 };
20560
20561 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20562     /**
20563      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20564      * for auto scrolling during drag operations.
20565      */
20566     /**
20567      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20568      * method after a failed drop (defaults to "c3daf9" - light blue)
20569      */
20570
20571     /**
20572      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20573      * for a valid target to drag based on the mouse down. Override this method
20574      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20575      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20576      * @param {EventObject} e The mouse down event
20577      * @return {Object} The dragData
20578      */
20579     getDragData : function(e){
20580         return Roo.dd.Registry.getHandleFromEvent(e);
20581     },
20582     
20583     /**
20584      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20585      * this.dragData.ddel
20586      * @param {Number} x The x position of the click on the dragged object
20587      * @param {Number} y The y position of the click on the dragged object
20588      * @return {Boolean} true to continue the drag, false to cancel
20589      */
20590     onInitDrag : function(x, y){
20591         this.proxy.update(this.dragData.ddel.cloneNode(true));
20592         this.onStartDrag(x, y);
20593         return true;
20594     },
20595     
20596     /**
20597      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20598      */
20599     afterRepair : function(){
20600         if(Roo.enableFx){
20601             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20602         }
20603         this.dragging = false;
20604     },
20605
20606     /**
20607      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20608      * the XY of this.dragData.ddel
20609      * @param {EventObject} e The mouse up event
20610      * @return {Array} The xy location (e.g. [100, 200])
20611      */
20612     getRepairXY : function(e){
20613         return Roo.Element.fly(this.dragData.ddel).getXY();  
20614     }
20615 });/*
20616  * Based on:
20617  * Ext JS Library 1.1.1
20618  * Copyright(c) 2006-2007, Ext JS, LLC.
20619  *
20620  * Originally Released Under LGPL - original licence link has changed is not relivant.
20621  *
20622  * Fork - LGPL
20623  * <script type="text/javascript">
20624  */
20625 /**
20626  * @class Roo.dd.DropZone
20627  * @extends Roo.dd.DropTarget
20628  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20629  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20630  * @constructor
20631  * @param {String/HTMLElement/Element} el The container element
20632  * @param {Object} config
20633  */
20634 Roo.dd.DropZone = function(el, config){
20635     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20636 };
20637
20638 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20639     /**
20640      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20641      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20642      * provide your own custom lookup.
20643      * @param {Event} e The event
20644      * @return {Object} data The custom data
20645      */
20646     getTargetFromEvent : function(e){
20647         return Roo.dd.Registry.getTargetFromEvent(e);
20648     },
20649
20650     /**
20651      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20652      * that it has registered.  This method has no default implementation and should be overridden to provide
20653      * node-specific processing if necessary.
20654      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20655      * {@link #getTargetFromEvent} for this node)
20656      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20657      * @param {Event} e The event
20658      * @param {Object} data An object containing arbitrary data supplied by the drag source
20659      */
20660     onNodeEnter : function(n, dd, e, data){
20661         
20662     },
20663
20664     /**
20665      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20666      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20667      * overridden to provide the proper feedback.
20668      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20669      * {@link #getTargetFromEvent} for this node)
20670      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20671      * @param {Event} e The event
20672      * @param {Object} data An object containing arbitrary data supplied by the drag source
20673      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20674      * underlying {@link Roo.dd.StatusProxy} can be updated
20675      */
20676     onNodeOver : function(n, dd, e, data){
20677         return this.dropAllowed;
20678     },
20679
20680     /**
20681      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20682      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20683      * node-specific processing if necessary.
20684      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20685      * {@link #getTargetFromEvent} for this node)
20686      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20687      * @param {Event} e The event
20688      * @param {Object} data An object containing arbitrary data supplied by the drag source
20689      */
20690     onNodeOut : function(n, dd, e, data){
20691         
20692     },
20693
20694     /**
20695      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20696      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20697      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20698      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20699      * {@link #getTargetFromEvent} for this node)
20700      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20701      * @param {Event} e The event
20702      * @param {Object} data An object containing arbitrary data supplied by the drag source
20703      * @return {Boolean} True if the drop was valid, else false
20704      */
20705     onNodeDrop : function(n, dd, e, data){
20706         return false;
20707     },
20708
20709     /**
20710      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20711      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20712      * it should be overridden to provide the proper feedback if necessary.
20713      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20714      * @param {Event} e The event
20715      * @param {Object} data An object containing arbitrary data supplied by the drag source
20716      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20717      * underlying {@link Roo.dd.StatusProxy} can be updated
20718      */
20719     onContainerOver : function(dd, e, data){
20720         return this.dropNotAllowed;
20721     },
20722
20723     /**
20724      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20725      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20726      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20727      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20728      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20729      * @param {Event} e The event
20730      * @param {Object} data An object containing arbitrary data supplied by the drag source
20731      * @return {Boolean} True if the drop was valid, else false
20732      */
20733     onContainerDrop : function(dd, e, data){
20734         return false;
20735     },
20736
20737     /**
20738      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20739      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20740      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20741      * you should override this method and provide a custom implementation.
20742      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20743      * @param {Event} e The event
20744      * @param {Object} data An object containing arbitrary data supplied by the drag source
20745      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20746      * underlying {@link Roo.dd.StatusProxy} can be updated
20747      */
20748     notifyEnter : function(dd, e, data){
20749         return this.dropNotAllowed;
20750     },
20751
20752     /**
20753      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20754      * This method will be called on every mouse movement while the drag source is over the drop zone.
20755      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20756      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20757      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20758      * registered node, it will call {@link #onContainerOver}.
20759      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20760      * @param {Event} e The event
20761      * @param {Object} data An object containing arbitrary data supplied by the drag source
20762      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20763      * underlying {@link Roo.dd.StatusProxy} can be updated
20764      */
20765     notifyOver : function(dd, e, data){
20766         var n = this.getTargetFromEvent(e);
20767         if(!n){ // not over valid drop target
20768             if(this.lastOverNode){
20769                 this.onNodeOut(this.lastOverNode, dd, e, data);
20770                 this.lastOverNode = null;
20771             }
20772             return this.onContainerOver(dd, e, data);
20773         }
20774         if(this.lastOverNode != n){
20775             if(this.lastOverNode){
20776                 this.onNodeOut(this.lastOverNode, dd, e, data);
20777             }
20778             this.onNodeEnter(n, dd, e, data);
20779             this.lastOverNode = n;
20780         }
20781         return this.onNodeOver(n, dd, e, data);
20782     },
20783
20784     /**
20785      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20786      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20787      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20788      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20789      * @param {Event} e The event
20790      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20791      */
20792     notifyOut : function(dd, e, data){
20793         if(this.lastOverNode){
20794             this.onNodeOut(this.lastOverNode, dd, e, data);
20795             this.lastOverNode = null;
20796         }
20797     },
20798
20799     /**
20800      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20801      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20802      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20803      * otherwise it will call {@link #onContainerDrop}.
20804      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20805      * @param {Event} e The event
20806      * @param {Object} data An object containing arbitrary data supplied by the drag source
20807      * @return {Boolean} True if the drop was valid, else false
20808      */
20809     notifyDrop : function(dd, e, data){
20810         if(this.lastOverNode){
20811             this.onNodeOut(this.lastOverNode, dd, e, data);
20812             this.lastOverNode = null;
20813         }
20814         var n = this.getTargetFromEvent(e);
20815         return n ?
20816             this.onNodeDrop(n, dd, e, data) :
20817             this.onContainerDrop(dd, e, data);
20818     },
20819
20820     // private
20821     triggerCacheRefresh : function(){
20822         Roo.dd.DDM.refreshCache(this.groups);
20823     }  
20824 });/*
20825  * Based on:
20826  * Ext JS Library 1.1.1
20827  * Copyright(c) 2006-2007, Ext JS, LLC.
20828  *
20829  * Originally Released Under LGPL - original licence link has changed is not relivant.
20830  *
20831  * Fork - LGPL
20832  * <script type="text/javascript">
20833  */
20834
20835
20836 /**
20837  * @class Roo.data.SortTypes
20838  * @singleton
20839  * Defines the default sorting (casting?) comparison functions used when sorting data.
20840  */
20841 Roo.data.SortTypes = {
20842     /**
20843      * Default sort that does nothing
20844      * @param {Mixed} s The value being converted
20845      * @return {Mixed} The comparison value
20846      */
20847     none : function(s){
20848         return s;
20849     },
20850     
20851     /**
20852      * The regular expression used to strip tags
20853      * @type {RegExp}
20854      * @property
20855      */
20856     stripTagsRE : /<\/?[^>]+>/gi,
20857     
20858     /**
20859      * Strips all HTML tags to sort on text only
20860      * @param {Mixed} s The value being converted
20861      * @return {String} The comparison value
20862      */
20863     asText : function(s){
20864         return String(s).replace(this.stripTagsRE, "");
20865     },
20866     
20867     /**
20868      * Strips all HTML tags to sort on text only - Case insensitive
20869      * @param {Mixed} s The value being converted
20870      * @return {String} The comparison value
20871      */
20872     asUCText : function(s){
20873         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20874     },
20875     
20876     /**
20877      * Case insensitive string
20878      * @param {Mixed} s The value being converted
20879      * @return {String} The comparison value
20880      */
20881     asUCString : function(s) {
20882         return String(s).toUpperCase();
20883     },
20884     
20885     /**
20886      * Date sorting
20887      * @param {Mixed} s The value being converted
20888      * @return {Number} The comparison value
20889      */
20890     asDate : function(s) {
20891         if(!s){
20892             return 0;
20893         }
20894         if(s instanceof Date){
20895             return s.getTime();
20896         }
20897         return Date.parse(String(s));
20898     },
20899     
20900     /**
20901      * Float sorting
20902      * @param {Mixed} s The value being converted
20903      * @return {Float} The comparison value
20904      */
20905     asFloat : function(s) {
20906         var val = parseFloat(String(s).replace(/,/g, ""));
20907         if(isNaN(val)) val = 0;
20908         return val;
20909     },
20910     
20911     /**
20912      * Integer sorting
20913      * @param {Mixed} s The value being converted
20914      * @return {Number} The comparison value
20915      */
20916     asInt : function(s) {
20917         var val = parseInt(String(s).replace(/,/g, ""));
20918         if(isNaN(val)) val = 0;
20919         return val;
20920     }
20921 };/*
20922  * Based on:
20923  * Ext JS Library 1.1.1
20924  * Copyright(c) 2006-2007, Ext JS, LLC.
20925  *
20926  * Originally Released Under LGPL - original licence link has changed is not relivant.
20927  *
20928  * Fork - LGPL
20929  * <script type="text/javascript">
20930  */
20931
20932 /**
20933 * @class Roo.data.Record
20934  * Instances of this class encapsulate both record <em>definition</em> information, and record
20935  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20936  * to access Records cached in an {@link Roo.data.Store} object.<br>
20937  * <p>
20938  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20939  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20940  * objects.<br>
20941  * <p>
20942  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20943  * @constructor
20944  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20945  * {@link #create}. The parameters are the same.
20946  * @param {Array} data An associative Array of data values keyed by the field name.
20947  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20948  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20949  * not specified an integer id is generated.
20950  */
20951 Roo.data.Record = function(data, id){
20952     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20953     this.data = data;
20954 };
20955
20956 /**
20957  * Generate a constructor for a specific record layout.
20958  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20959  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20960  * Each field definition object may contain the following properties: <ul>
20961  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
20962  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20963  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20964  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20965  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20966  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20967  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20968  * this may be omitted.</p></li>
20969  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20970  * <ul><li>auto (Default, implies no conversion)</li>
20971  * <li>string</li>
20972  * <li>int</li>
20973  * <li>float</li>
20974  * <li>boolean</li>
20975  * <li>date</li></ul></p></li>
20976  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20977  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20978  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20979  * by the Reader into an object that will be stored in the Record. It is passed the
20980  * following parameters:<ul>
20981  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20982  * </ul></p></li>
20983  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20984  * </ul>
20985  * <br>usage:<br><pre><code>
20986 var TopicRecord = Roo.data.Record.create(
20987     {name: 'title', mapping: 'topic_title'},
20988     {name: 'author', mapping: 'username'},
20989     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20990     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20991     {name: 'lastPoster', mapping: 'user2'},
20992     {name: 'excerpt', mapping: 'post_text'}
20993 );
20994
20995 var myNewRecord = new TopicRecord({
20996     title: 'Do my job please',
20997     author: 'noobie',
20998     totalPosts: 1,
20999     lastPost: new Date(),
21000     lastPoster: 'Animal',
21001     excerpt: 'No way dude!'
21002 });
21003 myStore.add(myNewRecord);
21004 </code></pre>
21005  * @method create
21006  * @static
21007  */
21008 Roo.data.Record.create = function(o){
21009     var f = function(){
21010         f.superclass.constructor.apply(this, arguments);
21011     };
21012     Roo.extend(f, Roo.data.Record);
21013     var p = f.prototype;
21014     p.fields = new Roo.util.MixedCollection(false, function(field){
21015         return field.name;
21016     });
21017     for(var i = 0, len = o.length; i < len; i++){
21018         p.fields.add(new Roo.data.Field(o[i]));
21019     }
21020     f.getField = function(name){
21021         return p.fields.get(name);  
21022     };
21023     return f;
21024 };
21025
21026 Roo.data.Record.AUTO_ID = 1000;
21027 Roo.data.Record.EDIT = 'edit';
21028 Roo.data.Record.REJECT = 'reject';
21029 Roo.data.Record.COMMIT = 'commit';
21030
21031 Roo.data.Record.prototype = {
21032     /**
21033      * Readonly flag - true if this record has been modified.
21034      * @type Boolean
21035      */
21036     dirty : false,
21037     editing : false,
21038     error: null,
21039     modified: null,
21040
21041     // private
21042     join : function(store){
21043         this.store = store;
21044     },
21045
21046     /**
21047      * Set the named field to the specified value.
21048      * @param {String} name The name of the field to set.
21049      * @param {Object} value The value to set the field to.
21050      */
21051     set : function(name, value){
21052         if(this.data[name] == value){
21053             return;
21054         }
21055         this.dirty = true;
21056         if(!this.modified){
21057             this.modified = {};
21058         }
21059         if(typeof this.modified[name] == 'undefined'){
21060             this.modified[name] = this.data[name];
21061         }
21062         this.data[name] = value;
21063         if(!this.editing && this.store){
21064             this.store.afterEdit(this);
21065         }       
21066     },
21067
21068     /**
21069      * Get the value of the named field.
21070      * @param {String} name The name of the field to get the value of.
21071      * @return {Object} The value of the field.
21072      */
21073     get : function(name){
21074         return this.data[name]; 
21075     },
21076
21077     // private
21078     beginEdit : function(){
21079         this.editing = true;
21080         this.modified = {}; 
21081     },
21082
21083     // private
21084     cancelEdit : function(){
21085         this.editing = false;
21086         delete this.modified;
21087     },
21088
21089     // private
21090     endEdit : function(){
21091         this.editing = false;
21092         if(this.dirty && this.store){
21093             this.store.afterEdit(this);
21094         }
21095     },
21096
21097     /**
21098      * Usually called by the {@link Roo.data.Store} which owns the Record.
21099      * Rejects all changes made to the Record since either creation, or the last commit operation.
21100      * Modified fields are reverted to their original values.
21101      * <p>
21102      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21103      * of reject operations.
21104      */
21105     reject : function(){
21106         var m = this.modified;
21107         for(var n in m){
21108             if(typeof m[n] != "function"){
21109                 this.data[n] = m[n];
21110             }
21111         }
21112         this.dirty = false;
21113         delete this.modified;
21114         this.editing = false;
21115         if(this.store){
21116             this.store.afterReject(this);
21117         }
21118     },
21119
21120     /**
21121      * Usually called by the {@link Roo.data.Store} which owns the Record.
21122      * Commits all changes made to the Record since either creation, or the last commit operation.
21123      * <p>
21124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21125      * of commit operations.
21126      */
21127     commit : function(){
21128         this.dirty = false;
21129         delete this.modified;
21130         this.editing = false;
21131         if(this.store){
21132             this.store.afterCommit(this);
21133         }
21134     },
21135
21136     // private
21137     hasError : function(){
21138         return this.error != null;
21139     },
21140
21141     // private
21142     clearError : function(){
21143         this.error = null;
21144     },
21145
21146     /**
21147      * Creates a copy of this record.
21148      * @param {String} id (optional) A new record id if you don't want to use this record's id
21149      * @return {Record}
21150      */
21151     copy : function(newId) {
21152         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21153     }
21154 };/*
21155  * Based on:
21156  * Ext JS Library 1.1.1
21157  * Copyright(c) 2006-2007, Ext JS, LLC.
21158  *
21159  * Originally Released Under LGPL - original licence link has changed is not relivant.
21160  *
21161  * Fork - LGPL
21162  * <script type="text/javascript">
21163  */
21164
21165
21166
21167 /**
21168  * @class Roo.data.Store
21169  * @extends Roo.util.Observable
21170  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21171  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21172  * <p>
21173  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
21174  * has no knowledge of the format of the data returned by the Proxy.<br>
21175  * <p>
21176  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21177  * instances from the data object. These records are cached and made available through accessor functions.
21178  * @constructor
21179  * Creates a new Store.
21180  * @param {Object} config A config object containing the objects needed for the Store to access data,
21181  * and read the data into Records.
21182  */
21183 Roo.data.Store = function(config){
21184     this.data = new Roo.util.MixedCollection(false);
21185     this.data.getKey = function(o){
21186         return o.id;
21187     };
21188     this.baseParams = {};
21189     // private
21190     this.paramNames = {
21191         "start" : "start",
21192         "limit" : "limit",
21193         "sort" : "sort",
21194         "dir" : "dir",
21195         "multisort" : "_multisort"
21196     };
21197
21198     if(config && config.data){
21199         this.inlineData = config.data;
21200         delete config.data;
21201     }
21202
21203     Roo.apply(this, config);
21204     
21205     if(this.reader){ // reader passed
21206         this.reader = Roo.factory(this.reader, Roo.data);
21207         this.reader.xmodule = this.xmodule || false;
21208         if(!this.recordType){
21209             this.recordType = this.reader.recordType;
21210         }
21211         if(this.reader.onMetaChange){
21212             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21213         }
21214     }
21215
21216     if(this.recordType){
21217         this.fields = this.recordType.prototype.fields;
21218     }
21219     this.modified = [];
21220
21221     this.addEvents({
21222         /**
21223          * @event datachanged
21224          * Fires when the data cache has changed, and a widget which is using this Store
21225          * as a Record cache should refresh its view.
21226          * @param {Store} this
21227          */
21228         datachanged : true,
21229         /**
21230          * @event metachange
21231          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21232          * @param {Store} this
21233          * @param {Object} meta The JSON metadata
21234          */
21235         metachange : true,
21236         /**
21237          * @event add
21238          * Fires when Records have been added to the Store
21239          * @param {Store} this
21240          * @param {Roo.data.Record[]} records The array of Records added
21241          * @param {Number} index The index at which the record(s) were added
21242          */
21243         add : true,
21244         /**
21245          * @event remove
21246          * Fires when a Record has been removed from the Store
21247          * @param {Store} this
21248          * @param {Roo.data.Record} record The Record that was removed
21249          * @param {Number} index The index at which the record was removed
21250          */
21251         remove : true,
21252         /**
21253          * @event update
21254          * Fires when a Record has been updated
21255          * @param {Store} this
21256          * @param {Roo.data.Record} record The Record that was updated
21257          * @param {String} operation The update operation being performed.  Value may be one of:
21258          * <pre><code>
21259  Roo.data.Record.EDIT
21260  Roo.data.Record.REJECT
21261  Roo.data.Record.COMMIT
21262          * </code></pre>
21263          */
21264         update : true,
21265         /**
21266          * @event clear
21267          * Fires when the data cache has been cleared.
21268          * @param {Store} this
21269          */
21270         clear : true,
21271         /**
21272          * @event beforeload
21273          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21274          * the load action will be canceled.
21275          * @param {Store} this
21276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21277          */
21278         beforeload : true,
21279         /**
21280          * @event beforeloadadd
21281          * Fires after a new set of Records has been loaded.
21282          * @param {Store} this
21283          * @param {Roo.data.Record[]} records The Records that were loaded
21284          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21285          */
21286         beforeloadadd : true,
21287         /**
21288          * @event load
21289          * Fires after a new set of Records has been loaded, before they are added to the store.
21290          * @param {Store} this
21291          * @param {Roo.data.Record[]} records The Records that were loaded
21292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21293          * @params {Object} return from reader
21294          */
21295         load : true,
21296         /**
21297          * @event loadexception
21298          * Fires if an exception occurs in the Proxy during loading.
21299          * Called with the signature of the Proxy's "loadexception" event.
21300          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21301          * 
21302          * @param {Proxy} 
21303          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21304          * @param {Object} load options 
21305          * @param {Object} jsonData from your request (normally this contains the Exception)
21306          */
21307         loadexception : true
21308     });
21309     
21310     if(this.proxy){
21311         this.proxy = Roo.factory(this.proxy, Roo.data);
21312         this.proxy.xmodule = this.xmodule || false;
21313         this.relayEvents(this.proxy,  ["loadexception"]);
21314     }
21315     this.sortToggle = {};
21316     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21317
21318     Roo.data.Store.superclass.constructor.call(this);
21319
21320     if(this.inlineData){
21321         this.loadData(this.inlineData);
21322         delete this.inlineData;
21323     }
21324 };
21325
21326 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21327      /**
21328     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21329     * without a remote query - used by combo/forms at present.
21330     */
21331     
21332     /**
21333     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21334     */
21335     /**
21336     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21337     */
21338     /**
21339     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21340     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21341     */
21342     /**
21343     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21344     * on any HTTP request
21345     */
21346     /**
21347     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21348     */
21349     /**
21350     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21351     */
21352     multiSort: false,
21353     /**
21354     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21355     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21356     */
21357     remoteSort : false,
21358
21359     /**
21360     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21361      * loaded or when a record is removed. (defaults to false).
21362     */
21363     pruneModifiedRecords : false,
21364
21365     // private
21366     lastOptions : null,
21367
21368     /**
21369      * Add Records to the Store and fires the add event.
21370      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21371      */
21372     add : function(records){
21373         records = [].concat(records);
21374         for(var i = 0, len = records.length; i < len; i++){
21375             records[i].join(this);
21376         }
21377         var index = this.data.length;
21378         this.data.addAll(records);
21379         this.fireEvent("add", this, records, index);
21380     },
21381
21382     /**
21383      * Remove a Record from the Store and fires the remove event.
21384      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21385      */
21386     remove : function(record){
21387         var index = this.data.indexOf(record);
21388         this.data.removeAt(index);
21389         if(this.pruneModifiedRecords){
21390             this.modified.remove(record);
21391         }
21392         this.fireEvent("remove", this, record, index);
21393     },
21394
21395     /**
21396      * Remove all Records from the Store and fires the clear event.
21397      */
21398     removeAll : function(){
21399         this.data.clear();
21400         if(this.pruneModifiedRecords){
21401             this.modified = [];
21402         }
21403         this.fireEvent("clear", this);
21404     },
21405
21406     /**
21407      * Inserts Records to the Store at the given index and fires the add event.
21408      * @param {Number} index The start index at which to insert the passed Records.
21409      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21410      */
21411     insert : function(index, records){
21412         records = [].concat(records);
21413         for(var i = 0, len = records.length; i < len; i++){
21414             this.data.insert(index, records[i]);
21415             records[i].join(this);
21416         }
21417         this.fireEvent("add", this, records, index);
21418     },
21419
21420     /**
21421      * Get the index within the cache of the passed Record.
21422      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21423      * @return {Number} The index of the passed Record. Returns -1 if not found.
21424      */
21425     indexOf : function(record){
21426         return this.data.indexOf(record);
21427     },
21428
21429     /**
21430      * Get the index within the cache of the Record with the passed id.
21431      * @param {String} id The id of the Record to find.
21432      * @return {Number} The index of the Record. Returns -1 if not found.
21433      */
21434     indexOfId : function(id){
21435         return this.data.indexOfKey(id);
21436     },
21437
21438     /**
21439      * Get the Record with the specified id.
21440      * @param {String} id The id of the Record to find.
21441      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21442      */
21443     getById : function(id){
21444         return this.data.key(id);
21445     },
21446
21447     /**
21448      * Get the Record at the specified index.
21449      * @param {Number} index The index of the Record to find.
21450      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21451      */
21452     getAt : function(index){
21453         return this.data.itemAt(index);
21454     },
21455
21456     /**
21457      * Returns a range of Records between specified indices.
21458      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21459      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21460      * @return {Roo.data.Record[]} An array of Records
21461      */
21462     getRange : function(start, end){
21463         return this.data.getRange(start, end);
21464     },
21465
21466     // private
21467     storeOptions : function(o){
21468         o = Roo.apply({}, o);
21469         delete o.callback;
21470         delete o.scope;
21471         this.lastOptions = o;
21472     },
21473
21474     /**
21475      * Loads the Record cache from the configured Proxy using the configured Reader.
21476      * <p>
21477      * If using remote paging, then the first load call must specify the <em>start</em>
21478      * and <em>limit</em> properties in the options.params property to establish the initial
21479      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21480      * <p>
21481      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21482      * and this call will return before the new data has been loaded. Perform any post-processing
21483      * in a callback function, or in a "load" event handler.</strong>
21484      * <p>
21485      * @param {Object} options An object containing properties which control loading options:<ul>
21486      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21487      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21488      * passed the following arguments:<ul>
21489      * <li>r : Roo.data.Record[]</li>
21490      * <li>options: Options object from the load call</li>
21491      * <li>success: Boolean success indicator</li></ul></li>
21492      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21493      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21494      * </ul>
21495      */
21496     load : function(options){
21497         options = options || {};
21498         if(this.fireEvent("beforeload", this, options) !== false){
21499             this.storeOptions(options);
21500             var p = Roo.apply(options.params || {}, this.baseParams);
21501             // if meta was not loaded from remote source.. try requesting it.
21502             if (!this.reader.metaFromRemote) {
21503                 p._requestMeta = 1;
21504             }
21505             if(this.sortInfo && this.remoteSort){
21506                 var pn = this.paramNames;
21507                 p[pn["sort"]] = this.sortInfo.field;
21508                 p[pn["dir"]] = this.sortInfo.direction;
21509             }
21510             if (this.multiSort) {
21511                 var pn = this.paramNames;
21512                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21513             }
21514             
21515             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21516         }
21517     },
21518
21519     /**
21520      * Reloads the Record cache from the configured Proxy using the configured Reader and
21521      * the options from the last load operation performed.
21522      * @param {Object} options (optional) An object containing properties which may override the options
21523      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21524      * the most recently used options are reused).
21525      */
21526     reload : function(options){
21527         this.load(Roo.applyIf(options||{}, this.lastOptions));
21528     },
21529
21530     // private
21531     // Called as a callback by the Reader during a load operation.
21532     loadRecords : function(o, options, success){
21533         if(!o || success === false){
21534             if(success !== false){
21535                 this.fireEvent("load", this, [], options, o);
21536             }
21537             if(options.callback){
21538                 options.callback.call(options.scope || this, [], options, false);
21539             }
21540             return;
21541         }
21542         // if data returned failure - throw an exception.
21543         if (o.success === false) {
21544             // show a message if no listener is registered.
21545             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21546                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21547             }
21548             // loadmask wil be hooked into this..
21549             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21550             return;
21551         }
21552         var r = o.records, t = o.totalRecords || r.length;
21553         
21554         this.fireEvent("beforeloadadd", this, r, options, o);
21555         
21556         if(!options || options.add !== true){
21557             if(this.pruneModifiedRecords){
21558                 this.modified = [];
21559             }
21560             for(var i = 0, len = r.length; i < len; i++){
21561                 r[i].join(this);
21562             }
21563             if(this.snapshot){
21564                 this.data = this.snapshot;
21565                 delete this.snapshot;
21566             }
21567             this.data.clear();
21568             this.data.addAll(r);
21569             this.totalLength = t;
21570             this.applySort();
21571             this.fireEvent("datachanged", this);
21572         }else{
21573             this.totalLength = Math.max(t, this.data.length+r.length);
21574             this.add(r);
21575         }
21576         this.fireEvent("load", this, r, options, o);
21577         if(options.callback){
21578             options.callback.call(options.scope || this, r, options, true);
21579         }
21580     },
21581
21582
21583     /**
21584      * Loads data from a passed data block. A Reader which understands the format of the data
21585      * must have been configured in the constructor.
21586      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21587      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21588      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21589      */
21590     loadData : function(o, append){
21591         var r = this.reader.readRecords(o);
21592         this.loadRecords(r, {add: append}, true);
21593     },
21594
21595     /**
21596      * Gets the number of cached records.
21597      * <p>
21598      * <em>If using paging, this may not be the total size of the dataset. If the data object
21599      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21600      * the data set size</em>
21601      */
21602     getCount : function(){
21603         return this.data.length || 0;
21604     },
21605
21606     /**
21607      * Gets the total number of records in the dataset as returned by the server.
21608      * <p>
21609      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21610      * the dataset size</em>
21611      */
21612     getTotalCount : function(){
21613         return this.totalLength || 0;
21614     },
21615
21616     /**
21617      * Returns the sort state of the Store as an object with two properties:
21618      * <pre><code>
21619  field {String} The name of the field by which the Records are sorted
21620  direction {String} The sort order, "ASC" or "DESC"
21621      * </code></pre>
21622      */
21623     getSortState : function(){
21624         return this.sortInfo;
21625     },
21626
21627     // private
21628     applySort : function(){
21629         if(this.sortInfo && !this.remoteSort){
21630             var s = this.sortInfo, f = s.field;
21631             var st = this.fields.get(f).sortType;
21632             var fn = function(r1, r2){
21633                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21634                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21635             };
21636             this.data.sort(s.direction, fn);
21637             if(this.snapshot && this.snapshot != this.data){
21638                 this.snapshot.sort(s.direction, fn);
21639             }
21640         }
21641     },
21642
21643     /**
21644      * Sets the default sort column and order to be used by the next load operation.
21645      * @param {String} fieldName The name of the field to sort by.
21646      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21647      */
21648     setDefaultSort : function(field, dir){
21649         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21650     },
21651
21652     /**
21653      * Sort the Records.
21654      * If remote sorting is used, the sort is performed on the server, and the cache is
21655      * reloaded. If local sorting is used, the cache is sorted internally.
21656      * @param {String} fieldName The name of the field to sort by.
21657      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21658      */
21659     sort : function(fieldName, dir){
21660         var f = this.fields.get(fieldName);
21661         if(!dir){
21662             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21663             
21664             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21665                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21666             }else{
21667                 dir = f.sortDir;
21668             }
21669         }
21670         this.sortToggle[f.name] = dir;
21671         this.sortInfo = {field: f.name, direction: dir};
21672         if(!this.remoteSort){
21673             this.applySort();
21674             this.fireEvent("datachanged", this);
21675         }else{
21676             this.load(this.lastOptions);
21677         }
21678     },
21679
21680     /**
21681      * Calls the specified function for each of the Records in the cache.
21682      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21683      * Returning <em>false</em> aborts and exits the iteration.
21684      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21685      */
21686     each : function(fn, scope){
21687         this.data.each(fn, scope);
21688     },
21689
21690     /**
21691      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21692      * (e.g., during paging).
21693      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21694      */
21695     getModifiedRecords : function(){
21696         return this.modified;
21697     },
21698
21699     // private
21700     createFilterFn : function(property, value, anyMatch){
21701         if(!value.exec){ // not a regex
21702             value = String(value);
21703             if(value.length == 0){
21704                 return false;
21705             }
21706             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21707         }
21708         return function(r){
21709             return value.test(r.data[property]);
21710         };
21711     },
21712
21713     /**
21714      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21715      * @param {String} property A field on your records
21716      * @param {Number} start The record index to start at (defaults to 0)
21717      * @param {Number} end The last record index to include (defaults to length - 1)
21718      * @return {Number} The sum
21719      */
21720     sum : function(property, start, end){
21721         var rs = this.data.items, v = 0;
21722         start = start || 0;
21723         end = (end || end === 0) ? end : rs.length-1;
21724
21725         for(var i = start; i <= end; i++){
21726             v += (rs[i].data[property] || 0);
21727         }
21728         return v;
21729     },
21730
21731     /**
21732      * Filter the records by a specified property.
21733      * @param {String} field A field on your records
21734      * @param {String/RegExp} value Either a string that the field
21735      * should start with or a RegExp to test against the field
21736      * @param {Boolean} anyMatch True to match any part not just the beginning
21737      */
21738     filter : function(property, value, anyMatch){
21739         var fn = this.createFilterFn(property, value, anyMatch);
21740         return fn ? this.filterBy(fn) : this.clearFilter();
21741     },
21742
21743     /**
21744      * Filter by a function. The specified function will be called with each
21745      * record in this data source. If the function returns true the record is included,
21746      * otherwise it is filtered.
21747      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21748      * @param {Object} scope (optional) The scope of the function (defaults to this)
21749      */
21750     filterBy : function(fn, scope){
21751         this.snapshot = this.snapshot || this.data;
21752         this.data = this.queryBy(fn, scope||this);
21753         this.fireEvent("datachanged", this);
21754     },
21755
21756     /**
21757      * Query the records by a specified property.
21758      * @param {String} field A field on your records
21759      * @param {String/RegExp} value Either a string that the field
21760      * should start with or a RegExp to test against the field
21761      * @param {Boolean} anyMatch True to match any part not just the beginning
21762      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21763      */
21764     query : function(property, value, anyMatch){
21765         var fn = this.createFilterFn(property, value, anyMatch);
21766         return fn ? this.queryBy(fn) : this.data.clone();
21767     },
21768
21769     /**
21770      * Query by a function. The specified function will be called with each
21771      * record in this data source. If the function returns true the record is included
21772      * in the results.
21773      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21774      * @param {Object} scope (optional) The scope of the function (defaults to this)
21775       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21776      **/
21777     queryBy : function(fn, scope){
21778         var data = this.snapshot || this.data;
21779         return data.filterBy(fn, scope||this);
21780     },
21781
21782     /**
21783      * Collects unique values for a particular dataIndex from this store.
21784      * @param {String} dataIndex The property to collect
21785      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21786      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21787      * @return {Array} An array of the unique values
21788      **/
21789     collect : function(dataIndex, allowNull, bypassFilter){
21790         var d = (bypassFilter === true && this.snapshot) ?
21791                 this.snapshot.items : this.data.items;
21792         var v, sv, r = [], l = {};
21793         for(var i = 0, len = d.length; i < len; i++){
21794             v = d[i].data[dataIndex];
21795             sv = String(v);
21796             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21797                 l[sv] = true;
21798                 r[r.length] = v;
21799             }
21800         }
21801         return r;
21802     },
21803
21804     /**
21805      * Revert to a view of the Record cache with no filtering applied.
21806      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21807      */
21808     clearFilter : function(suppressEvent){
21809         if(this.snapshot && this.snapshot != this.data){
21810             this.data = this.snapshot;
21811             delete this.snapshot;
21812             if(suppressEvent !== true){
21813                 this.fireEvent("datachanged", this);
21814             }
21815         }
21816     },
21817
21818     // private
21819     afterEdit : function(record){
21820         if(this.modified.indexOf(record) == -1){
21821             this.modified.push(record);
21822         }
21823         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21824     },
21825     
21826     // private
21827     afterReject : function(record){
21828         this.modified.remove(record);
21829         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21830     },
21831
21832     // private
21833     afterCommit : function(record){
21834         this.modified.remove(record);
21835         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21836     },
21837
21838     /**
21839      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21840      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21841      */
21842     commitChanges : function(){
21843         var m = this.modified.slice(0);
21844         this.modified = [];
21845         for(var i = 0, len = m.length; i < len; i++){
21846             m[i].commit();
21847         }
21848     },
21849
21850     /**
21851      * Cancel outstanding changes on all changed records.
21852      */
21853     rejectChanges : function(){
21854         var m = this.modified.slice(0);
21855         this.modified = [];
21856         for(var i = 0, len = m.length; i < len; i++){
21857             m[i].reject();
21858         }
21859     },
21860
21861     onMetaChange : function(meta, rtype, o){
21862         this.recordType = rtype;
21863         this.fields = rtype.prototype.fields;
21864         delete this.snapshot;
21865         this.sortInfo = meta.sortInfo || this.sortInfo;
21866         this.modified = [];
21867         this.fireEvent('metachange', this, this.reader.meta);
21868     },
21869     
21870     moveIndex : function(data, type)
21871     {
21872         var index = this.indexOf(data);
21873         
21874         var newIndex = index + type;
21875         
21876         this.remove(data);
21877         
21878         this.insert(newIndex, data);
21879         
21880     }
21881 });/*
21882  * Based on:
21883  * Ext JS Library 1.1.1
21884  * Copyright(c) 2006-2007, Ext JS, LLC.
21885  *
21886  * Originally Released Under LGPL - original licence link has changed is not relivant.
21887  *
21888  * Fork - LGPL
21889  * <script type="text/javascript">
21890  */
21891
21892 /**
21893  * @class Roo.data.SimpleStore
21894  * @extends Roo.data.Store
21895  * Small helper class to make creating Stores from Array data easier.
21896  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21897  * @cfg {Array} fields An array of field definition objects, or field name strings.
21898  * @cfg {Array} data The multi-dimensional array of data
21899  * @constructor
21900  * @param {Object} config
21901  */
21902 Roo.data.SimpleStore = function(config){
21903     Roo.data.SimpleStore.superclass.constructor.call(this, {
21904         isLocal : true,
21905         reader: new Roo.data.ArrayReader({
21906                 id: config.id
21907             },
21908             Roo.data.Record.create(config.fields)
21909         ),
21910         proxy : new Roo.data.MemoryProxy(config.data)
21911     });
21912     this.load();
21913 };
21914 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21915  * Based on:
21916  * Ext JS Library 1.1.1
21917  * Copyright(c) 2006-2007, Ext JS, LLC.
21918  *
21919  * Originally Released Under LGPL - original licence link has changed is not relivant.
21920  *
21921  * Fork - LGPL
21922  * <script type="text/javascript">
21923  */
21924
21925 /**
21926 /**
21927  * @extends Roo.data.Store
21928  * @class Roo.data.JsonStore
21929  * Small helper class to make creating Stores for JSON data easier. <br/>
21930 <pre><code>
21931 var store = new Roo.data.JsonStore({
21932     url: 'get-images.php',
21933     root: 'images',
21934     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21935 });
21936 </code></pre>
21937  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21938  * JsonReader and HttpProxy (unless inline data is provided).</b>
21939  * @cfg {Array} fields An array of field definition objects, or field name strings.
21940  * @constructor
21941  * @param {Object} config
21942  */
21943 Roo.data.JsonStore = function(c){
21944     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21945         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21946         reader: new Roo.data.JsonReader(c, c.fields)
21947     }));
21948 };
21949 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21950  * Based on:
21951  * Ext JS Library 1.1.1
21952  * Copyright(c) 2006-2007, Ext JS, LLC.
21953  *
21954  * Originally Released Under LGPL - original licence link has changed is not relivant.
21955  *
21956  * Fork - LGPL
21957  * <script type="text/javascript">
21958  */
21959
21960  
21961 Roo.data.Field = function(config){
21962     if(typeof config == "string"){
21963         config = {name: config};
21964     }
21965     Roo.apply(this, config);
21966     
21967     if(!this.type){
21968         this.type = "auto";
21969     }
21970     
21971     var st = Roo.data.SortTypes;
21972     // named sortTypes are supported, here we look them up
21973     if(typeof this.sortType == "string"){
21974         this.sortType = st[this.sortType];
21975     }
21976     
21977     // set default sortType for strings and dates
21978     if(!this.sortType){
21979         switch(this.type){
21980             case "string":
21981                 this.sortType = st.asUCString;
21982                 break;
21983             case "date":
21984                 this.sortType = st.asDate;
21985                 break;
21986             default:
21987                 this.sortType = st.none;
21988         }
21989     }
21990
21991     // define once
21992     var stripRe = /[\$,%]/g;
21993
21994     // prebuilt conversion function for this field, instead of
21995     // switching every time we're reading a value
21996     if(!this.convert){
21997         var cv, dateFormat = this.dateFormat;
21998         switch(this.type){
21999             case "":
22000             case "auto":
22001             case undefined:
22002                 cv = function(v){ return v; };
22003                 break;
22004             case "string":
22005                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22006                 break;
22007             case "int":
22008                 cv = function(v){
22009                     return v !== undefined && v !== null && v !== '' ?
22010                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22011                     };
22012                 break;
22013             case "float":
22014                 cv = function(v){
22015                     return v !== undefined && v !== null && v !== '' ?
22016                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22017                     };
22018                 break;
22019             case "bool":
22020             case "boolean":
22021                 cv = function(v){ return v === true || v === "true" || v == 1; };
22022                 break;
22023             case "date":
22024                 cv = function(v){
22025                     if(!v){
22026                         return '';
22027                     }
22028                     if(v instanceof Date){
22029                         return v;
22030                     }
22031                     if(dateFormat){
22032                         if(dateFormat == "timestamp"){
22033                             return new Date(v*1000);
22034                         }
22035                         return Date.parseDate(v, dateFormat);
22036                     }
22037                     var parsed = Date.parse(v);
22038                     return parsed ? new Date(parsed) : null;
22039                 };
22040              break;
22041             
22042         }
22043         this.convert = cv;
22044     }
22045 };
22046
22047 Roo.data.Field.prototype = {
22048     dateFormat: null,
22049     defaultValue: "",
22050     mapping: null,
22051     sortType : null,
22052     sortDir : "ASC"
22053 };/*
22054  * Based on:
22055  * Ext JS Library 1.1.1
22056  * Copyright(c) 2006-2007, Ext JS, LLC.
22057  *
22058  * Originally Released Under LGPL - original licence link has changed is not relivant.
22059  *
22060  * Fork - LGPL
22061  * <script type="text/javascript">
22062  */
22063  
22064 // Base class for reading structured data from a data source.  This class is intended to be
22065 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22066
22067 /**
22068  * @class Roo.data.DataReader
22069  * Base class for reading structured data from a data source.  This class is intended to be
22070  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22071  */
22072
22073 Roo.data.DataReader = function(meta, recordType){
22074     
22075     this.meta = meta;
22076     
22077     this.recordType = recordType instanceof Array ? 
22078         Roo.data.Record.create(recordType) : recordType;
22079 };
22080
22081 Roo.data.DataReader.prototype = {
22082      /**
22083      * Create an empty record
22084      * @param {Object} data (optional) - overlay some values
22085      * @return {Roo.data.Record} record created.
22086      */
22087     newRow :  function(d) {
22088         var da =  {};
22089         this.recordType.prototype.fields.each(function(c) {
22090             switch( c.type) {
22091                 case 'int' : da[c.name] = 0; break;
22092                 case 'date' : da[c.name] = new Date(); break;
22093                 case 'float' : da[c.name] = 0.0; break;
22094                 case 'boolean' : da[c.name] = false; break;
22095                 default : da[c.name] = ""; break;
22096             }
22097             
22098         });
22099         return new this.recordType(Roo.apply(da, d));
22100     }
22101     
22102 };/*
22103  * Based on:
22104  * Ext JS Library 1.1.1
22105  * Copyright(c) 2006-2007, Ext JS, LLC.
22106  *
22107  * Originally Released Under LGPL - original licence link has changed is not relivant.
22108  *
22109  * Fork - LGPL
22110  * <script type="text/javascript">
22111  */
22112
22113 /**
22114  * @class Roo.data.DataProxy
22115  * @extends Roo.data.Observable
22116  * This class is an abstract base class for implementations which provide retrieval of
22117  * unformatted data objects.<br>
22118  * <p>
22119  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22120  * (of the appropriate type which knows how to parse the data object) to provide a block of
22121  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22122  * <p>
22123  * Custom implementations must implement the load method as described in
22124  * {@link Roo.data.HttpProxy#load}.
22125  */
22126 Roo.data.DataProxy = function(){
22127     this.addEvents({
22128         /**
22129          * @event beforeload
22130          * Fires before a network request is made to retrieve a data object.
22131          * @param {Object} This DataProxy object.
22132          * @param {Object} params The params parameter to the load function.
22133          */
22134         beforeload : true,
22135         /**
22136          * @event load
22137          * Fires before the load method's callback is called.
22138          * @param {Object} This DataProxy object.
22139          * @param {Object} o The data object.
22140          * @param {Object} arg The callback argument object passed to the load function.
22141          */
22142         load : true,
22143         /**
22144          * @event loadexception
22145          * Fires if an Exception occurs during data retrieval.
22146          * @param {Object} This DataProxy object.
22147          * @param {Object} o The data object.
22148          * @param {Object} arg The callback argument object passed to the load function.
22149          * @param {Object} e The Exception.
22150          */
22151         loadexception : true
22152     });
22153     Roo.data.DataProxy.superclass.constructor.call(this);
22154 };
22155
22156 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22157
22158     /**
22159      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22160      */
22161 /*
22162  * Based on:
22163  * Ext JS Library 1.1.1
22164  * Copyright(c) 2006-2007, Ext JS, LLC.
22165  *
22166  * Originally Released Under LGPL - original licence link has changed is not relivant.
22167  *
22168  * Fork - LGPL
22169  * <script type="text/javascript">
22170  */
22171 /**
22172  * @class Roo.data.MemoryProxy
22173  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22174  * to the Reader when its load method is called.
22175  * @constructor
22176  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22177  */
22178 Roo.data.MemoryProxy = function(data){
22179     if (data.data) {
22180         data = data.data;
22181     }
22182     Roo.data.MemoryProxy.superclass.constructor.call(this);
22183     this.data = data;
22184 };
22185
22186 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22187     /**
22188      * Load data from the requested source (in this case an in-memory
22189      * data object passed to the constructor), read the data object into
22190      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22191      * process that block using the passed callback.
22192      * @param {Object} params This parameter is not used by the MemoryProxy class.
22193      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22194      * object into a block of Roo.data.Records.
22195      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22196      * The function must be passed <ul>
22197      * <li>The Record block object</li>
22198      * <li>The "arg" argument from the load function</li>
22199      * <li>A boolean success indicator</li>
22200      * </ul>
22201      * @param {Object} scope The scope in which to call the callback
22202      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22203      */
22204     load : function(params, reader, callback, scope, arg){
22205         params = params || {};
22206         var result;
22207         try {
22208             result = reader.readRecords(this.data);
22209         }catch(e){
22210             this.fireEvent("loadexception", this, arg, null, e);
22211             callback.call(scope, null, arg, false);
22212             return;
22213         }
22214         callback.call(scope, result, arg, true);
22215     },
22216     
22217     // private
22218     update : function(params, records){
22219         
22220     }
22221 });/*
22222  * Based on:
22223  * Ext JS Library 1.1.1
22224  * Copyright(c) 2006-2007, Ext JS, LLC.
22225  *
22226  * Originally Released Under LGPL - original licence link has changed is not relivant.
22227  *
22228  * Fork - LGPL
22229  * <script type="text/javascript">
22230  */
22231 /**
22232  * @class Roo.data.HttpProxy
22233  * @extends Roo.data.DataProxy
22234  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22235  * configured to reference a certain URL.<br><br>
22236  * <p>
22237  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22238  * from which the running page was served.<br><br>
22239  * <p>
22240  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22241  * <p>
22242  * Be aware that to enable the browser to parse an XML document, the server must set
22243  * the Content-Type header in the HTTP response to "text/xml".
22244  * @constructor
22245  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22246  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22247  * will be used to make the request.
22248  */
22249 Roo.data.HttpProxy = function(conn){
22250     Roo.data.HttpProxy.superclass.constructor.call(this);
22251     // is conn a conn config or a real conn?
22252     this.conn = conn;
22253     this.useAjax = !conn || !conn.events;
22254   
22255 };
22256
22257 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22258     // thse are take from connection...
22259     
22260     /**
22261      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22262      */
22263     /**
22264      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22265      * extra parameters to each request made by this object. (defaults to undefined)
22266      */
22267     /**
22268      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22269      *  to each request made by this object. (defaults to undefined)
22270      */
22271     /**
22272      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
22273      */
22274     /**
22275      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22276      */
22277      /**
22278      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22279      * @type Boolean
22280      */
22281   
22282
22283     /**
22284      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22285      * @type Boolean
22286      */
22287     /**
22288      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22289      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22290      * a finer-grained basis than the DataProxy events.
22291      */
22292     getConnection : function(){
22293         return this.useAjax ? Roo.Ajax : this.conn;
22294     },
22295
22296     /**
22297      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22298      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22299      * process that block using the passed callback.
22300      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22301      * for the request to the remote server.
22302      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22303      * object into a block of Roo.data.Records.
22304      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22305      * The function must be passed <ul>
22306      * <li>The Record block object</li>
22307      * <li>The "arg" argument from the load function</li>
22308      * <li>A boolean success indicator</li>
22309      * </ul>
22310      * @param {Object} scope The scope in which to call the callback
22311      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22312      */
22313     load : function(params, reader, callback, scope, arg){
22314         if(this.fireEvent("beforeload", this, params) !== false){
22315             var  o = {
22316                 params : params || {},
22317                 request: {
22318                     callback : callback,
22319                     scope : scope,
22320                     arg : arg
22321                 },
22322                 reader: reader,
22323                 callback : this.loadResponse,
22324                 scope: this
22325             };
22326             if(this.useAjax){
22327                 Roo.applyIf(o, this.conn);
22328                 if(this.activeRequest){
22329                     Roo.Ajax.abort(this.activeRequest);
22330                 }
22331                 this.activeRequest = Roo.Ajax.request(o);
22332             }else{
22333                 this.conn.request(o);
22334             }
22335         }else{
22336             callback.call(scope||this, null, arg, false);
22337         }
22338     },
22339
22340     // private
22341     loadResponse : function(o, success, response){
22342         delete this.activeRequest;
22343         if(!success){
22344             this.fireEvent("loadexception", this, o, response);
22345             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22346             return;
22347         }
22348         var result;
22349         try {
22350             result = o.reader.read(response);
22351         }catch(e){
22352             this.fireEvent("loadexception", this, o, response, e);
22353             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22354             return;
22355         }
22356         
22357         this.fireEvent("load", this, o, o.request.arg);
22358         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22359     },
22360
22361     // private
22362     update : function(dataSet){
22363
22364     },
22365
22366     // private
22367     updateResponse : function(dataSet){
22368
22369     }
22370 });/*
22371  * Based on:
22372  * Ext JS Library 1.1.1
22373  * Copyright(c) 2006-2007, Ext JS, LLC.
22374  *
22375  * Originally Released Under LGPL - original licence link has changed is not relivant.
22376  *
22377  * Fork - LGPL
22378  * <script type="text/javascript">
22379  */
22380
22381 /**
22382  * @class Roo.data.ScriptTagProxy
22383  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22384  * other than the originating domain of the running page.<br><br>
22385  * <p>
22386  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
22387  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22388  * <p>
22389  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22390  * source code that is used as the source inside a &lt;script> tag.<br><br>
22391  * <p>
22392  * In order for the browser to process the returned data, the server must wrap the data object
22393  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22394  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22395  * depending on whether the callback name was passed:
22396  * <p>
22397  * <pre><code>
22398 boolean scriptTag = false;
22399 String cb = request.getParameter("callback");
22400 if (cb != null) {
22401     scriptTag = true;
22402     response.setContentType("text/javascript");
22403 } else {
22404     response.setContentType("application/x-json");
22405 }
22406 Writer out = response.getWriter();
22407 if (scriptTag) {
22408     out.write(cb + "(");
22409 }
22410 out.print(dataBlock.toJsonString());
22411 if (scriptTag) {
22412     out.write(");");
22413 }
22414 </pre></code>
22415  *
22416  * @constructor
22417  * @param {Object} config A configuration object.
22418  */
22419 Roo.data.ScriptTagProxy = function(config){
22420     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22421     Roo.apply(this, config);
22422     this.head = document.getElementsByTagName("head")[0];
22423 };
22424
22425 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22426
22427 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22428     /**
22429      * @cfg {String} url The URL from which to request the data object.
22430      */
22431     /**
22432      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22433      */
22434     timeout : 30000,
22435     /**
22436      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22437      * the server the name of the callback function set up by the load call to process the returned data object.
22438      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22439      * javascript output which calls this named function passing the data object as its only parameter.
22440      */
22441     callbackParam : "callback",
22442     /**
22443      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22444      * name to the request.
22445      */
22446     nocache : true,
22447
22448     /**
22449      * Load data from the configured URL, read the data object into
22450      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22451      * process that block using the passed callback.
22452      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22453      * for the request to the remote server.
22454      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22455      * object into a block of Roo.data.Records.
22456      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22457      * The function must be passed <ul>
22458      * <li>The Record block object</li>
22459      * <li>The "arg" argument from the load function</li>
22460      * <li>A boolean success indicator</li>
22461      * </ul>
22462      * @param {Object} scope The scope in which to call the callback
22463      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22464      */
22465     load : function(params, reader, callback, scope, arg){
22466         if(this.fireEvent("beforeload", this, params) !== false){
22467
22468             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22469
22470             var url = this.url;
22471             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22472             if(this.nocache){
22473                 url += "&_dc=" + (new Date().getTime());
22474             }
22475             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22476             var trans = {
22477                 id : transId,
22478                 cb : "stcCallback"+transId,
22479                 scriptId : "stcScript"+transId,
22480                 params : params,
22481                 arg : arg,
22482                 url : url,
22483                 callback : callback,
22484                 scope : scope,
22485                 reader : reader
22486             };
22487             var conn = this;
22488
22489             window[trans.cb] = function(o){
22490                 conn.handleResponse(o, trans);
22491             };
22492
22493             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22494
22495             if(this.autoAbort !== false){
22496                 this.abort();
22497             }
22498
22499             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22500
22501             var script = document.createElement("script");
22502             script.setAttribute("src", url);
22503             script.setAttribute("type", "text/javascript");
22504             script.setAttribute("id", trans.scriptId);
22505             this.head.appendChild(script);
22506
22507             this.trans = trans;
22508         }else{
22509             callback.call(scope||this, null, arg, false);
22510         }
22511     },
22512
22513     // private
22514     isLoading : function(){
22515         return this.trans ? true : false;
22516     },
22517
22518     /**
22519      * Abort the current server request.
22520      */
22521     abort : function(){
22522         if(this.isLoading()){
22523             this.destroyTrans(this.trans);
22524         }
22525     },
22526
22527     // private
22528     destroyTrans : function(trans, isLoaded){
22529         this.head.removeChild(document.getElementById(trans.scriptId));
22530         clearTimeout(trans.timeoutId);
22531         if(isLoaded){
22532             window[trans.cb] = undefined;
22533             try{
22534                 delete window[trans.cb];
22535             }catch(e){}
22536         }else{
22537             // if hasn't been loaded, wait for load to remove it to prevent script error
22538             window[trans.cb] = function(){
22539                 window[trans.cb] = undefined;
22540                 try{
22541                     delete window[trans.cb];
22542                 }catch(e){}
22543             };
22544         }
22545     },
22546
22547     // private
22548     handleResponse : function(o, trans){
22549         this.trans = false;
22550         this.destroyTrans(trans, true);
22551         var result;
22552         try {
22553             result = trans.reader.readRecords(o);
22554         }catch(e){
22555             this.fireEvent("loadexception", this, o, trans.arg, e);
22556             trans.callback.call(trans.scope||window, null, trans.arg, false);
22557             return;
22558         }
22559         this.fireEvent("load", this, o, trans.arg);
22560         trans.callback.call(trans.scope||window, result, trans.arg, true);
22561     },
22562
22563     // private
22564     handleFailure : function(trans){
22565         this.trans = false;
22566         this.destroyTrans(trans, false);
22567         this.fireEvent("loadexception", this, null, trans.arg);
22568         trans.callback.call(trans.scope||window, null, trans.arg, false);
22569     }
22570 });/*
22571  * Based on:
22572  * Ext JS Library 1.1.1
22573  * Copyright(c) 2006-2007, Ext JS, LLC.
22574  *
22575  * Originally Released Under LGPL - original licence link has changed is not relivant.
22576  *
22577  * Fork - LGPL
22578  * <script type="text/javascript">
22579  */
22580
22581 /**
22582  * @class Roo.data.JsonReader
22583  * @extends Roo.data.DataReader
22584  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22585  * based on mappings in a provided Roo.data.Record constructor.
22586  * 
22587  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22588  * in the reply previously. 
22589  * 
22590  * <p>
22591  * Example code:
22592  * <pre><code>
22593 var RecordDef = Roo.data.Record.create([
22594     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22595     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22596 ]);
22597 var myReader = new Roo.data.JsonReader({
22598     totalProperty: "results",    // The property which contains the total dataset size (optional)
22599     root: "rows",                // The property which contains an Array of row objects
22600     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22601 }, RecordDef);
22602 </code></pre>
22603  * <p>
22604  * This would consume a JSON file like this:
22605  * <pre><code>
22606 { 'results': 2, 'rows': [
22607     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22608     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22609 }
22610 </code></pre>
22611  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22612  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22613  * paged from the remote server.
22614  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22615  * @cfg {String} root name of the property which contains the Array of row objects.
22616  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22617  * @constructor
22618  * Create a new JsonReader
22619  * @param {Object} meta Metadata configuration options
22620  * @param {Object} recordType Either an Array of field definition objects,
22621  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22622  */
22623 Roo.data.JsonReader = function(meta, recordType){
22624     
22625     meta = meta || {};
22626     // set some defaults:
22627     Roo.applyIf(meta, {
22628         totalProperty: 'total',
22629         successProperty : 'success',
22630         root : 'data',
22631         id : 'id'
22632     });
22633     
22634     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22635 };
22636 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22637     
22638     /**
22639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22640      * Used by Store query builder to append _requestMeta to params.
22641      * 
22642      */
22643     metaFromRemote : false,
22644     /**
22645      * This method is only used by a DataProxy which has retrieved data from a remote server.
22646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22647      * @return {Object} data A data block which is used by an Roo.data.Store object as
22648      * a cache of Roo.data.Records.
22649      */
22650     read : function(response){
22651         var json = response.responseText;
22652        
22653         var o = /* eval:var:o */ eval("("+json+")");
22654         if(!o) {
22655             throw {message: "JsonReader.read: Json object not found"};
22656         }
22657         
22658         if(o.metaData){
22659             
22660             delete this.ef;
22661             this.metaFromRemote = true;
22662             this.meta = o.metaData;
22663             this.recordType = Roo.data.Record.create(o.metaData.fields);
22664             this.onMetaChange(this.meta, this.recordType, o);
22665         }
22666         return this.readRecords(o);
22667     },
22668
22669     // private function a store will implement
22670     onMetaChange : function(meta, recordType, o){
22671
22672     },
22673
22674     /**
22675          * @ignore
22676          */
22677     simpleAccess: function(obj, subsc) {
22678         return obj[subsc];
22679     },
22680
22681         /**
22682          * @ignore
22683          */
22684     getJsonAccessor: function(){
22685         var re = /[\[\.]/;
22686         return function(expr) {
22687             try {
22688                 return(re.test(expr))
22689                     ? new Function("obj", "return obj." + expr)
22690                     : function(obj){
22691                         return obj[expr];
22692                     };
22693             } catch(e){}
22694             return Roo.emptyFn;
22695         };
22696     }(),
22697
22698     /**
22699      * Create a data block containing Roo.data.Records from an XML document.
22700      * @param {Object} o An object which contains an Array of row objects in the property specified
22701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22702      * which contains the total size of the dataset.
22703      * @return {Object} data A data block which is used by an Roo.data.Store object as
22704      * a cache of Roo.data.Records.
22705      */
22706     readRecords : function(o){
22707         /**
22708          * After any data loads, the raw JSON data is available for further custom processing.
22709          * @type Object
22710          */
22711         this.o = o;
22712         var s = this.meta, Record = this.recordType,
22713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22714
22715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22716         if (!this.ef) {
22717             if(s.totalProperty) {
22718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22719                 }
22720                 if(s.successProperty) {
22721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22722                 }
22723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22724                 if (s.id) {
22725                         var g = this.getJsonAccessor(s.id);
22726                         this.getId = function(rec) {
22727                                 var r = g(rec);  
22728                                 return (r === undefined || r === "") ? null : r;
22729                         };
22730                 } else {
22731                         this.getId = function(){return null;};
22732                 }
22733             this.ef = [];
22734             for(var jj = 0; jj < fl; jj++){
22735                 f = fi[jj];
22736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22737                 this.ef[jj] = this.getJsonAccessor(map);
22738             }
22739         }
22740
22741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22742         if(s.totalProperty){
22743             var vt = parseInt(this.getTotal(o), 10);
22744             if(!isNaN(vt)){
22745                 totalRecords = vt;
22746             }
22747         }
22748         if(s.successProperty){
22749             var vs = this.getSuccess(o);
22750             if(vs === false || vs === 'false'){
22751                 success = false;
22752             }
22753         }
22754         var records = [];
22755         for(var i = 0; i < c; i++){
22756                 var n = root[i];
22757             var values = {};
22758             var id = this.getId(n);
22759             for(var j = 0; j < fl; j++){
22760                 f = fi[j];
22761             var v = this.ef[j](n);
22762             if (!f.convert) {
22763                 Roo.log('missing convert for ' + f.name);
22764                 Roo.log(f);
22765                 continue;
22766             }
22767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22768             }
22769             var record = new Record(values, id);
22770             record.json = n;
22771             records[i] = record;
22772         }
22773         return {
22774             raw : o,
22775             success : success,
22776             records : records,
22777             totalRecords : totalRecords
22778         };
22779     }
22780 });/*
22781  * Based on:
22782  * Ext JS Library 1.1.1
22783  * Copyright(c) 2006-2007, Ext JS, LLC.
22784  *
22785  * Originally Released Under LGPL - original licence link has changed is not relivant.
22786  *
22787  * Fork - LGPL
22788  * <script type="text/javascript">
22789  */
22790
22791 /**
22792  * @class Roo.data.XmlReader
22793  * @extends Roo.data.DataReader
22794  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22795  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22796  * <p>
22797  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22798  * header in the HTTP response must be set to "text/xml".</em>
22799  * <p>
22800  * Example code:
22801  * <pre><code>
22802 var RecordDef = Roo.data.Record.create([
22803    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22804    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22805 ]);
22806 var myReader = new Roo.data.XmlReader({
22807    totalRecords: "results", // The element which contains the total dataset size (optional)
22808    record: "row",           // The repeated element which contains row information
22809    id: "id"                 // The element within the row that provides an ID for the record (optional)
22810 }, RecordDef);
22811 </code></pre>
22812  * <p>
22813  * This would consume an XML file like this:
22814  * <pre><code>
22815 &lt;?xml?>
22816 &lt;dataset>
22817  &lt;results>2&lt;/results>
22818  &lt;row>
22819    &lt;id>1&lt;/id>
22820    &lt;name>Bill&lt;/name>
22821    &lt;occupation>Gardener&lt;/occupation>
22822  &lt;/row>
22823  &lt;row>
22824    &lt;id>2&lt;/id>
22825    &lt;name>Ben&lt;/name>
22826    &lt;occupation>Horticulturalist&lt;/occupation>
22827  &lt;/row>
22828 &lt;/dataset>
22829 </code></pre>
22830  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22831  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22832  * paged from the remote server.
22833  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22834  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22835  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22836  * a record identifier value.
22837  * @constructor
22838  * Create a new XmlReader
22839  * @param {Object} meta Metadata configuration options
22840  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22841  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22842  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22843  */
22844 Roo.data.XmlReader = function(meta, recordType){
22845     meta = meta || {};
22846     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22847 };
22848 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22849     /**
22850      * This method is only used by a DataProxy which has retrieved data from a remote server.
22851          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22852          * to contain a method called 'responseXML' that returns an XML document object.
22853      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22854      * a cache of Roo.data.Records.
22855      */
22856     read : function(response){
22857         var doc = response.responseXML;
22858         if(!doc) {
22859             throw {message: "XmlReader.read: XML Document not available"};
22860         }
22861         return this.readRecords(doc);
22862     },
22863
22864     /**
22865      * Create a data block containing Roo.data.Records from an XML document.
22866          * @param {Object} doc A parsed XML document.
22867      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22868      * a cache of Roo.data.Records.
22869      */
22870     readRecords : function(doc){
22871         /**
22872          * After any data loads/reads, the raw XML Document is available for further custom processing.
22873          * @type XMLDocument
22874          */
22875         this.xmlData = doc;
22876         var root = doc.documentElement || doc;
22877         var q = Roo.DomQuery;
22878         var recordType = this.recordType, fields = recordType.prototype.fields;
22879         var sid = this.meta.id;
22880         var totalRecords = 0, success = true;
22881         if(this.meta.totalRecords){
22882             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22883         }
22884         
22885         if(this.meta.success){
22886             var sv = q.selectValue(this.meta.success, root, true);
22887             success = sv !== false && sv !== 'false';
22888         }
22889         var records = [];
22890         var ns = q.select(this.meta.record, root);
22891         for(var i = 0, len = ns.length; i < len; i++) {
22892                 var n = ns[i];
22893                 var values = {};
22894                 var id = sid ? q.selectValue(sid, n) : undefined;
22895                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22896                     var f = fields.items[j];
22897                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22898                     v = f.convert(v);
22899                     values[f.name] = v;
22900                 }
22901                 var record = new recordType(values, id);
22902                 record.node = n;
22903                 records[records.length] = record;
22904             }
22905
22906             return {
22907                 success : success,
22908                 records : records,
22909                 totalRecords : totalRecords || records.length
22910             };
22911     }
22912 });/*
22913  * Based on:
22914  * Ext JS Library 1.1.1
22915  * Copyright(c) 2006-2007, Ext JS, LLC.
22916  *
22917  * Originally Released Under LGPL - original licence link has changed is not relivant.
22918  *
22919  * Fork - LGPL
22920  * <script type="text/javascript">
22921  */
22922
22923 /**
22924  * @class Roo.data.ArrayReader
22925  * @extends Roo.data.DataReader
22926  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22927  * Each element of that Array represents a row of data fields. The
22928  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22929  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22930  * <p>
22931  * Example code:.
22932  * <pre><code>
22933 var RecordDef = Roo.data.Record.create([
22934     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22935     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22936 ]);
22937 var myReader = new Roo.data.ArrayReader({
22938     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22939 }, RecordDef);
22940 </code></pre>
22941  * <p>
22942  * This would consume an Array like this:
22943  * <pre><code>
22944 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22945   </code></pre>
22946  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22947  * @constructor
22948  * Create a new JsonReader
22949  * @param {Object} meta Metadata configuration options.
22950  * @param {Object} recordType Either an Array of field definition objects
22951  * as specified to {@link Roo.data.Record#create},
22952  * or an {@link Roo.data.Record} object
22953  * created using {@link Roo.data.Record#create}.
22954  */
22955 Roo.data.ArrayReader = function(meta, recordType){
22956     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22957 };
22958
22959 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22960     /**
22961      * Create a data block containing Roo.data.Records from an XML document.
22962      * @param {Object} o An Array of row objects which represents the dataset.
22963      * @return {Object} data A data block which is used by an Roo.data.Store object as
22964      * a cache of Roo.data.Records.
22965      */
22966     readRecords : function(o){
22967         var sid = this.meta ? this.meta.id : null;
22968         var recordType = this.recordType, fields = recordType.prototype.fields;
22969         var records = [];
22970         var root = o;
22971             for(var i = 0; i < root.length; i++){
22972                     var n = root[i];
22973                 var values = {};
22974                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22975                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22976                 var f = fields.items[j];
22977                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22978                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22979                 v = f.convert(v);
22980                 values[f.name] = v;
22981             }
22982                 var record = new recordType(values, id);
22983                 record.json = n;
22984                 records[records.length] = record;
22985             }
22986             return {
22987                 records : records,
22988                 totalRecords : records.length
22989             };
22990     }
22991 });/*
22992  * Based on:
22993  * Ext JS Library 1.1.1
22994  * Copyright(c) 2006-2007, Ext JS, LLC.
22995  *
22996  * Originally Released Under LGPL - original licence link has changed is not relivant.
22997  *
22998  * Fork - LGPL
22999  * <script type="text/javascript">
23000  */
23001
23002
23003 /**
23004  * @class Roo.data.Tree
23005  * @extends Roo.util.Observable
23006  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23007  * in the tree have most standard DOM functionality.
23008  * @constructor
23009  * @param {Node} root (optional) The root node
23010  */
23011 Roo.data.Tree = function(root){
23012    this.nodeHash = {};
23013    /**
23014     * The root node for this tree
23015     * @type Node
23016     */
23017    this.root = null;
23018    if(root){
23019        this.setRootNode(root);
23020    }
23021    this.addEvents({
23022        /**
23023         * @event append
23024         * Fires when a new child node is appended to a node in this tree.
23025         * @param {Tree} tree The owner tree
23026         * @param {Node} parent The parent node
23027         * @param {Node} node The newly appended node
23028         * @param {Number} index The index of the newly appended node
23029         */
23030        "append" : true,
23031        /**
23032         * @event remove
23033         * Fires when a child node is removed from a node in this tree.
23034         * @param {Tree} tree The owner tree
23035         * @param {Node} parent The parent node
23036         * @param {Node} node The child node removed
23037         */
23038        "remove" : true,
23039        /**
23040         * @event move
23041         * Fires when a node is moved to a new location in the tree
23042         * @param {Tree} tree The owner tree
23043         * @param {Node} node The node moved
23044         * @param {Node} oldParent The old parent of this node
23045         * @param {Node} newParent The new parent of this node
23046         * @param {Number} index The index it was moved to
23047         */
23048        "move" : true,
23049        /**
23050         * @event insert
23051         * Fires when a new child node is inserted in a node in this tree.
23052         * @param {Tree} tree The owner tree
23053         * @param {Node} parent The parent node
23054         * @param {Node} node The child node inserted
23055         * @param {Node} refNode The child node the node was inserted before
23056         */
23057        "insert" : true,
23058        /**
23059         * @event beforeappend
23060         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23061         * @param {Tree} tree The owner tree
23062         * @param {Node} parent The parent node
23063         * @param {Node} node The child node to be appended
23064         */
23065        "beforeappend" : true,
23066        /**
23067         * @event beforeremove
23068         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} parent The parent node
23071         * @param {Node} node The child node to be removed
23072         */
23073        "beforeremove" : true,
23074        /**
23075         * @event beforemove
23076         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23077         * @param {Tree} tree The owner tree
23078         * @param {Node} node The node being moved
23079         * @param {Node} oldParent The parent of the node
23080         * @param {Node} newParent The new parent the node is moving to
23081         * @param {Number} index The index it is being moved to
23082         */
23083        "beforemove" : true,
23084        /**
23085         * @event beforeinsert
23086         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23087         * @param {Tree} tree The owner tree
23088         * @param {Node} parent The parent node
23089         * @param {Node} node The child node to be inserted
23090         * @param {Node} refNode The child node the node is being inserted before
23091         */
23092        "beforeinsert" : true
23093    });
23094
23095     Roo.data.Tree.superclass.constructor.call(this);
23096 };
23097
23098 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23099     pathSeparator: "/",
23100
23101     proxyNodeEvent : function(){
23102         return this.fireEvent.apply(this, arguments);
23103     },
23104
23105     /**
23106      * Returns the root node for this tree.
23107      * @return {Node}
23108      */
23109     getRootNode : function(){
23110         return this.root;
23111     },
23112
23113     /**
23114      * Sets the root node for this tree.
23115      * @param {Node} node
23116      * @return {Node}
23117      */
23118     setRootNode : function(node){
23119         this.root = node;
23120         node.ownerTree = this;
23121         node.isRoot = true;
23122         this.registerNode(node);
23123         return node;
23124     },
23125
23126     /**
23127      * Gets a node in this tree by its id.
23128      * @param {String} id
23129      * @return {Node}
23130      */
23131     getNodeById : function(id){
23132         return this.nodeHash[id];
23133     },
23134
23135     registerNode : function(node){
23136         this.nodeHash[node.id] = node;
23137     },
23138
23139     unregisterNode : function(node){
23140         delete this.nodeHash[node.id];
23141     },
23142
23143     toString : function(){
23144         return "[Tree"+(this.id?" "+this.id:"")+"]";
23145     }
23146 });
23147
23148 /**
23149  * @class Roo.data.Node
23150  * @extends Roo.util.Observable
23151  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23152  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23153  * @constructor
23154  * @param {Object} attributes The attributes/config for the node
23155  */
23156 Roo.data.Node = function(attributes){
23157     /**
23158      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23159      * @type {Object}
23160      */
23161     this.attributes = attributes || {};
23162     this.leaf = this.attributes.leaf;
23163     /**
23164      * The node id. @type String
23165      */
23166     this.id = this.attributes.id;
23167     if(!this.id){
23168         this.id = Roo.id(null, "ynode-");
23169         this.attributes.id = this.id;
23170     }
23171      
23172     
23173     /**
23174      * All child nodes of this node. @type Array
23175      */
23176     this.childNodes = [];
23177     if(!this.childNodes.indexOf){ // indexOf is a must
23178         this.childNodes.indexOf = function(o){
23179             for(var i = 0, len = this.length; i < len; i++){
23180                 if(this[i] == o) {
23181                     return i;
23182                 }
23183             }
23184             return -1;
23185         };
23186     }
23187     /**
23188      * The parent node for this node. @type Node
23189      */
23190     this.parentNode = null;
23191     /**
23192      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23193      */
23194     this.firstChild = null;
23195     /**
23196      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23197      */
23198     this.lastChild = null;
23199     /**
23200      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23201      */
23202     this.previousSibling = null;
23203     /**
23204      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23205      */
23206     this.nextSibling = null;
23207
23208     this.addEvents({
23209        /**
23210         * @event append
23211         * Fires when a new child node is appended
23212         * @param {Tree} tree The owner tree
23213         * @param {Node} this This node
23214         * @param {Node} node The newly appended node
23215         * @param {Number} index The index of the newly appended node
23216         */
23217        "append" : true,
23218        /**
23219         * @event remove
23220         * Fires when a child node is removed
23221         * @param {Tree} tree The owner tree
23222         * @param {Node} this This node
23223         * @param {Node} node The removed node
23224         */
23225        "remove" : true,
23226        /**
23227         * @event move
23228         * Fires when this node is moved to a new location in the tree
23229         * @param {Tree} tree The owner tree
23230         * @param {Node} this This node
23231         * @param {Node} oldParent The old parent of this node
23232         * @param {Node} newParent The new parent of this node
23233         * @param {Number} index The index it was moved to
23234         */
23235        "move" : true,
23236        /**
23237         * @event insert
23238         * Fires when a new child node is inserted.
23239         * @param {Tree} tree The owner tree
23240         * @param {Node} this This node
23241         * @param {Node} node The child node inserted
23242         * @param {Node} refNode The child node the node was inserted before
23243         */
23244        "insert" : true,
23245        /**
23246         * @event beforeappend
23247         * Fires before a new child is appended, return false to cancel the append.
23248         * @param {Tree} tree The owner tree
23249         * @param {Node} this This node
23250         * @param {Node} node The child node to be appended
23251         */
23252        "beforeappend" : true,
23253        /**
23254         * @event beforeremove
23255         * Fires before a child is removed, return false to cancel the remove.
23256         * @param {Tree} tree The owner tree
23257         * @param {Node} this This node
23258         * @param {Node} node The child node to be removed
23259         */
23260        "beforeremove" : true,
23261        /**
23262         * @event beforemove
23263         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23264         * @param {Tree} tree The owner tree
23265         * @param {Node} this This node
23266         * @param {Node} oldParent The parent of this node
23267         * @param {Node} newParent The new parent this node is moving to
23268         * @param {Number} index The index it is being moved to
23269         */
23270        "beforemove" : true,
23271        /**
23272         * @event beforeinsert
23273         * Fires before a new child is inserted, return false to cancel the insert.
23274         * @param {Tree} tree The owner tree
23275         * @param {Node} this This node
23276         * @param {Node} node The child node to be inserted
23277         * @param {Node} refNode The child node the node is being inserted before
23278         */
23279        "beforeinsert" : true
23280    });
23281     this.listeners = this.attributes.listeners;
23282     Roo.data.Node.superclass.constructor.call(this);
23283 };
23284
23285 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23286     fireEvent : function(evtName){
23287         // first do standard event for this node
23288         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23289             return false;
23290         }
23291         // then bubble it up to the tree if the event wasn't cancelled
23292         var ot = this.getOwnerTree();
23293         if(ot){
23294             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23295                 return false;
23296             }
23297         }
23298         return true;
23299     },
23300
23301     /**
23302      * Returns true if this node is a leaf
23303      * @return {Boolean}
23304      */
23305     isLeaf : function(){
23306         return this.leaf === true;
23307     },
23308
23309     // private
23310     setFirstChild : function(node){
23311         this.firstChild = node;
23312     },
23313
23314     //private
23315     setLastChild : function(node){
23316         this.lastChild = node;
23317     },
23318
23319
23320     /**
23321      * Returns true if this node is the last child of its parent
23322      * @return {Boolean}
23323      */
23324     isLast : function(){
23325        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23326     },
23327
23328     /**
23329      * Returns true if this node is the first child of its parent
23330      * @return {Boolean}
23331      */
23332     isFirst : function(){
23333        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23334     },
23335
23336     hasChildNodes : function(){
23337         return !this.isLeaf() && this.childNodes.length > 0;
23338     },
23339
23340     /**
23341      * Insert node(s) as the last child node of this node.
23342      * @param {Node/Array} node The node or Array of nodes to append
23343      * @return {Node} The appended node if single append, or null if an array was passed
23344      */
23345     appendChild : function(node){
23346         var multi = false;
23347         if(node instanceof Array){
23348             multi = node;
23349         }else if(arguments.length > 1){
23350             multi = arguments;
23351         }
23352         // if passed an array or multiple args do them one by one
23353         if(multi){
23354             for(var i = 0, len = multi.length; i < len; i++) {
23355                 this.appendChild(multi[i]);
23356             }
23357         }else{
23358             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23359                 return false;
23360             }
23361             var index = this.childNodes.length;
23362             var oldParent = node.parentNode;
23363             // it's a move, make sure we move it cleanly
23364             if(oldParent){
23365                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23366                     return false;
23367                 }
23368                 oldParent.removeChild(node);
23369             }
23370             index = this.childNodes.length;
23371             if(index == 0){
23372                 this.setFirstChild(node);
23373             }
23374             this.childNodes.push(node);
23375             node.parentNode = this;
23376             var ps = this.childNodes[index-1];
23377             if(ps){
23378                 node.previousSibling = ps;
23379                 ps.nextSibling = node;
23380             }else{
23381                 node.previousSibling = null;
23382             }
23383             node.nextSibling = null;
23384             this.setLastChild(node);
23385             node.setOwnerTree(this.getOwnerTree());
23386             this.fireEvent("append", this.ownerTree, this, node, index);
23387             if(oldParent){
23388                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23389             }
23390             return node;
23391         }
23392     },
23393
23394     /**
23395      * Removes a child node from this node.
23396      * @param {Node} node The node to remove
23397      * @return {Node} The removed node
23398      */
23399     removeChild : function(node){
23400         var index = this.childNodes.indexOf(node);
23401         if(index == -1){
23402             return false;
23403         }
23404         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23405             return false;
23406         }
23407
23408         // remove it from childNodes collection
23409         this.childNodes.splice(index, 1);
23410
23411         // update siblings
23412         if(node.previousSibling){
23413             node.previousSibling.nextSibling = node.nextSibling;
23414         }
23415         if(node.nextSibling){
23416             node.nextSibling.previousSibling = node.previousSibling;
23417         }
23418
23419         // update child refs
23420         if(this.firstChild == node){
23421             this.setFirstChild(node.nextSibling);
23422         }
23423         if(this.lastChild == node){
23424             this.setLastChild(node.previousSibling);
23425         }
23426
23427         node.setOwnerTree(null);
23428         // clear any references from the node
23429         node.parentNode = null;
23430         node.previousSibling = null;
23431         node.nextSibling = null;
23432         this.fireEvent("remove", this.ownerTree, this, node);
23433         return node;
23434     },
23435
23436     /**
23437      * Inserts the first node before the second node in this nodes childNodes collection.
23438      * @param {Node} node The node to insert
23439      * @param {Node} refNode The node to insert before (if null the node is appended)
23440      * @return {Node} The inserted node
23441      */
23442     insertBefore : function(node, refNode){
23443         if(!refNode){ // like standard Dom, refNode can be null for append
23444             return this.appendChild(node);
23445         }
23446         // nothing to do
23447         if(node == refNode){
23448             return false;
23449         }
23450
23451         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23452             return false;
23453         }
23454         var index = this.childNodes.indexOf(refNode);
23455         var oldParent = node.parentNode;
23456         var refIndex = index;
23457
23458         // when moving internally, indexes will change after remove
23459         if(oldParent == this && this.childNodes.indexOf(node) < index){
23460             refIndex--;
23461         }
23462
23463         // it's a move, make sure we move it cleanly
23464         if(oldParent){
23465             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23466                 return false;
23467             }
23468             oldParent.removeChild(node);
23469         }
23470         if(refIndex == 0){
23471             this.setFirstChild(node);
23472         }
23473         this.childNodes.splice(refIndex, 0, node);
23474         node.parentNode = this;
23475         var ps = this.childNodes[refIndex-1];
23476         if(ps){
23477             node.previousSibling = ps;
23478             ps.nextSibling = node;
23479         }else{
23480             node.previousSibling = null;
23481         }
23482         node.nextSibling = refNode;
23483         refNode.previousSibling = node;
23484         node.setOwnerTree(this.getOwnerTree());
23485         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23486         if(oldParent){
23487             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23488         }
23489         return node;
23490     },
23491
23492     /**
23493      * Returns the child node at the specified index.
23494      * @param {Number} index
23495      * @return {Node}
23496      */
23497     item : function(index){
23498         return this.childNodes[index];
23499     },
23500
23501     /**
23502      * Replaces one child node in this node with another.
23503      * @param {Node} newChild The replacement node
23504      * @param {Node} oldChild The node to replace
23505      * @return {Node} The replaced node
23506      */
23507     replaceChild : function(newChild, oldChild){
23508         this.insertBefore(newChild, oldChild);
23509         this.removeChild(oldChild);
23510         return oldChild;
23511     },
23512
23513     /**
23514      * Returns the index of a child node
23515      * @param {Node} node
23516      * @return {Number} The index of the node or -1 if it was not found
23517      */
23518     indexOf : function(child){
23519         return this.childNodes.indexOf(child);
23520     },
23521
23522     /**
23523      * Returns the tree this node is in.
23524      * @return {Tree}
23525      */
23526     getOwnerTree : function(){
23527         // if it doesn't have one, look for one
23528         if(!this.ownerTree){
23529             var p = this;
23530             while(p){
23531                 if(p.ownerTree){
23532                     this.ownerTree = p.ownerTree;
23533                     break;
23534                 }
23535                 p = p.parentNode;
23536             }
23537         }
23538         return this.ownerTree;
23539     },
23540
23541     /**
23542      * Returns depth of this node (the root node has a depth of 0)
23543      * @return {Number}
23544      */
23545     getDepth : function(){
23546         var depth = 0;
23547         var p = this;
23548         while(p.parentNode){
23549             ++depth;
23550             p = p.parentNode;
23551         }
23552         return depth;
23553     },
23554
23555     // private
23556     setOwnerTree : function(tree){
23557         // if it's move, we need to update everyone
23558         if(tree != this.ownerTree){
23559             if(this.ownerTree){
23560                 this.ownerTree.unregisterNode(this);
23561             }
23562             this.ownerTree = tree;
23563             var cs = this.childNodes;
23564             for(var i = 0, len = cs.length; i < len; i++) {
23565                 cs[i].setOwnerTree(tree);
23566             }
23567             if(tree){
23568                 tree.registerNode(this);
23569             }
23570         }
23571     },
23572
23573     /**
23574      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23575      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23576      * @return {String} The path
23577      */
23578     getPath : function(attr){
23579         attr = attr || "id";
23580         var p = this.parentNode;
23581         var b = [this.attributes[attr]];
23582         while(p){
23583             b.unshift(p.attributes[attr]);
23584             p = p.parentNode;
23585         }
23586         var sep = this.getOwnerTree().pathSeparator;
23587         return sep + b.join(sep);
23588     },
23589
23590     /**
23591      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23592      * function call will be the scope provided or the current node. The arguments to the function
23593      * will be the args provided or the current node. If the function returns false at any point,
23594      * the bubble is stopped.
23595      * @param {Function} fn The function to call
23596      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23597      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23598      */
23599     bubble : function(fn, scope, args){
23600         var p = this;
23601         while(p){
23602             if(fn.call(scope || p, args || p) === false){
23603                 break;
23604             }
23605             p = p.parentNode;
23606         }
23607     },
23608
23609     /**
23610      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23611      * function call will be the scope provided or the current node. The arguments to the function
23612      * will be the args provided or the current node. If the function returns false at any point,
23613      * the cascade is stopped on that branch.
23614      * @param {Function} fn The function to call
23615      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23616      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23617      */
23618     cascade : function(fn, scope, args){
23619         if(fn.call(scope || this, args || this) !== false){
23620             var cs = this.childNodes;
23621             for(var i = 0, len = cs.length; i < len; i++) {
23622                 cs[i].cascade(fn, scope, args);
23623             }
23624         }
23625     },
23626
23627     /**
23628      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23629      * function call will be the scope provided or the current node. The arguments to the function
23630      * will be the args provided or the current node. If the function returns false at any point,
23631      * the iteration stops.
23632      * @param {Function} fn The function to call
23633      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23634      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23635      */
23636     eachChild : function(fn, scope, args){
23637         var cs = this.childNodes;
23638         for(var i = 0, len = cs.length; i < len; i++) {
23639                 if(fn.call(scope || this, args || cs[i]) === false){
23640                     break;
23641                 }
23642         }
23643     },
23644
23645     /**
23646      * Finds the first child that has the attribute with the specified value.
23647      * @param {String} attribute The attribute name
23648      * @param {Mixed} value The value to search for
23649      * @return {Node} The found child or null if none was found
23650      */
23651     findChild : function(attribute, value){
23652         var cs = this.childNodes;
23653         for(var i = 0, len = cs.length; i < len; i++) {
23654                 if(cs[i].attributes[attribute] == value){
23655                     return cs[i];
23656                 }
23657         }
23658         return null;
23659     },
23660
23661     /**
23662      * Finds the first child by a custom function. The child matches if the function passed
23663      * returns true.
23664      * @param {Function} fn
23665      * @param {Object} scope (optional)
23666      * @return {Node} The found child or null if none was found
23667      */
23668     findChildBy : function(fn, scope){
23669         var cs = this.childNodes;
23670         for(var i = 0, len = cs.length; i < len; i++) {
23671                 if(fn.call(scope||cs[i], cs[i]) === true){
23672                     return cs[i];
23673                 }
23674         }
23675         return null;
23676     },
23677
23678     /**
23679      * Sorts this nodes children using the supplied sort function
23680      * @param {Function} fn
23681      * @param {Object} scope (optional)
23682      */
23683     sort : function(fn, scope){
23684         var cs = this.childNodes;
23685         var len = cs.length;
23686         if(len > 0){
23687             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23688             cs.sort(sortFn);
23689             for(var i = 0; i < len; i++){
23690                 var n = cs[i];
23691                 n.previousSibling = cs[i-1];
23692                 n.nextSibling = cs[i+1];
23693                 if(i == 0){
23694                     this.setFirstChild(n);
23695                 }
23696                 if(i == len-1){
23697                     this.setLastChild(n);
23698                 }
23699             }
23700         }
23701     },
23702
23703     /**
23704      * Returns true if this node is an ancestor (at any point) of the passed node.
23705      * @param {Node} node
23706      * @return {Boolean}
23707      */
23708     contains : function(node){
23709         return node.isAncestor(this);
23710     },
23711
23712     /**
23713      * Returns true if the passed node is an ancestor (at any point) of this node.
23714      * @param {Node} node
23715      * @return {Boolean}
23716      */
23717     isAncestor : function(node){
23718         var p = this.parentNode;
23719         while(p){
23720             if(p == node){
23721                 return true;
23722             }
23723             p = p.parentNode;
23724         }
23725         return false;
23726     },
23727
23728     toString : function(){
23729         return "[Node"+(this.id?" "+this.id:"")+"]";
23730     }
23731 });/*
23732  * Based on:
23733  * Ext JS Library 1.1.1
23734  * Copyright(c) 2006-2007, Ext JS, LLC.
23735  *
23736  * Originally Released Under LGPL - original licence link has changed is not relivant.
23737  *
23738  * Fork - LGPL
23739  * <script type="text/javascript">
23740  */
23741  (function(){ 
23742 /**
23743  * @class Roo.Layer
23744  * @extends Roo.Element
23745  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23746  * automatic maintaining of shadow/shim positions.
23747  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23748  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23749  * you can pass a string with a CSS class name. False turns off the shadow.
23750  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23751  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23752  * @cfg {String} cls CSS class to add to the element
23753  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23754  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23755  * @constructor
23756  * @param {Object} config An object with config options.
23757  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23758  */
23759
23760 Roo.Layer = function(config, existingEl){
23761     config = config || {};
23762     var dh = Roo.DomHelper;
23763     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23764     if(existingEl){
23765         this.dom = Roo.getDom(existingEl);
23766     }
23767     if(!this.dom){
23768         var o = config.dh || {tag: "div", cls: "x-layer"};
23769         this.dom = dh.append(pel, o);
23770     }
23771     if(config.cls){
23772         this.addClass(config.cls);
23773     }
23774     this.constrain = config.constrain !== false;
23775     this.visibilityMode = Roo.Element.VISIBILITY;
23776     if(config.id){
23777         this.id = this.dom.id = config.id;
23778     }else{
23779         this.id = Roo.id(this.dom);
23780     }
23781     this.zindex = config.zindex || this.getZIndex();
23782     this.position("absolute", this.zindex);
23783     if(config.shadow){
23784         this.shadowOffset = config.shadowOffset || 4;
23785         this.shadow = new Roo.Shadow({
23786             offset : this.shadowOffset,
23787             mode : config.shadow
23788         });
23789     }else{
23790         this.shadowOffset = 0;
23791     }
23792     this.useShim = config.shim !== false && Roo.useShims;
23793     this.useDisplay = config.useDisplay;
23794     this.hide();
23795 };
23796
23797 var supr = Roo.Element.prototype;
23798
23799 // shims are shared among layer to keep from having 100 iframes
23800 var shims = [];
23801
23802 Roo.extend(Roo.Layer, Roo.Element, {
23803
23804     getZIndex : function(){
23805         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23806     },
23807
23808     getShim : function(){
23809         if(!this.useShim){
23810             return null;
23811         }
23812         if(this.shim){
23813             return this.shim;
23814         }
23815         var shim = shims.shift();
23816         if(!shim){
23817             shim = this.createShim();
23818             shim.enableDisplayMode('block');
23819             shim.dom.style.display = 'none';
23820             shim.dom.style.visibility = 'visible';
23821         }
23822         var pn = this.dom.parentNode;
23823         if(shim.dom.parentNode != pn){
23824             pn.insertBefore(shim.dom, this.dom);
23825         }
23826         shim.setStyle('z-index', this.getZIndex()-2);
23827         this.shim = shim;
23828         return shim;
23829     },
23830
23831     hideShim : function(){
23832         if(this.shim){
23833             this.shim.setDisplayed(false);
23834             shims.push(this.shim);
23835             delete this.shim;
23836         }
23837     },
23838
23839     disableShadow : function(){
23840         if(this.shadow){
23841             this.shadowDisabled = true;
23842             this.shadow.hide();
23843             this.lastShadowOffset = this.shadowOffset;
23844             this.shadowOffset = 0;
23845         }
23846     },
23847
23848     enableShadow : function(show){
23849         if(this.shadow){
23850             this.shadowDisabled = false;
23851             this.shadowOffset = this.lastShadowOffset;
23852             delete this.lastShadowOffset;
23853             if(show){
23854                 this.sync(true);
23855             }
23856         }
23857     },
23858
23859     // private
23860     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23861     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23862     sync : function(doShow){
23863         var sw = this.shadow;
23864         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23865             var sh = this.getShim();
23866
23867             var w = this.getWidth(),
23868                 h = this.getHeight();
23869
23870             var l = this.getLeft(true),
23871                 t = this.getTop(true);
23872
23873             if(sw && !this.shadowDisabled){
23874                 if(doShow && !sw.isVisible()){
23875                     sw.show(this);
23876                 }else{
23877                     sw.realign(l, t, w, h);
23878                 }
23879                 if(sh){
23880                     if(doShow){
23881                        sh.show();
23882                     }
23883                     // fit the shim behind the shadow, so it is shimmed too
23884                     var a = sw.adjusts, s = sh.dom.style;
23885                     s.left = (Math.min(l, l+a.l))+"px";
23886                     s.top = (Math.min(t, t+a.t))+"px";
23887                     s.width = (w+a.w)+"px";
23888                     s.height = (h+a.h)+"px";
23889                 }
23890             }else if(sh){
23891                 if(doShow){
23892                    sh.show();
23893                 }
23894                 sh.setSize(w, h);
23895                 sh.setLeftTop(l, t);
23896             }
23897             
23898         }
23899     },
23900
23901     // private
23902     destroy : function(){
23903         this.hideShim();
23904         if(this.shadow){
23905             this.shadow.hide();
23906         }
23907         this.removeAllListeners();
23908         var pn = this.dom.parentNode;
23909         if(pn){
23910             pn.removeChild(this.dom);
23911         }
23912         Roo.Element.uncache(this.id);
23913     },
23914
23915     remove : function(){
23916         this.destroy();
23917     },
23918
23919     // private
23920     beginUpdate : function(){
23921         this.updating = true;
23922     },
23923
23924     // private
23925     endUpdate : function(){
23926         this.updating = false;
23927         this.sync(true);
23928     },
23929
23930     // private
23931     hideUnders : function(negOffset){
23932         if(this.shadow){
23933             this.shadow.hide();
23934         }
23935         this.hideShim();
23936     },
23937
23938     // private
23939     constrainXY : function(){
23940         if(this.constrain){
23941             var vw = Roo.lib.Dom.getViewWidth(),
23942                 vh = Roo.lib.Dom.getViewHeight();
23943             var s = Roo.get(document).getScroll();
23944
23945             var xy = this.getXY();
23946             var x = xy[0], y = xy[1];   
23947             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23948             // only move it if it needs it
23949             var moved = false;
23950             // first validate right/bottom
23951             if((x + w) > vw+s.left){
23952                 x = vw - w - this.shadowOffset;
23953                 moved = true;
23954             }
23955             if((y + h) > vh+s.top){
23956                 y = vh - h - this.shadowOffset;
23957                 moved = true;
23958             }
23959             // then make sure top/left isn't negative
23960             if(x < s.left){
23961                 x = s.left;
23962                 moved = true;
23963             }
23964             if(y < s.top){
23965                 y = s.top;
23966                 moved = true;
23967             }
23968             if(moved){
23969                 if(this.avoidY){
23970                     var ay = this.avoidY;
23971                     if(y <= ay && (y+h) >= ay){
23972                         y = ay-h-5;   
23973                     }
23974                 }
23975                 xy = [x, y];
23976                 this.storeXY(xy);
23977                 supr.setXY.call(this, xy);
23978                 this.sync();
23979             }
23980         }
23981     },
23982
23983     isVisible : function(){
23984         return this.visible;    
23985     },
23986
23987     // private
23988     showAction : function(){
23989         this.visible = true; // track visibility to prevent getStyle calls
23990         if(this.useDisplay === true){
23991             this.setDisplayed("");
23992         }else if(this.lastXY){
23993             supr.setXY.call(this, this.lastXY);
23994         }else if(this.lastLT){
23995             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23996         }
23997     },
23998
23999     // private
24000     hideAction : function(){
24001         this.visible = false;
24002         if(this.useDisplay === true){
24003             this.setDisplayed(false);
24004         }else{
24005             this.setLeftTop(-10000,-10000);
24006         }
24007     },
24008
24009     // overridden Element method
24010     setVisible : function(v, a, d, c, e){
24011         if(v){
24012             this.showAction();
24013         }
24014         if(a && v){
24015             var cb = function(){
24016                 this.sync(true);
24017                 if(c){
24018                     c();
24019                 }
24020             }.createDelegate(this);
24021             supr.setVisible.call(this, true, true, d, cb, e);
24022         }else{
24023             if(!v){
24024                 this.hideUnders(true);
24025             }
24026             var cb = c;
24027             if(a){
24028                 cb = function(){
24029                     this.hideAction();
24030                     if(c){
24031                         c();
24032                     }
24033                 }.createDelegate(this);
24034             }
24035             supr.setVisible.call(this, v, a, d, cb, e);
24036             if(v){
24037                 this.sync(true);
24038             }else if(!a){
24039                 this.hideAction();
24040             }
24041         }
24042     },
24043
24044     storeXY : function(xy){
24045         delete this.lastLT;
24046         this.lastXY = xy;
24047     },
24048
24049     storeLeftTop : function(left, top){
24050         delete this.lastXY;
24051         this.lastLT = [left, top];
24052     },
24053
24054     // private
24055     beforeFx : function(){
24056         this.beforeAction();
24057         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24058     },
24059
24060     // private
24061     afterFx : function(){
24062         Roo.Layer.superclass.afterFx.apply(this, arguments);
24063         this.sync(this.isVisible());
24064     },
24065
24066     // private
24067     beforeAction : function(){
24068         if(!this.updating && this.shadow){
24069             this.shadow.hide();
24070         }
24071     },
24072
24073     // overridden Element method
24074     setLeft : function(left){
24075         this.storeLeftTop(left, this.getTop(true));
24076         supr.setLeft.apply(this, arguments);
24077         this.sync();
24078     },
24079
24080     setTop : function(top){
24081         this.storeLeftTop(this.getLeft(true), top);
24082         supr.setTop.apply(this, arguments);
24083         this.sync();
24084     },
24085
24086     setLeftTop : function(left, top){
24087         this.storeLeftTop(left, top);
24088         supr.setLeftTop.apply(this, arguments);
24089         this.sync();
24090     },
24091
24092     setXY : function(xy, a, d, c, e){
24093         this.fixDisplay();
24094         this.beforeAction();
24095         this.storeXY(xy);
24096         var cb = this.createCB(c);
24097         supr.setXY.call(this, xy, a, d, cb, e);
24098         if(!a){
24099             cb();
24100         }
24101     },
24102
24103     // private
24104     createCB : function(c){
24105         var el = this;
24106         return function(){
24107             el.constrainXY();
24108             el.sync(true);
24109             if(c){
24110                 c();
24111             }
24112         };
24113     },
24114
24115     // overridden Element method
24116     setX : function(x, a, d, c, e){
24117         this.setXY([x, this.getY()], a, d, c, e);
24118     },
24119
24120     // overridden Element method
24121     setY : function(y, a, d, c, e){
24122         this.setXY([this.getX(), y], a, d, c, e);
24123     },
24124
24125     // overridden Element method
24126     setSize : function(w, h, a, d, c, e){
24127         this.beforeAction();
24128         var cb = this.createCB(c);
24129         supr.setSize.call(this, w, h, a, d, cb, e);
24130         if(!a){
24131             cb();
24132         }
24133     },
24134
24135     // overridden Element method
24136     setWidth : function(w, a, d, c, e){
24137         this.beforeAction();
24138         var cb = this.createCB(c);
24139         supr.setWidth.call(this, w, a, d, cb, e);
24140         if(!a){
24141             cb();
24142         }
24143     },
24144
24145     // overridden Element method
24146     setHeight : function(h, a, d, c, e){
24147         this.beforeAction();
24148         var cb = this.createCB(c);
24149         supr.setHeight.call(this, h, a, d, cb, e);
24150         if(!a){
24151             cb();
24152         }
24153     },
24154
24155     // overridden Element method
24156     setBounds : function(x, y, w, h, a, d, c, e){
24157         this.beforeAction();
24158         var cb = this.createCB(c);
24159         if(!a){
24160             this.storeXY([x, y]);
24161             supr.setXY.call(this, [x, y]);
24162             supr.setSize.call(this, w, h, a, d, cb, e);
24163             cb();
24164         }else{
24165             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24166         }
24167         return this;
24168     },
24169     
24170     /**
24171      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24172      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24173      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24174      * @param {Number} zindex The new z-index to set
24175      * @return {this} The Layer
24176      */
24177     setZIndex : function(zindex){
24178         this.zindex = zindex;
24179         this.setStyle("z-index", zindex + 2);
24180         if(this.shadow){
24181             this.shadow.setZIndex(zindex + 1);
24182         }
24183         if(this.shim){
24184             this.shim.setStyle("z-index", zindex);
24185         }
24186     }
24187 });
24188 })();/*
24189  * Based on:
24190  * Ext JS Library 1.1.1
24191  * Copyright(c) 2006-2007, Ext JS, LLC.
24192  *
24193  * Originally Released Under LGPL - original licence link has changed is not relivant.
24194  *
24195  * Fork - LGPL
24196  * <script type="text/javascript">
24197  */
24198
24199
24200 /**
24201  * @class Roo.Shadow
24202  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24203  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24204  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24205  * @constructor
24206  * Create a new Shadow
24207  * @param {Object} config The config object
24208  */
24209 Roo.Shadow = function(config){
24210     Roo.apply(this, config);
24211     if(typeof this.mode != "string"){
24212         this.mode = this.defaultMode;
24213     }
24214     var o = this.offset, a = {h: 0};
24215     var rad = Math.floor(this.offset/2);
24216     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24217         case "drop":
24218             a.w = 0;
24219             a.l = a.t = o;
24220             a.t -= 1;
24221             if(Roo.isIE){
24222                 a.l -= this.offset + rad;
24223                 a.t -= this.offset + rad;
24224                 a.w -= rad;
24225                 a.h -= rad;
24226                 a.t += 1;
24227             }
24228         break;
24229         case "sides":
24230             a.w = (o*2);
24231             a.l = -o;
24232             a.t = o-1;
24233             if(Roo.isIE){
24234                 a.l -= (this.offset - rad);
24235                 a.t -= this.offset + rad;
24236                 a.l += 1;
24237                 a.w -= (this.offset - rad)*2;
24238                 a.w -= rad + 1;
24239                 a.h -= 1;
24240             }
24241         break;
24242         case "frame":
24243             a.w = a.h = (o*2);
24244             a.l = a.t = -o;
24245             a.t += 1;
24246             a.h -= 2;
24247             if(Roo.isIE){
24248                 a.l -= (this.offset - rad);
24249                 a.t -= (this.offset - rad);
24250                 a.l += 1;
24251                 a.w -= (this.offset + rad + 1);
24252                 a.h -= (this.offset + rad);
24253                 a.h += 1;
24254             }
24255         break;
24256     };
24257
24258     this.adjusts = a;
24259 };
24260
24261 Roo.Shadow.prototype = {
24262     /**
24263      * @cfg {String} mode
24264      * The shadow display mode.  Supports the following options:<br />
24265      * sides: Shadow displays on both sides and bottom only<br />
24266      * frame: Shadow displays equally on all four sides<br />
24267      * drop: Traditional bottom-right drop shadow (default)
24268      */
24269     /**
24270      * @cfg {String} offset
24271      * The number of pixels to offset the shadow from the element (defaults to 4)
24272      */
24273     offset: 4,
24274
24275     // private
24276     defaultMode: "drop",
24277
24278     /**
24279      * Displays the shadow under the target element
24280      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24281      */
24282     show : function(target){
24283         target = Roo.get(target);
24284         if(!this.el){
24285             this.el = Roo.Shadow.Pool.pull();
24286             if(this.el.dom.nextSibling != target.dom){
24287                 this.el.insertBefore(target);
24288             }
24289         }
24290         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24291         if(Roo.isIE){
24292             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24293         }
24294         this.realign(
24295             target.getLeft(true),
24296             target.getTop(true),
24297             target.getWidth(),
24298             target.getHeight()
24299         );
24300         this.el.dom.style.display = "block";
24301     },
24302
24303     /**
24304      * Returns true if the shadow is visible, else false
24305      */
24306     isVisible : function(){
24307         return this.el ? true : false;  
24308     },
24309
24310     /**
24311      * Direct alignment when values are already available. Show must be called at least once before
24312      * calling this method to ensure it is initialized.
24313      * @param {Number} left The target element left position
24314      * @param {Number} top The target element top position
24315      * @param {Number} width The target element width
24316      * @param {Number} height The target element height
24317      */
24318     realign : function(l, t, w, h){
24319         if(!this.el){
24320             return;
24321         }
24322         var a = this.adjusts, d = this.el.dom, s = d.style;
24323         var iea = 0;
24324         s.left = (l+a.l)+"px";
24325         s.top = (t+a.t)+"px";
24326         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24327  
24328         if(s.width != sws || s.height != shs){
24329             s.width = sws;
24330             s.height = shs;
24331             if(!Roo.isIE){
24332                 var cn = d.childNodes;
24333                 var sww = Math.max(0, (sw-12))+"px";
24334                 cn[0].childNodes[1].style.width = sww;
24335                 cn[1].childNodes[1].style.width = sww;
24336                 cn[2].childNodes[1].style.width = sww;
24337                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24338             }
24339         }
24340     },
24341
24342     /**
24343      * Hides this shadow
24344      */
24345     hide : function(){
24346         if(this.el){
24347             this.el.dom.style.display = "none";
24348             Roo.Shadow.Pool.push(this.el);
24349             delete this.el;
24350         }
24351     },
24352
24353     /**
24354      * Adjust the z-index of this shadow
24355      * @param {Number} zindex The new z-index
24356      */
24357     setZIndex : function(z){
24358         this.zIndex = z;
24359         if(this.el){
24360             this.el.setStyle("z-index", z);
24361         }
24362     }
24363 };
24364
24365 // Private utility class that manages the internal Shadow cache
24366 Roo.Shadow.Pool = function(){
24367     var p = [];
24368     var markup = Roo.isIE ?
24369                  '<div class="x-ie-shadow"></div>' :
24370                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
24371     return {
24372         pull : function(){
24373             var sh = p.shift();
24374             if(!sh){
24375                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24376                 sh.autoBoxAdjust = false;
24377             }
24378             return sh;
24379         },
24380
24381         push : function(sh){
24382             p.push(sh);
24383         }
24384     };
24385 }();/*
24386  * Based on:
24387  * Ext JS Library 1.1.1
24388  * Copyright(c) 2006-2007, Ext JS, LLC.
24389  *
24390  * Originally Released Under LGPL - original licence link has changed is not relivant.
24391  *
24392  * Fork - LGPL
24393  * <script type="text/javascript">
24394  */
24395
24396
24397 /**
24398  * @class Roo.SplitBar
24399  * @extends Roo.util.Observable
24400  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24401  * <br><br>
24402  * Usage:
24403  * <pre><code>
24404 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24405                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24406 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24407 split.minSize = 100;
24408 split.maxSize = 600;
24409 split.animate = true;
24410 split.on('moved', splitterMoved);
24411 </code></pre>
24412  * @constructor
24413  * Create a new SplitBar
24414  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24415  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24416  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24417  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24418                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24419                         position of the SplitBar).
24420  */
24421 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24422     
24423     /** @private */
24424     this.el = Roo.get(dragElement, true);
24425     this.el.dom.unselectable = "on";
24426     /** @private */
24427     this.resizingEl = Roo.get(resizingElement, true);
24428
24429     /**
24430      * @private
24431      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24432      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24433      * @type Number
24434      */
24435     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24436     
24437     /**
24438      * The minimum size of the resizing element. (Defaults to 0)
24439      * @type Number
24440      */
24441     this.minSize = 0;
24442     
24443     /**
24444      * The maximum size of the resizing element. (Defaults to 2000)
24445      * @type Number
24446      */
24447     this.maxSize = 2000;
24448     
24449     /**
24450      * Whether to animate the transition to the new size
24451      * @type Boolean
24452      */
24453     this.animate = false;
24454     
24455     /**
24456      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24457      * @type Boolean
24458      */
24459     this.useShim = false;
24460     
24461     /** @private */
24462     this.shim = null;
24463     
24464     if(!existingProxy){
24465         /** @private */
24466         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24467     }else{
24468         this.proxy = Roo.get(existingProxy).dom;
24469     }
24470     /** @private */
24471     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24472     
24473     /** @private */
24474     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24475     
24476     /** @private */
24477     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24478     
24479     /** @private */
24480     this.dragSpecs = {};
24481     
24482     /**
24483      * @private The adapter to use to positon and resize elements
24484      */
24485     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24486     this.adapter.init(this);
24487     
24488     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24489         /** @private */
24490         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24491         this.el.addClass("x-splitbar-h");
24492     }else{
24493         /** @private */
24494         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24495         this.el.addClass("x-splitbar-v");
24496     }
24497     
24498     this.addEvents({
24499         /**
24500          * @event resize
24501          * Fires when the splitter is moved (alias for {@link #event-moved})
24502          * @param {Roo.SplitBar} this
24503          * @param {Number} newSize the new width or height
24504          */
24505         "resize" : true,
24506         /**
24507          * @event moved
24508          * Fires when the splitter is moved
24509          * @param {Roo.SplitBar} this
24510          * @param {Number} newSize the new width or height
24511          */
24512         "moved" : true,
24513         /**
24514          * @event beforeresize
24515          * Fires before the splitter is dragged
24516          * @param {Roo.SplitBar} this
24517          */
24518         "beforeresize" : true,
24519
24520         "beforeapply" : true
24521     });
24522
24523     Roo.util.Observable.call(this);
24524 };
24525
24526 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24527     onStartProxyDrag : function(x, y){
24528         this.fireEvent("beforeresize", this);
24529         if(!this.overlay){
24530             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24531             o.unselectable();
24532             o.enableDisplayMode("block");
24533             // all splitbars share the same overlay
24534             Roo.SplitBar.prototype.overlay = o;
24535         }
24536         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24537         this.overlay.show();
24538         Roo.get(this.proxy).setDisplayed("block");
24539         var size = this.adapter.getElementSize(this);
24540         this.activeMinSize = this.getMinimumSize();;
24541         this.activeMaxSize = this.getMaximumSize();;
24542         var c1 = size - this.activeMinSize;
24543         var c2 = Math.max(this.activeMaxSize - size, 0);
24544         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24545             this.dd.resetConstraints();
24546             this.dd.setXConstraint(
24547                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24548                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24549             );
24550             this.dd.setYConstraint(0, 0);
24551         }else{
24552             this.dd.resetConstraints();
24553             this.dd.setXConstraint(0, 0);
24554             this.dd.setYConstraint(
24555                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24556                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24557             );
24558          }
24559         this.dragSpecs.startSize = size;
24560         this.dragSpecs.startPoint = [x, y];
24561         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24562     },
24563     
24564     /** 
24565      * @private Called after the drag operation by the DDProxy
24566      */
24567     onEndProxyDrag : function(e){
24568         Roo.get(this.proxy).setDisplayed(false);
24569         var endPoint = Roo.lib.Event.getXY(e);
24570         if(this.overlay){
24571             this.overlay.hide();
24572         }
24573         var newSize;
24574         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24575             newSize = this.dragSpecs.startSize + 
24576                 (this.placement == Roo.SplitBar.LEFT ?
24577                     endPoint[0] - this.dragSpecs.startPoint[0] :
24578                     this.dragSpecs.startPoint[0] - endPoint[0]
24579                 );
24580         }else{
24581             newSize = this.dragSpecs.startSize + 
24582                 (this.placement == Roo.SplitBar.TOP ?
24583                     endPoint[1] - this.dragSpecs.startPoint[1] :
24584                     this.dragSpecs.startPoint[1] - endPoint[1]
24585                 );
24586         }
24587         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24588         if(newSize != this.dragSpecs.startSize){
24589             if(this.fireEvent('beforeapply', this, newSize) !== false){
24590                 this.adapter.setElementSize(this, newSize);
24591                 this.fireEvent("moved", this, newSize);
24592                 this.fireEvent("resize", this, newSize);
24593             }
24594         }
24595     },
24596     
24597     /**
24598      * Get the adapter this SplitBar uses
24599      * @return The adapter object
24600      */
24601     getAdapter : function(){
24602         return this.adapter;
24603     },
24604     
24605     /**
24606      * Set the adapter this SplitBar uses
24607      * @param {Object} adapter A SplitBar adapter object
24608      */
24609     setAdapter : function(adapter){
24610         this.adapter = adapter;
24611         this.adapter.init(this);
24612     },
24613     
24614     /**
24615      * Gets the minimum size for the resizing element
24616      * @return {Number} The minimum size
24617      */
24618     getMinimumSize : function(){
24619         return this.minSize;
24620     },
24621     
24622     /**
24623      * Sets the minimum size for the resizing element
24624      * @param {Number} minSize The minimum size
24625      */
24626     setMinimumSize : function(minSize){
24627         this.minSize = minSize;
24628     },
24629     
24630     /**
24631      * Gets the maximum size for the resizing element
24632      * @return {Number} The maximum size
24633      */
24634     getMaximumSize : function(){
24635         return this.maxSize;
24636     },
24637     
24638     /**
24639      * Sets the maximum size for the resizing element
24640      * @param {Number} maxSize The maximum size
24641      */
24642     setMaximumSize : function(maxSize){
24643         this.maxSize = maxSize;
24644     },
24645     
24646     /**
24647      * Sets the initialize size for the resizing element
24648      * @param {Number} size The initial size
24649      */
24650     setCurrentSize : function(size){
24651         var oldAnimate = this.animate;
24652         this.animate = false;
24653         this.adapter.setElementSize(this, size);
24654         this.animate = oldAnimate;
24655     },
24656     
24657     /**
24658      * Destroy this splitbar. 
24659      * @param {Boolean} removeEl True to remove the element
24660      */
24661     destroy : function(removeEl){
24662         if(this.shim){
24663             this.shim.remove();
24664         }
24665         this.dd.unreg();
24666         this.proxy.parentNode.removeChild(this.proxy);
24667         if(removeEl){
24668             this.el.remove();
24669         }
24670     }
24671 });
24672
24673 /**
24674  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
24675  */
24676 Roo.SplitBar.createProxy = function(dir){
24677     var proxy = new Roo.Element(document.createElement("div"));
24678     proxy.unselectable();
24679     var cls = 'x-splitbar-proxy';
24680     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24681     document.body.appendChild(proxy.dom);
24682     return proxy.dom;
24683 };
24684
24685 /** 
24686  * @class Roo.SplitBar.BasicLayoutAdapter
24687  * Default Adapter. It assumes the splitter and resizing element are not positioned
24688  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24689  */
24690 Roo.SplitBar.BasicLayoutAdapter = function(){
24691 };
24692
24693 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24694     // do nothing for now
24695     init : function(s){
24696     
24697     },
24698     /**
24699      * Called before drag operations to get the current size of the resizing element. 
24700      * @param {Roo.SplitBar} s The SplitBar using this adapter
24701      */
24702      getElementSize : function(s){
24703         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24704             return s.resizingEl.getWidth();
24705         }else{
24706             return s.resizingEl.getHeight();
24707         }
24708     },
24709     
24710     /**
24711      * Called after drag operations to set the size of the resizing element.
24712      * @param {Roo.SplitBar} s The SplitBar using this adapter
24713      * @param {Number} newSize The new size to set
24714      * @param {Function} onComplete A function to be invoked when resizing is complete
24715      */
24716     setElementSize : function(s, newSize, onComplete){
24717         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24718             if(!s.animate){
24719                 s.resizingEl.setWidth(newSize);
24720                 if(onComplete){
24721                     onComplete(s, newSize);
24722                 }
24723             }else{
24724                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24725             }
24726         }else{
24727             
24728             if(!s.animate){
24729                 s.resizingEl.setHeight(newSize);
24730                 if(onComplete){
24731                     onComplete(s, newSize);
24732                 }
24733             }else{
24734                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24735             }
24736         }
24737     }
24738 };
24739
24740 /** 
24741  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24742  * @extends Roo.SplitBar.BasicLayoutAdapter
24743  * Adapter that  moves the splitter element to align with the resized sizing element. 
24744  * Used with an absolute positioned SplitBar.
24745  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24746  * document.body, make sure you assign an id to the body element.
24747  */
24748 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24749     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24750     this.container = Roo.get(container);
24751 };
24752
24753 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24754     init : function(s){
24755         this.basic.init(s);
24756     },
24757     
24758     getElementSize : function(s){
24759         return this.basic.getElementSize(s);
24760     },
24761     
24762     setElementSize : function(s, newSize, onComplete){
24763         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24764     },
24765     
24766     moveSplitter : function(s){
24767         var yes = Roo.SplitBar;
24768         switch(s.placement){
24769             case yes.LEFT:
24770                 s.el.setX(s.resizingEl.getRight());
24771                 break;
24772             case yes.RIGHT:
24773                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24774                 break;
24775             case yes.TOP:
24776                 s.el.setY(s.resizingEl.getBottom());
24777                 break;
24778             case yes.BOTTOM:
24779                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24780                 break;
24781         }
24782     }
24783 };
24784
24785 /**
24786  * Orientation constant - Create a vertical SplitBar
24787  * @static
24788  * @type Number
24789  */
24790 Roo.SplitBar.VERTICAL = 1;
24791
24792 /**
24793  * Orientation constant - Create a horizontal SplitBar
24794  * @static
24795  * @type Number
24796  */
24797 Roo.SplitBar.HORIZONTAL = 2;
24798
24799 /**
24800  * Placement constant - The resizing element is to the left of the splitter element
24801  * @static
24802  * @type Number
24803  */
24804 Roo.SplitBar.LEFT = 1;
24805
24806 /**
24807  * Placement constant - The resizing element is to the right of the splitter element
24808  * @static
24809  * @type Number
24810  */
24811 Roo.SplitBar.RIGHT = 2;
24812
24813 /**
24814  * Placement constant - The resizing element is positioned above the splitter element
24815  * @static
24816  * @type Number
24817  */
24818 Roo.SplitBar.TOP = 3;
24819
24820 /**
24821  * Placement constant - The resizing element is positioned under splitter element
24822  * @static
24823  * @type Number
24824  */
24825 Roo.SplitBar.BOTTOM = 4;
24826 /*
24827  * Based on:
24828  * Ext JS Library 1.1.1
24829  * Copyright(c) 2006-2007, Ext JS, LLC.
24830  *
24831  * Originally Released Under LGPL - original licence link has changed is not relivant.
24832  *
24833  * Fork - LGPL
24834  * <script type="text/javascript">
24835  */
24836
24837 /**
24838  * @class Roo.View
24839  * @extends Roo.util.Observable
24840  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24841  * This class also supports single and multi selection modes. <br>
24842  * Create a data model bound view:
24843  <pre><code>
24844  var store = new Roo.data.Store(...);
24845
24846  var view = new Roo.View({
24847     el : "my-element",
24848     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24849  
24850     singleSelect: true,
24851     selectedClass: "ydataview-selected",
24852     store: store
24853  });
24854
24855  // listen for node click?
24856  view.on("click", function(vw, index, node, e){
24857  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24858  });
24859
24860  // load XML data
24861  dataModel.load("foobar.xml");
24862  </code></pre>
24863  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24864  * <br><br>
24865  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24866  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24867  * 
24868  * Note: old style constructor is still suported (container, template, config)
24869  * 
24870  * @constructor
24871  * Create a new View
24872  * @param {Object} config The config object
24873  * 
24874  */
24875 Roo.View = function(config, depreciated_tpl, depreciated_config){
24876     
24877     this.parent = false;
24878     
24879     if (typeof(depreciated_tpl) == 'undefined') {
24880         // new way.. - universal constructor.
24881         Roo.apply(this, config);
24882         this.el  = Roo.get(this.el);
24883     } else {
24884         // old format..
24885         this.el  = Roo.get(config);
24886         this.tpl = depreciated_tpl;
24887         Roo.apply(this, depreciated_config);
24888     }
24889     this.wrapEl  = this.el.wrap().wrap();
24890     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24891     
24892     
24893     if(typeof(this.tpl) == "string"){
24894         this.tpl = new Roo.Template(this.tpl);
24895     } else {
24896         // support xtype ctors..
24897         this.tpl = new Roo.factory(this.tpl, Roo);
24898     }
24899     
24900     
24901     this.tpl.compile();
24902     
24903     /** @private */
24904     this.addEvents({
24905         /**
24906          * @event beforeclick
24907          * Fires before a click is processed. Returns false to cancel the default action.
24908          * @param {Roo.View} this
24909          * @param {Number} index The index of the target node
24910          * @param {HTMLElement} node The target node
24911          * @param {Roo.EventObject} e The raw event object
24912          */
24913             "beforeclick" : true,
24914         /**
24915          * @event click
24916          * Fires when a template node is clicked.
24917          * @param {Roo.View} this
24918          * @param {Number} index The index of the target node
24919          * @param {HTMLElement} node The target node
24920          * @param {Roo.EventObject} e The raw event object
24921          */
24922             "click" : true,
24923         /**
24924          * @event dblclick
24925          * Fires when a template node is double clicked.
24926          * @param {Roo.View} this
24927          * @param {Number} index The index of the target node
24928          * @param {HTMLElement} node The target node
24929          * @param {Roo.EventObject} e The raw event object
24930          */
24931             "dblclick" : true,
24932         /**
24933          * @event contextmenu
24934          * Fires when a template node is right clicked.
24935          * @param {Roo.View} this
24936          * @param {Number} index The index of the target node
24937          * @param {HTMLElement} node The target node
24938          * @param {Roo.EventObject} e The raw event object
24939          */
24940             "contextmenu" : true,
24941         /**
24942          * @event selectionchange
24943          * Fires when the selected nodes change.
24944          * @param {Roo.View} this
24945          * @param {Array} selections Array of the selected nodes
24946          */
24947             "selectionchange" : true,
24948     
24949         /**
24950          * @event beforeselect
24951          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24952          * @param {Roo.View} this
24953          * @param {HTMLElement} node The node to be selected
24954          * @param {Array} selections Array of currently selected nodes
24955          */
24956             "beforeselect" : true,
24957         /**
24958          * @event preparedata
24959          * Fires on every row to render, to allow you to change the data.
24960          * @param {Roo.View} this
24961          * @param {Object} data to be rendered (change this)
24962          */
24963           "preparedata" : true
24964           
24965           
24966         });
24967
24968
24969
24970     this.el.on({
24971         "click": this.onClick,
24972         "dblclick": this.onDblClick,
24973         "contextmenu": this.onContextMenu,
24974         scope:this
24975     });
24976
24977     this.selections = [];
24978     this.nodes = [];
24979     this.cmp = new Roo.CompositeElementLite([]);
24980     if(this.store){
24981         this.store = Roo.factory(this.store, Roo.data);
24982         this.setStore(this.store, true);
24983     }
24984     
24985     if ( this.footer && this.footer.xtype) {
24986            
24987          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24988         
24989         this.footer.dataSource = this.store
24990         this.footer.container = fctr;
24991         this.footer = Roo.factory(this.footer, Roo);
24992         fctr.insertFirst(this.el);
24993         
24994         // this is a bit insane - as the paging toolbar seems to detach the el..
24995 //        dom.parentNode.parentNode.parentNode
24996          // they get detached?
24997     }
24998     
24999     
25000     Roo.View.superclass.constructor.call(this);
25001     
25002     
25003 };
25004
25005 Roo.extend(Roo.View, Roo.util.Observable, {
25006     
25007      /**
25008      * @cfg {Roo.data.Store} store Data store to load data from.
25009      */
25010     store : false,
25011     
25012     /**
25013      * @cfg {String|Roo.Element} el The container element.
25014      */
25015     el : '',
25016     
25017     /**
25018      * @cfg {String|Roo.Template} tpl The template used by this View 
25019      */
25020     tpl : false,
25021     /**
25022      * @cfg {String} dataName the named area of the template to use as the data area
25023      *                          Works with domtemplates roo-name="name"
25024      */
25025     dataName: false,
25026     /**
25027      * @cfg {String} selectedClass The css class to add to selected nodes
25028      */
25029     selectedClass : "x-view-selected",
25030      /**
25031      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25032      */
25033     emptyText : "",
25034     
25035     /**
25036      * @cfg {String} text to display on mask (default Loading)
25037      */
25038     mask : false,
25039     /**
25040      * @cfg {Boolean} multiSelect Allow multiple selection
25041      */
25042     multiSelect : false,
25043     /**
25044      * @cfg {Boolean} singleSelect Allow single selection
25045      */
25046     singleSelect:  false,
25047     
25048     /**
25049      * @cfg {Boolean} toggleSelect - selecting 
25050      */
25051     toggleSelect : false,
25052     
25053     /**
25054      * @cfg {Boolean} tickable - selecting 
25055      */
25056     tickable : false,
25057     
25058     /**
25059      * Returns the element this view is bound to.
25060      * @return {Roo.Element}
25061      */
25062     getEl : function(){
25063         return this.wrapEl;
25064     },
25065     
25066     
25067
25068     /**
25069      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25070      */
25071     refresh : function(){
25072         //Roo.log('refresh');
25073         var t = this.tpl;
25074         
25075         // if we are using something like 'domtemplate', then
25076         // the what gets used is:
25077         // t.applySubtemplate(NAME, data, wrapping data..)
25078         // the outer template then get' applied with
25079         //     the store 'extra data'
25080         // and the body get's added to the
25081         //      roo-name="data" node?
25082         //      <span class='roo-tpl-{name}'></span> ?????
25083         
25084         
25085         
25086         this.clearSelections();
25087         this.el.update("");
25088         var html = [];
25089         var records = this.store.getRange();
25090         if(records.length < 1) {
25091             
25092             // is this valid??  = should it render a template??
25093             
25094             this.el.update(this.emptyText);
25095             return;
25096         }
25097         var el = this.el;
25098         if (this.dataName) {
25099             this.el.update(t.apply(this.store.meta)); //????
25100             el = this.el.child('.roo-tpl-' + this.dataName);
25101         }
25102         
25103         for(var i = 0, len = records.length; i < len; i++){
25104             var data = this.prepareData(records[i].data, i, records[i]);
25105             this.fireEvent("preparedata", this, data, i, records[i]);
25106             
25107             var d = Roo.apply({}, data);
25108             
25109             if(this.tickable){
25110                 Roo.apply(d, {'roo-id' : Roo.id()});
25111                 
25112                 var _this = this;
25113             
25114                 Roo.each(this.parent.item, function(item){
25115                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25116                         return;
25117                     }
25118                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25119                 });
25120             }
25121             
25122             html[html.length] = Roo.util.Format.trim(
25123                 this.dataName ?
25124                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25125                     t.apply(d)
25126             );
25127         }
25128         
25129         
25130         
25131         el.update(html.join(""));
25132         this.nodes = el.dom.childNodes;
25133         this.updateIndexes(0);
25134     },
25135     
25136
25137     /**
25138      * Function to override to reformat the data that is sent to
25139      * the template for each node.
25140      * DEPRICATED - use the preparedata event handler.
25141      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25142      * a JSON object for an UpdateManager bound view).
25143      */
25144     prepareData : function(data, index, record)
25145     {
25146         this.fireEvent("preparedata", this, data, index, record);
25147         return data;
25148     },
25149
25150     onUpdate : function(ds, record){
25151         // Roo.log('on update');   
25152         this.clearSelections();
25153         var index = this.store.indexOf(record);
25154         var n = this.nodes[index];
25155         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25156         n.parentNode.removeChild(n);
25157         this.updateIndexes(index, index);
25158     },
25159
25160     
25161     
25162 // --------- FIXME     
25163     onAdd : function(ds, records, index)
25164     {
25165         //Roo.log(['on Add', ds, records, index] );        
25166         this.clearSelections();
25167         if(this.nodes.length == 0){
25168             this.refresh();
25169             return;
25170         }
25171         var n = this.nodes[index];
25172         for(var i = 0, len = records.length; i < len; i++){
25173             var d = this.prepareData(records[i].data, i, records[i]);
25174             if(n){
25175                 this.tpl.insertBefore(n, d);
25176             }else{
25177                 
25178                 this.tpl.append(this.el, d);
25179             }
25180         }
25181         this.updateIndexes(index);
25182     },
25183
25184     onRemove : function(ds, record, index){
25185        // Roo.log('onRemove');
25186         this.clearSelections();
25187         var el = this.dataName  ?
25188             this.el.child('.roo-tpl-' + this.dataName) :
25189             this.el; 
25190         
25191         el.dom.removeChild(this.nodes[index]);
25192         this.updateIndexes(index);
25193     },
25194
25195     /**
25196      * Refresh an individual node.
25197      * @param {Number} index
25198      */
25199     refreshNode : function(index){
25200         this.onUpdate(this.store, this.store.getAt(index));
25201     },
25202
25203     updateIndexes : function(startIndex, endIndex){
25204         var ns = this.nodes;
25205         startIndex = startIndex || 0;
25206         endIndex = endIndex || ns.length - 1;
25207         for(var i = startIndex; i <= endIndex; i++){
25208             ns[i].nodeIndex = i;
25209         }
25210     },
25211
25212     /**
25213      * Changes the data store this view uses and refresh the view.
25214      * @param {Store} store
25215      */
25216     setStore : function(store, initial){
25217         if(!initial && this.store){
25218             this.store.un("datachanged", this.refresh);
25219             this.store.un("add", this.onAdd);
25220             this.store.un("remove", this.onRemove);
25221             this.store.un("update", this.onUpdate);
25222             this.store.un("clear", this.refresh);
25223             this.store.un("beforeload", this.onBeforeLoad);
25224             this.store.un("load", this.onLoad);
25225             this.store.un("loadexception", this.onLoad);
25226         }
25227         if(store){
25228           
25229             store.on("datachanged", this.refresh, this);
25230             store.on("add", this.onAdd, this);
25231             store.on("remove", this.onRemove, this);
25232             store.on("update", this.onUpdate, this);
25233             store.on("clear", this.refresh, this);
25234             store.on("beforeload", this.onBeforeLoad, this);
25235             store.on("load", this.onLoad, this);
25236             store.on("loadexception", this.onLoad, this);
25237         }
25238         
25239         if(store){
25240             this.refresh();
25241         }
25242     },
25243     /**
25244      * onbeforeLoad - masks the loading area.
25245      *
25246      */
25247     onBeforeLoad : function(store,opts)
25248     {
25249          //Roo.log('onBeforeLoad');   
25250         if (!opts.add) {
25251             this.el.update("");
25252         }
25253         this.el.mask(this.mask ? this.mask : "Loading" ); 
25254     },
25255     onLoad : function ()
25256     {
25257         this.el.unmask();
25258     },
25259     
25260
25261     /**
25262      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25263      * @param {HTMLElement} node
25264      * @return {HTMLElement} The template node
25265      */
25266     findItemFromChild : function(node){
25267         var el = this.dataName  ?
25268             this.el.child('.roo-tpl-' + this.dataName,true) :
25269             this.el.dom; 
25270         
25271         if(!node || node.parentNode == el){
25272                     return node;
25273             }
25274             var p = node.parentNode;
25275             while(p && p != el){
25276             if(p.parentNode == el){
25277                 return p;
25278             }
25279             p = p.parentNode;
25280         }
25281             return null;
25282     },
25283
25284     /** @ignore */
25285     onClick : function(e){
25286         var item = this.findItemFromChild(e.getTarget());
25287         if(item){
25288             var index = this.indexOf(item);
25289             if(this.onItemClick(item, index, e) !== false){
25290                 this.fireEvent("click", this, index, item, e);
25291             }
25292         }else{
25293             this.clearSelections();
25294         }
25295     },
25296
25297     /** @ignore */
25298     onContextMenu : function(e){
25299         var item = this.findItemFromChild(e.getTarget());
25300         if(item){
25301             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25302         }
25303     },
25304
25305     /** @ignore */
25306     onDblClick : function(e){
25307         var item = this.findItemFromChild(e.getTarget());
25308         if(item){
25309             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25310         }
25311     },
25312
25313     onItemClick : function(item, index, e)
25314     {
25315         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25316             return false;
25317         }
25318         if (this.toggleSelect) {
25319             var m = this.isSelected(item) ? 'unselect' : 'select';
25320             //Roo.log(m);
25321             var _t = this;
25322             _t[m](item, true, false);
25323             return true;
25324         }
25325         if(this.multiSelect || this.singleSelect){
25326             if(this.multiSelect && e.shiftKey && this.lastSelection){
25327                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25328             }else{
25329                 this.select(item, this.multiSelect && e.ctrlKey);
25330                 this.lastSelection = item;
25331             }
25332             
25333             if(!this.tickable){
25334                 e.preventDefault();
25335             }
25336             
25337         }
25338         return true;
25339     },
25340
25341     /**
25342      * Get the number of selected nodes.
25343      * @return {Number}
25344      */
25345     getSelectionCount : function(){
25346         return this.selections.length;
25347     },
25348
25349     /**
25350      * Get the currently selected nodes.
25351      * @return {Array} An array of HTMLElements
25352      */
25353     getSelectedNodes : function(){
25354         return this.selections;
25355     },
25356
25357     /**
25358      * Get the indexes of the selected nodes.
25359      * @return {Array}
25360      */
25361     getSelectedIndexes : function(){
25362         var indexes = [], s = this.selections;
25363         for(var i = 0, len = s.length; i < len; i++){
25364             indexes.push(s[i].nodeIndex);
25365         }
25366         return indexes;
25367     },
25368
25369     /**
25370      * Clear all selections
25371      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25372      */
25373     clearSelections : function(suppressEvent){
25374         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25375             this.cmp.elements = this.selections;
25376             this.cmp.removeClass(this.selectedClass);
25377             this.selections = [];
25378             if(!suppressEvent){
25379                 this.fireEvent("selectionchange", this, this.selections);
25380             }
25381         }
25382     },
25383
25384     /**
25385      * Returns true if the passed node is selected
25386      * @param {HTMLElement/Number} node The node or node index
25387      * @return {Boolean}
25388      */
25389     isSelected : function(node){
25390         var s = this.selections;
25391         if(s.length < 1){
25392             return false;
25393         }
25394         node = this.getNode(node);
25395         return s.indexOf(node) !== -1;
25396     },
25397
25398     /**
25399      * Selects nodes.
25400      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25401      * @param {Boolean} keepExisting (optional) true to keep existing selections
25402      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25403      */
25404     select : function(nodeInfo, keepExisting, suppressEvent){
25405         if(nodeInfo instanceof Array){
25406             if(!keepExisting){
25407                 this.clearSelections(true);
25408             }
25409             for(var i = 0, len = nodeInfo.length; i < len; i++){
25410                 this.select(nodeInfo[i], true, true);
25411             }
25412             return;
25413         } 
25414         var node = this.getNode(nodeInfo);
25415         if(!node || this.isSelected(node)){
25416             return; // already selected.
25417         }
25418         if(!keepExisting){
25419             this.clearSelections(true);
25420         }
25421         
25422         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25423             Roo.fly(node).addClass(this.selectedClass);
25424             this.selections.push(node);
25425             if(!suppressEvent){
25426                 this.fireEvent("selectionchange", this, this.selections);
25427             }
25428         }
25429         
25430         
25431     },
25432       /**
25433      * Unselects nodes.
25434      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25435      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25436      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25437      */
25438     unselect : function(nodeInfo, keepExisting, suppressEvent)
25439     {
25440         if(nodeInfo instanceof Array){
25441             Roo.each(this.selections, function(s) {
25442                 this.unselect(s, nodeInfo);
25443             }, this);
25444             return;
25445         }
25446         var node = this.getNode(nodeInfo);
25447         if(!node || !this.isSelected(node)){
25448             //Roo.log("not selected");
25449             return; // not selected.
25450         }
25451         // fireevent???
25452         var ns = [];
25453         Roo.each(this.selections, function(s) {
25454             if (s == node ) {
25455                 Roo.fly(node).removeClass(this.selectedClass);
25456
25457                 return;
25458             }
25459             ns.push(s);
25460         },this);
25461         
25462         this.selections= ns;
25463         this.fireEvent("selectionchange", this, this.selections);
25464     },
25465
25466     /**
25467      * Gets a template node.
25468      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25469      * @return {HTMLElement} The node or null if it wasn't found
25470      */
25471     getNode : function(nodeInfo){
25472         if(typeof nodeInfo == "string"){
25473             return document.getElementById(nodeInfo);
25474         }else if(typeof nodeInfo == "number"){
25475             return this.nodes[nodeInfo];
25476         }
25477         return nodeInfo;
25478     },
25479
25480     /**
25481      * Gets a range template nodes.
25482      * @param {Number} startIndex
25483      * @param {Number} endIndex
25484      * @return {Array} An array of nodes
25485      */
25486     getNodes : function(start, end){
25487         var ns = this.nodes;
25488         start = start || 0;
25489         end = typeof end == "undefined" ? ns.length - 1 : end;
25490         var nodes = [];
25491         if(start <= end){
25492             for(var i = start; i <= end; i++){
25493                 nodes.push(ns[i]);
25494             }
25495         } else{
25496             for(var i = start; i >= end; i--){
25497                 nodes.push(ns[i]);
25498             }
25499         }
25500         return nodes;
25501     },
25502
25503     /**
25504      * Finds the index of the passed node
25505      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25506      * @return {Number} The index of the node or -1
25507      */
25508     indexOf : function(node){
25509         node = this.getNode(node);
25510         if(typeof node.nodeIndex == "number"){
25511             return node.nodeIndex;
25512         }
25513         var ns = this.nodes;
25514         for(var i = 0, len = ns.length; i < len; i++){
25515             if(ns[i] == node){
25516                 return i;
25517             }
25518         }
25519         return -1;
25520     }
25521 });
25522 /*
25523  * Based on:
25524  * Ext JS Library 1.1.1
25525  * Copyright(c) 2006-2007, Ext JS, LLC.
25526  *
25527  * Originally Released Under LGPL - original licence link has changed is not relivant.
25528  *
25529  * Fork - LGPL
25530  * <script type="text/javascript">
25531  */
25532
25533 /**
25534  * @class Roo.JsonView
25535  * @extends Roo.View
25536  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25537 <pre><code>
25538 var view = new Roo.JsonView({
25539     container: "my-element",
25540     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25541     multiSelect: true, 
25542     jsonRoot: "data" 
25543 });
25544
25545 // listen for node click?
25546 view.on("click", function(vw, index, node, e){
25547     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25548 });
25549
25550 // direct load of JSON data
25551 view.load("foobar.php");
25552
25553 // Example from my blog list
25554 var tpl = new Roo.Template(
25555     '&lt;div class="entry"&gt;' +
25556     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25557     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25558     "&lt;/div&gt;&lt;hr /&gt;"
25559 );
25560
25561 var moreView = new Roo.JsonView({
25562     container :  "entry-list", 
25563     template : tpl,
25564     jsonRoot: "posts"
25565 });
25566 moreView.on("beforerender", this.sortEntries, this);
25567 moreView.load({
25568     url: "/blog/get-posts.php",
25569     params: "allposts=true",
25570     text: "Loading Blog Entries..."
25571 });
25572 </code></pre>
25573
25574 * Note: old code is supported with arguments : (container, template, config)
25575
25576
25577  * @constructor
25578  * Create a new JsonView
25579  * 
25580  * @param {Object} config The config object
25581  * 
25582  */
25583 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25584     
25585     
25586     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25587
25588     var um = this.el.getUpdateManager();
25589     um.setRenderer(this);
25590     um.on("update", this.onLoad, this);
25591     um.on("failure", this.onLoadException, this);
25592
25593     /**
25594      * @event beforerender
25595      * Fires before rendering of the downloaded JSON data.
25596      * @param {Roo.JsonView} this
25597      * @param {Object} data The JSON data loaded
25598      */
25599     /**
25600      * @event load
25601      * Fires when data is loaded.
25602      * @param {Roo.JsonView} this
25603      * @param {Object} data The JSON data loaded
25604      * @param {Object} response The raw Connect response object
25605      */
25606     /**
25607      * @event loadexception
25608      * Fires when loading fails.
25609      * @param {Roo.JsonView} this
25610      * @param {Object} response The raw Connect response object
25611      */
25612     this.addEvents({
25613         'beforerender' : true,
25614         'load' : true,
25615         'loadexception' : true
25616     });
25617 };
25618 Roo.extend(Roo.JsonView, Roo.View, {
25619     /**
25620      * @type {String} The root property in the loaded JSON object that contains the data
25621      */
25622     jsonRoot : "",
25623
25624     /**
25625      * Refreshes the view.
25626      */
25627     refresh : function(){
25628         this.clearSelections();
25629         this.el.update("");
25630         var html = [];
25631         var o = this.jsonData;
25632         if(o && o.length > 0){
25633             for(var i = 0, len = o.length; i < len; i++){
25634                 var data = this.prepareData(o[i], i, o);
25635                 html[html.length] = this.tpl.apply(data);
25636             }
25637         }else{
25638             html.push(this.emptyText);
25639         }
25640         this.el.update(html.join(""));
25641         this.nodes = this.el.dom.childNodes;
25642         this.updateIndexes(0);
25643     },
25644
25645     /**
25646      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
25647      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
25648      <pre><code>
25649      view.load({
25650          url: "your-url.php",
25651          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25652          callback: yourFunction,
25653          scope: yourObject, //(optional scope)
25654          discardUrl: false,
25655          nocache: false,
25656          text: "Loading...",
25657          timeout: 30,
25658          scripts: false
25659      });
25660      </code></pre>
25661      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25662      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
25663      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
25664      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25665      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
25666      */
25667     load : function(){
25668         var um = this.el.getUpdateManager();
25669         um.update.apply(um, arguments);
25670     },
25671
25672     render : function(el, response){
25673         this.clearSelections();
25674         this.el.update("");
25675         var o;
25676         try{
25677             o = Roo.util.JSON.decode(response.responseText);
25678             if(this.jsonRoot){
25679                 
25680                 o = o[this.jsonRoot];
25681             }
25682         } catch(e){
25683         }
25684         /**
25685          * The current JSON data or null
25686          */
25687         this.jsonData = o;
25688         this.beforeRender();
25689         this.refresh();
25690     },
25691
25692 /**
25693  * Get the number of records in the current JSON dataset
25694  * @return {Number}
25695  */
25696     getCount : function(){
25697         return this.jsonData ? this.jsonData.length : 0;
25698     },
25699
25700 /**
25701  * Returns the JSON object for the specified node(s)
25702  * @param {HTMLElement/Array} node The node or an array of nodes
25703  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25704  * you get the JSON object for the node
25705  */
25706     getNodeData : function(node){
25707         if(node instanceof Array){
25708             var data = [];
25709             for(var i = 0, len = node.length; i < len; i++){
25710                 data.push(this.getNodeData(node[i]));
25711             }
25712             return data;
25713         }
25714         return this.jsonData[this.indexOf(node)] || null;
25715     },
25716
25717     beforeRender : function(){
25718         this.snapshot = this.jsonData;
25719         if(this.sortInfo){
25720             this.sort.apply(this, this.sortInfo);
25721         }
25722         this.fireEvent("beforerender", this, this.jsonData);
25723     },
25724
25725     onLoad : function(el, o){
25726         this.fireEvent("load", this, this.jsonData, o);
25727     },
25728
25729     onLoadException : function(el, o){
25730         this.fireEvent("loadexception", this, o);
25731     },
25732
25733 /**
25734  * Filter the data by a specific property.
25735  * @param {String} property A property on your JSON objects
25736  * @param {String/RegExp} value Either string that the property values
25737  * should start with, or a RegExp to test against the property
25738  */
25739     filter : function(property, value){
25740         if(this.jsonData){
25741             var data = [];
25742             var ss = this.snapshot;
25743             if(typeof value == "string"){
25744                 var vlen = value.length;
25745                 if(vlen == 0){
25746                     this.clearFilter();
25747                     return;
25748                 }
25749                 value = value.toLowerCase();
25750                 for(var i = 0, len = ss.length; i < len; i++){
25751                     var o = ss[i];
25752                     if(o[property].substr(0, vlen).toLowerCase() == value){
25753                         data.push(o);
25754                     }
25755                 }
25756             } else if(value.exec){ // regex?
25757                 for(var i = 0, len = ss.length; i < len; i++){
25758                     var o = ss[i];
25759                     if(value.test(o[property])){
25760                         data.push(o);
25761                     }
25762                 }
25763             } else{
25764                 return;
25765             }
25766             this.jsonData = data;
25767             this.refresh();
25768         }
25769     },
25770
25771 /**
25772  * Filter by a function. The passed function will be called with each
25773  * object in the current dataset. If the function returns true the value is kept,
25774  * otherwise it is filtered.
25775  * @param {Function} fn
25776  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25777  */
25778     filterBy : function(fn, scope){
25779         if(this.jsonData){
25780             var data = [];
25781             var ss = this.snapshot;
25782             for(var i = 0, len = ss.length; i < len; i++){
25783                 var o = ss[i];
25784                 if(fn.call(scope || this, o)){
25785                     data.push(o);
25786                 }
25787             }
25788             this.jsonData = data;
25789             this.refresh();
25790         }
25791     },
25792
25793 /**
25794  * Clears the current filter.
25795  */
25796     clearFilter : function(){
25797         if(this.snapshot && this.jsonData != this.snapshot){
25798             this.jsonData = this.snapshot;
25799             this.refresh();
25800         }
25801     },
25802
25803
25804 /**
25805  * Sorts the data for this view and refreshes it.
25806  * @param {String} property A property on your JSON objects to sort on
25807  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25808  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25809  */
25810     sort : function(property, dir, sortType){
25811         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25812         if(this.jsonData){
25813             var p = property;
25814             var dsc = dir && dir.toLowerCase() == "desc";
25815             var f = function(o1, o2){
25816                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25817                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25818                 ;
25819                 if(v1 < v2){
25820                     return dsc ? +1 : -1;
25821                 } else if(v1 > v2){
25822                     return dsc ? -1 : +1;
25823                 } else{
25824                     return 0;
25825                 }
25826             };
25827             this.jsonData.sort(f);
25828             this.refresh();
25829             if(this.jsonData != this.snapshot){
25830                 this.snapshot.sort(f);
25831             }
25832         }
25833     }
25834 });/*
25835  * Based on:
25836  * Ext JS Library 1.1.1
25837  * Copyright(c) 2006-2007, Ext JS, LLC.
25838  *
25839  * Originally Released Under LGPL - original licence link has changed is not relivant.
25840  *
25841  * Fork - LGPL
25842  * <script type="text/javascript">
25843  */
25844  
25845
25846 /**
25847  * @class Roo.ColorPalette
25848  * @extends Roo.Component
25849  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25850  * Here's an example of typical usage:
25851  * <pre><code>
25852 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25853 cp.render('my-div');
25854
25855 cp.on('select', function(palette, selColor){
25856     // do something with selColor
25857 });
25858 </code></pre>
25859  * @constructor
25860  * Create a new ColorPalette
25861  * @param {Object} config The config object
25862  */
25863 Roo.ColorPalette = function(config){
25864     Roo.ColorPalette.superclass.constructor.call(this, config);
25865     this.addEvents({
25866         /**
25867              * @event select
25868              * Fires when a color is selected
25869              * @param {ColorPalette} this
25870              * @param {String} color The 6-digit color hex code (without the # symbol)
25871              */
25872         select: true
25873     });
25874
25875     if(this.handler){
25876         this.on("select", this.handler, this.scope, true);
25877     }
25878 };
25879 Roo.extend(Roo.ColorPalette, Roo.Component, {
25880     /**
25881      * @cfg {String} itemCls
25882      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25883      */
25884     itemCls : "x-color-palette",
25885     /**
25886      * @cfg {String} value
25887      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25888      * the hex codes are case-sensitive.
25889      */
25890     value : null,
25891     clickEvent:'click',
25892     // private
25893     ctype: "Roo.ColorPalette",
25894
25895     /**
25896      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25897      */
25898     allowReselect : false,
25899
25900     /**
25901      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25902      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25903      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25904      * of colors with the width setting until the box is symmetrical.</p>
25905      * <p>You can override individual colors if needed:</p>
25906      * <pre><code>
25907 var cp = new Roo.ColorPalette();
25908 cp.colors[0] = "FF0000";  // change the first box to red
25909 </code></pre>
25910
25911 Or you can provide a custom array of your own for complete control:
25912 <pre><code>
25913 var cp = new Roo.ColorPalette();
25914 cp.colors = ["000000", "993300", "333300"];
25915 </code></pre>
25916      * @type Array
25917      */
25918     colors : [
25919         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25920         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25921         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25922         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25923         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25924     ],
25925
25926     // private
25927     onRender : function(container, position){
25928         var t = new Roo.MasterTemplate(
25929             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25930         );
25931         var c = this.colors;
25932         for(var i = 0, len = c.length; i < len; i++){
25933             t.add([c[i]]);
25934         }
25935         var el = document.createElement("div");
25936         el.className = this.itemCls;
25937         t.overwrite(el);
25938         container.dom.insertBefore(el, position);
25939         this.el = Roo.get(el);
25940         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25941         if(this.clickEvent != 'click'){
25942             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25943         }
25944     },
25945
25946     // private
25947     afterRender : function(){
25948         Roo.ColorPalette.superclass.afterRender.call(this);
25949         if(this.value){
25950             var s = this.value;
25951             this.value = null;
25952             this.select(s);
25953         }
25954     },
25955
25956     // private
25957     handleClick : function(e, t){
25958         e.preventDefault();
25959         if(!this.disabled){
25960             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25961             this.select(c.toUpperCase());
25962         }
25963     },
25964
25965     /**
25966      * Selects the specified color in the palette (fires the select event)
25967      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25968      */
25969     select : function(color){
25970         color = color.replace("#", "");
25971         if(color != this.value || this.allowReselect){
25972             var el = this.el;
25973             if(this.value){
25974                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25975             }
25976             el.child("a.color-"+color).addClass("x-color-palette-sel");
25977             this.value = color;
25978             this.fireEvent("select", this, color);
25979         }
25980     }
25981 });/*
25982  * Based on:
25983  * Ext JS Library 1.1.1
25984  * Copyright(c) 2006-2007, Ext JS, LLC.
25985  *
25986  * Originally Released Under LGPL - original licence link has changed is not relivant.
25987  *
25988  * Fork - LGPL
25989  * <script type="text/javascript">
25990  */
25991  
25992 /**
25993  * @class Roo.DatePicker
25994  * @extends Roo.Component
25995  * Simple date picker class.
25996  * @constructor
25997  * Create a new DatePicker
25998  * @param {Object} config The config object
25999  */
26000 Roo.DatePicker = function(config){
26001     Roo.DatePicker.superclass.constructor.call(this, config);
26002
26003     this.value = config && config.value ?
26004                  config.value.clearTime() : new Date().clearTime();
26005
26006     this.addEvents({
26007         /**
26008              * @event select
26009              * Fires when a date is selected
26010              * @param {DatePicker} this
26011              * @param {Date} date The selected date
26012              */
26013         'select': true,
26014         /**
26015              * @event monthchange
26016              * Fires when the displayed month changes 
26017              * @param {DatePicker} this
26018              * @param {Date} date The selected month
26019              */
26020         'monthchange': true
26021     });
26022
26023     if(this.handler){
26024         this.on("select", this.handler,  this.scope || this);
26025     }
26026     // build the disabledDatesRE
26027     if(!this.disabledDatesRE && this.disabledDates){
26028         var dd = this.disabledDates;
26029         var re = "(?:";
26030         for(var i = 0; i < dd.length; i++){
26031             re += dd[i];
26032             if(i != dd.length-1) re += "|";
26033         }
26034         this.disabledDatesRE = new RegExp(re + ")");
26035     }
26036 };
26037
26038 Roo.extend(Roo.DatePicker, Roo.Component, {
26039     /**
26040      * @cfg {String} todayText
26041      * The text to display on the button that selects the current date (defaults to "Today")
26042      */
26043     todayText : "Today",
26044     /**
26045      * @cfg {String} okText
26046      * The text to display on the ok button
26047      */
26048     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26049     /**
26050      * @cfg {String} cancelText
26051      * The text to display on the cancel button
26052      */
26053     cancelText : "Cancel",
26054     /**
26055      * @cfg {String} todayTip
26056      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26057      */
26058     todayTip : "{0} (Spacebar)",
26059     /**
26060      * @cfg {Date} minDate
26061      * Minimum allowable date (JavaScript date object, defaults to null)
26062      */
26063     minDate : null,
26064     /**
26065      * @cfg {Date} maxDate
26066      * Maximum allowable date (JavaScript date object, defaults to null)
26067      */
26068     maxDate : null,
26069     /**
26070      * @cfg {String} minText
26071      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26072      */
26073     minText : "This date is before the minimum date",
26074     /**
26075      * @cfg {String} maxText
26076      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26077      */
26078     maxText : "This date is after the maximum date",
26079     /**
26080      * @cfg {String} format
26081      * The default date format string which can be overriden for localization support.  The format must be
26082      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26083      */
26084     format : "m/d/y",
26085     /**
26086      * @cfg {Array} disabledDays
26087      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26088      */
26089     disabledDays : null,
26090     /**
26091      * @cfg {String} disabledDaysText
26092      * The tooltip to display when the date falls on a disabled day (defaults to "")
26093      */
26094     disabledDaysText : "",
26095     /**
26096      * @cfg {RegExp} disabledDatesRE
26097      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26098      */
26099     disabledDatesRE : null,
26100     /**
26101      * @cfg {String} disabledDatesText
26102      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26103      */
26104     disabledDatesText : "",
26105     /**
26106      * @cfg {Boolean} constrainToViewport
26107      * True to constrain the date picker to the viewport (defaults to true)
26108      */
26109     constrainToViewport : true,
26110     /**
26111      * @cfg {Array} monthNames
26112      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26113      */
26114     monthNames : Date.monthNames,
26115     /**
26116      * @cfg {Array} dayNames
26117      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26118      */
26119     dayNames : Date.dayNames,
26120     /**
26121      * @cfg {String} nextText
26122      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26123      */
26124     nextText: 'Next Month (Control+Right)',
26125     /**
26126      * @cfg {String} prevText
26127      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26128      */
26129     prevText: 'Previous Month (Control+Left)',
26130     /**
26131      * @cfg {String} monthYearText
26132      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26133      */
26134     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26135     /**
26136      * @cfg {Number} startDay
26137      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26138      */
26139     startDay : 0,
26140     /**
26141      * @cfg {Bool} showClear
26142      * Show a clear button (usefull for date form elements that can be blank.)
26143      */
26144     
26145     showClear: false,
26146     
26147     /**
26148      * Sets the value of the date field
26149      * @param {Date} value The date to set
26150      */
26151     setValue : function(value){
26152         var old = this.value;
26153         
26154         if (typeof(value) == 'string') {
26155          
26156             value = Date.parseDate(value, this.format);
26157         }
26158         if (!value) {
26159             value = new Date();
26160         }
26161         
26162         this.value = value.clearTime(true);
26163         if(this.el){
26164             this.update(this.value);
26165         }
26166     },
26167
26168     /**
26169      * Gets the current selected value of the date field
26170      * @return {Date} The selected date
26171      */
26172     getValue : function(){
26173         return this.value;
26174     },
26175
26176     // private
26177     focus : function(){
26178         if(this.el){
26179             this.update(this.activeDate);
26180         }
26181     },
26182
26183     // privateval
26184     onRender : function(container, position){
26185         
26186         var m = [
26187              '<table cellspacing="0">',
26188                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
26189                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26190         var dn = this.dayNames;
26191         for(var i = 0; i < 7; i++){
26192             var d = this.startDay+i;
26193             if(d > 6){
26194                 d = d-7;
26195             }
26196             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26197         }
26198         m[m.length] = "</tr></thead><tbody><tr>";
26199         for(var i = 0; i < 42; i++) {
26200             if(i % 7 == 0 && i != 0){
26201                 m[m.length] = "</tr><tr>";
26202             }
26203             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26204         }
26205         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26206             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26207
26208         var el = document.createElement("div");
26209         el.className = "x-date-picker";
26210         el.innerHTML = m.join("");
26211
26212         container.dom.insertBefore(el, position);
26213
26214         this.el = Roo.get(el);
26215         this.eventEl = Roo.get(el.firstChild);
26216
26217         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26218             handler: this.showPrevMonth,
26219             scope: this,
26220             preventDefault:true,
26221             stopDefault:true
26222         });
26223
26224         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26225             handler: this.showNextMonth,
26226             scope: this,
26227             preventDefault:true,
26228             stopDefault:true
26229         });
26230
26231         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26232
26233         this.monthPicker = this.el.down('div.x-date-mp');
26234         this.monthPicker.enableDisplayMode('block');
26235         
26236         var kn = new Roo.KeyNav(this.eventEl, {
26237             "left" : function(e){
26238                 e.ctrlKey ?
26239                     this.showPrevMonth() :
26240                     this.update(this.activeDate.add("d", -1));
26241             },
26242
26243             "right" : function(e){
26244                 e.ctrlKey ?
26245                     this.showNextMonth() :
26246                     this.update(this.activeDate.add("d", 1));
26247             },
26248
26249             "up" : function(e){
26250                 e.ctrlKey ?
26251                     this.showNextYear() :
26252                     this.update(this.activeDate.add("d", -7));
26253             },
26254
26255             "down" : function(e){
26256                 e.ctrlKey ?
26257                     this.showPrevYear() :
26258                     this.update(this.activeDate.add("d", 7));
26259             },
26260
26261             "pageUp" : function(e){
26262                 this.showNextMonth();
26263             },
26264
26265             "pageDown" : function(e){
26266                 this.showPrevMonth();
26267             },
26268
26269             "enter" : function(e){
26270                 e.stopPropagation();
26271                 return true;
26272             },
26273
26274             scope : this
26275         });
26276
26277         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26278
26279         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26280
26281         this.el.unselectable();
26282         
26283         this.cells = this.el.select("table.x-date-inner tbody td");
26284         this.textNodes = this.el.query("table.x-date-inner tbody span");
26285
26286         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26287             text: "&#160;",
26288             tooltip: this.monthYearText
26289         });
26290
26291         this.mbtn.on('click', this.showMonthPicker, this);
26292         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26293
26294
26295         var today = (new Date()).dateFormat(this.format);
26296         
26297         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26298         if (this.showClear) {
26299             baseTb.add( new Roo.Toolbar.Fill());
26300         }
26301         baseTb.add({
26302             text: String.format(this.todayText, today),
26303             tooltip: String.format(this.todayTip, today),
26304             handler: this.selectToday,
26305             scope: this
26306         });
26307         
26308         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26309             
26310         //});
26311         if (this.showClear) {
26312             
26313             baseTb.add( new Roo.Toolbar.Fill());
26314             baseTb.add({
26315                 text: '&#160;',
26316                 cls: 'x-btn-icon x-btn-clear',
26317                 handler: function() {
26318                     //this.value = '';
26319                     this.fireEvent("select", this, '');
26320                 },
26321                 scope: this
26322             });
26323         }
26324         
26325         
26326         if(Roo.isIE){
26327             this.el.repaint();
26328         }
26329         this.update(this.value);
26330     },
26331
26332     createMonthPicker : function(){
26333         if(!this.monthPicker.dom.firstChild){
26334             var buf = ['<table border="0" cellspacing="0">'];
26335             for(var i = 0; i < 6; i++){
26336                 buf.push(
26337                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26338                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26339                     i == 0 ?
26340                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
26341                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26342                 );
26343             }
26344             buf.push(
26345                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26346                     this.okText,
26347                     '</button><button type="button" class="x-date-mp-cancel">',
26348                     this.cancelText,
26349                     '</button></td></tr>',
26350                 '</table>'
26351             );
26352             this.monthPicker.update(buf.join(''));
26353             this.monthPicker.on('click', this.onMonthClick, this);
26354             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26355
26356             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26357             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26358
26359             this.mpMonths.each(function(m, a, i){
26360                 i += 1;
26361                 if((i%2) == 0){
26362                     m.dom.xmonth = 5 + Math.round(i * .5);
26363                 }else{
26364                     m.dom.xmonth = Math.round((i-1) * .5);
26365                 }
26366             });
26367         }
26368     },
26369
26370     showMonthPicker : function(){
26371         this.createMonthPicker();
26372         var size = this.el.getSize();
26373         this.monthPicker.setSize(size);
26374         this.monthPicker.child('table').setSize(size);
26375
26376         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26377         this.updateMPMonth(this.mpSelMonth);
26378         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26379         this.updateMPYear(this.mpSelYear);
26380
26381         this.monthPicker.slideIn('t', {duration:.2});
26382     },
26383
26384     updateMPYear : function(y){
26385         this.mpyear = y;
26386         var ys = this.mpYears.elements;
26387         for(var i = 1; i <= 10; i++){
26388             var td = ys[i-1], y2;
26389             if((i%2) == 0){
26390                 y2 = y + Math.round(i * .5);
26391                 td.firstChild.innerHTML = y2;
26392                 td.xyear = y2;
26393             }else{
26394                 y2 = y - (5-Math.round(i * .5));
26395                 td.firstChild.innerHTML = y2;
26396                 td.xyear = y2;
26397             }
26398             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26399         }
26400     },
26401
26402     updateMPMonth : function(sm){
26403         this.mpMonths.each(function(m, a, i){
26404             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26405         });
26406     },
26407
26408     selectMPMonth: function(m){
26409         
26410     },
26411
26412     onMonthClick : function(e, t){
26413         e.stopEvent();
26414         var el = new Roo.Element(t), pn;
26415         if(el.is('button.x-date-mp-cancel')){
26416             this.hideMonthPicker();
26417         }
26418         else if(el.is('button.x-date-mp-ok')){
26419             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26420             this.hideMonthPicker();
26421         }
26422         else if(pn = el.up('td.x-date-mp-month', 2)){
26423             this.mpMonths.removeClass('x-date-mp-sel');
26424             pn.addClass('x-date-mp-sel');
26425             this.mpSelMonth = pn.dom.xmonth;
26426         }
26427         else if(pn = el.up('td.x-date-mp-year', 2)){
26428             this.mpYears.removeClass('x-date-mp-sel');
26429             pn.addClass('x-date-mp-sel');
26430             this.mpSelYear = pn.dom.xyear;
26431         }
26432         else if(el.is('a.x-date-mp-prev')){
26433             this.updateMPYear(this.mpyear-10);
26434         }
26435         else if(el.is('a.x-date-mp-next')){
26436             this.updateMPYear(this.mpyear+10);
26437         }
26438     },
26439
26440     onMonthDblClick : function(e, t){
26441         e.stopEvent();
26442         var el = new Roo.Element(t), pn;
26443         if(pn = el.up('td.x-date-mp-month', 2)){
26444             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26445             this.hideMonthPicker();
26446         }
26447         else if(pn = el.up('td.x-date-mp-year', 2)){
26448             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26449             this.hideMonthPicker();
26450         }
26451     },
26452
26453     hideMonthPicker : function(disableAnim){
26454         if(this.monthPicker){
26455             if(disableAnim === true){
26456                 this.monthPicker.hide();
26457             }else{
26458                 this.monthPicker.slideOut('t', {duration:.2});
26459             }
26460         }
26461     },
26462
26463     // private
26464     showPrevMonth : function(e){
26465         this.update(this.activeDate.add("mo", -1));
26466     },
26467
26468     // private
26469     showNextMonth : function(e){
26470         this.update(this.activeDate.add("mo", 1));
26471     },
26472
26473     // private
26474     showPrevYear : function(){
26475         this.update(this.activeDate.add("y", -1));
26476     },
26477
26478     // private
26479     showNextYear : function(){
26480         this.update(this.activeDate.add("y", 1));
26481     },
26482
26483     // private
26484     handleMouseWheel : function(e){
26485         var delta = e.getWheelDelta();
26486         if(delta > 0){
26487             this.showPrevMonth();
26488             e.stopEvent();
26489         } else if(delta < 0){
26490             this.showNextMonth();
26491             e.stopEvent();
26492         }
26493     },
26494
26495     // private
26496     handleDateClick : function(e, t){
26497         e.stopEvent();
26498         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26499             this.setValue(new Date(t.dateValue));
26500             this.fireEvent("select", this, this.value);
26501         }
26502     },
26503
26504     // private
26505     selectToday : function(){
26506         this.setValue(new Date().clearTime());
26507         this.fireEvent("select", this, this.value);
26508     },
26509
26510     // private
26511     update : function(date)
26512     {
26513         var vd = this.activeDate;
26514         this.activeDate = date;
26515         if(vd && this.el){
26516             var t = date.getTime();
26517             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26518                 this.cells.removeClass("x-date-selected");
26519                 this.cells.each(function(c){
26520                    if(c.dom.firstChild.dateValue == t){
26521                        c.addClass("x-date-selected");
26522                        setTimeout(function(){
26523                             try{c.dom.firstChild.focus();}catch(e){}
26524                        }, 50);
26525                        return false;
26526                    }
26527                 });
26528                 return;
26529             }
26530         }
26531         
26532         var days = date.getDaysInMonth();
26533         var firstOfMonth = date.getFirstDateOfMonth();
26534         var startingPos = firstOfMonth.getDay()-this.startDay;
26535
26536         if(startingPos <= this.startDay){
26537             startingPos += 7;
26538         }
26539
26540         var pm = date.add("mo", -1);
26541         var prevStart = pm.getDaysInMonth()-startingPos;
26542
26543         var cells = this.cells.elements;
26544         var textEls = this.textNodes;
26545         days += startingPos;
26546
26547         // convert everything to numbers so it's fast
26548         var day = 86400000;
26549         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26550         var today = new Date().clearTime().getTime();
26551         var sel = date.clearTime().getTime();
26552         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26553         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26554         var ddMatch = this.disabledDatesRE;
26555         var ddText = this.disabledDatesText;
26556         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26557         var ddaysText = this.disabledDaysText;
26558         var format = this.format;
26559
26560         var setCellClass = function(cal, cell){
26561             cell.title = "";
26562             var t = d.getTime();
26563             cell.firstChild.dateValue = t;
26564             if(t == today){
26565                 cell.className += " x-date-today";
26566                 cell.title = cal.todayText;
26567             }
26568             if(t == sel){
26569                 cell.className += " x-date-selected";
26570                 setTimeout(function(){
26571                     try{cell.firstChild.focus();}catch(e){}
26572                 }, 50);
26573             }
26574             // disabling
26575             if(t < min) {
26576                 cell.className = " x-date-disabled";
26577                 cell.title = cal.minText;
26578                 return;
26579             }
26580             if(t > max) {
26581                 cell.className = " x-date-disabled";
26582                 cell.title = cal.maxText;
26583                 return;
26584             }
26585             if(ddays){
26586                 if(ddays.indexOf(d.getDay()) != -1){
26587                     cell.title = ddaysText;
26588                     cell.className = " x-date-disabled";
26589                 }
26590             }
26591             if(ddMatch && format){
26592                 var fvalue = d.dateFormat(format);
26593                 if(ddMatch.test(fvalue)){
26594                     cell.title = ddText.replace("%0", fvalue);
26595                     cell.className = " x-date-disabled";
26596                 }
26597             }
26598         };
26599
26600         var i = 0;
26601         for(; i < startingPos; i++) {
26602             textEls[i].innerHTML = (++prevStart);
26603             d.setDate(d.getDate()+1);
26604             cells[i].className = "x-date-prevday";
26605             setCellClass(this, cells[i]);
26606         }
26607         for(; i < days; i++){
26608             intDay = i - startingPos + 1;
26609             textEls[i].innerHTML = (intDay);
26610             d.setDate(d.getDate()+1);
26611             cells[i].className = "x-date-active";
26612             setCellClass(this, cells[i]);
26613         }
26614         var extraDays = 0;
26615         for(; i < 42; i++) {
26616              textEls[i].innerHTML = (++extraDays);
26617              d.setDate(d.getDate()+1);
26618              cells[i].className = "x-date-nextday";
26619              setCellClass(this, cells[i]);
26620         }
26621
26622         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26623         this.fireEvent('monthchange', this, date);
26624         
26625         if(!this.internalRender){
26626             var main = this.el.dom.firstChild;
26627             var w = main.offsetWidth;
26628             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26629             Roo.fly(main).setWidth(w);
26630             this.internalRender = true;
26631             // opera does not respect the auto grow header center column
26632             // then, after it gets a width opera refuses to recalculate
26633             // without a second pass
26634             if(Roo.isOpera && !this.secondPass){
26635                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26636                 this.secondPass = true;
26637                 this.update.defer(10, this, [date]);
26638             }
26639         }
26640         
26641         
26642     }
26643 });        /*
26644  * Based on:
26645  * Ext JS Library 1.1.1
26646  * Copyright(c) 2006-2007, Ext JS, LLC.
26647  *
26648  * Originally Released Under LGPL - original licence link has changed is not relivant.
26649  *
26650  * Fork - LGPL
26651  * <script type="text/javascript">
26652  */
26653 /**
26654  * @class Roo.TabPanel
26655  * @extends Roo.util.Observable
26656  * A lightweight tab container.
26657  * <br><br>
26658  * Usage:
26659  * <pre><code>
26660 // basic tabs 1, built from existing content
26661 var tabs = new Roo.TabPanel("tabs1");
26662 tabs.addTab("script", "View Script");
26663 tabs.addTab("markup", "View Markup");
26664 tabs.activate("script");
26665
26666 // more advanced tabs, built from javascript
26667 var jtabs = new Roo.TabPanel("jtabs");
26668 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26669
26670 // set up the UpdateManager
26671 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26672 var updater = tab2.getUpdateManager();
26673 updater.setDefaultUrl("ajax1.htm");
26674 tab2.on('activate', updater.refresh, updater, true);
26675
26676 // Use setUrl for Ajax loading
26677 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26678 tab3.setUrl("ajax2.htm", null, true);
26679
26680 // Disabled tab
26681 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26682 tab4.disable();
26683
26684 jtabs.activate("jtabs-1");
26685  * </code></pre>
26686  * @constructor
26687  * Create a new TabPanel.
26688  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26689  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26690  */
26691 Roo.TabPanel = function(container, config){
26692     /**
26693     * The container element for this TabPanel.
26694     * @type Roo.Element
26695     */
26696     this.el = Roo.get(container, true);
26697     if(config){
26698         if(typeof config == "boolean"){
26699             this.tabPosition = config ? "bottom" : "top";
26700         }else{
26701             Roo.apply(this, config);
26702         }
26703     }
26704     if(this.tabPosition == "bottom"){
26705         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26706         this.el.addClass("x-tabs-bottom");
26707     }
26708     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26709     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26710     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26711     if(Roo.isIE){
26712         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26713     }
26714     if(this.tabPosition != "bottom"){
26715         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26716          * @type Roo.Element
26717          */
26718         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26719         this.el.addClass("x-tabs-top");
26720     }
26721     this.items = [];
26722
26723     this.bodyEl.setStyle("position", "relative");
26724
26725     this.active = null;
26726     this.activateDelegate = this.activate.createDelegate(this);
26727
26728     this.addEvents({
26729         /**
26730          * @event tabchange
26731          * Fires when the active tab changes
26732          * @param {Roo.TabPanel} this
26733          * @param {Roo.TabPanelItem} activePanel The new active tab
26734          */
26735         "tabchange": true,
26736         /**
26737          * @event beforetabchange
26738          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26739          * @param {Roo.TabPanel} this
26740          * @param {Object} e Set cancel to true on this object to cancel the tab change
26741          * @param {Roo.TabPanelItem} tab The tab being changed to
26742          */
26743         "beforetabchange" : true
26744     });
26745
26746     Roo.EventManager.onWindowResize(this.onResize, this);
26747     this.cpad = this.el.getPadding("lr");
26748     this.hiddenCount = 0;
26749
26750
26751     // toolbar on the tabbar support...
26752     if (this.toolbar) {
26753         var tcfg = this.toolbar;
26754         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26755         this.toolbar = new Roo.Toolbar(tcfg);
26756         if (Roo.isSafari) {
26757             var tbl = tcfg.container.child('table', true);
26758             tbl.setAttribute('width', '100%');
26759         }
26760         
26761     }
26762    
26763
26764
26765     Roo.TabPanel.superclass.constructor.call(this);
26766 };
26767
26768 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26769     /*
26770      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26771      */
26772     tabPosition : "top",
26773     /*
26774      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26775      */
26776     currentTabWidth : 0,
26777     /*
26778      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26779      */
26780     minTabWidth : 40,
26781     /*
26782      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26783      */
26784     maxTabWidth : 250,
26785     /*
26786      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26787      */
26788     preferredTabWidth : 175,
26789     /*
26790      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26791      */
26792     resizeTabs : false,
26793     /*
26794      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26795      */
26796     monitorResize : true,
26797     /*
26798      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26799      */
26800     toolbar : false,
26801
26802     /**
26803      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26804      * @param {String} id The id of the div to use <b>or create</b>
26805      * @param {String} text The text for the tab
26806      * @param {String} content (optional) Content to put in the TabPanelItem body
26807      * @param {Boolean} closable (optional) True to create a close icon on the tab
26808      * @return {Roo.TabPanelItem} The created TabPanelItem
26809      */
26810     addTab : function(id, text, content, closable){
26811         var item = new Roo.TabPanelItem(this, id, text, closable);
26812         this.addTabItem(item);
26813         if(content){
26814             item.setContent(content);
26815         }
26816         return item;
26817     },
26818
26819     /**
26820      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26821      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26822      * @return {Roo.TabPanelItem}
26823      */
26824     getTab : function(id){
26825         return this.items[id];
26826     },
26827
26828     /**
26829      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26830      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26831      */
26832     hideTab : function(id){
26833         var t = this.items[id];
26834         if(!t.isHidden()){
26835            t.setHidden(true);
26836            this.hiddenCount++;
26837            this.autoSizeTabs();
26838         }
26839     },
26840
26841     /**
26842      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26843      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26844      */
26845     unhideTab : function(id){
26846         var t = this.items[id];
26847         if(t.isHidden()){
26848            t.setHidden(false);
26849            this.hiddenCount--;
26850            this.autoSizeTabs();
26851         }
26852     },
26853
26854     /**
26855      * Adds an existing {@link Roo.TabPanelItem}.
26856      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26857      */
26858     addTabItem : function(item){
26859         this.items[item.id] = item;
26860         this.items.push(item);
26861         if(this.resizeTabs){
26862            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26863            this.autoSizeTabs();
26864         }else{
26865             item.autoSize();
26866         }
26867     },
26868
26869     /**
26870      * Removes a {@link Roo.TabPanelItem}.
26871      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26872      */
26873     removeTab : function(id){
26874         var items = this.items;
26875         var tab = items[id];
26876         if(!tab) { return; }
26877         var index = items.indexOf(tab);
26878         if(this.active == tab && items.length > 1){
26879             var newTab = this.getNextAvailable(index);
26880             if(newTab) {
26881                 newTab.activate();
26882             }
26883         }
26884         this.stripEl.dom.removeChild(tab.pnode.dom);
26885         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26886             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26887         }
26888         items.splice(index, 1);
26889         delete this.items[tab.id];
26890         tab.fireEvent("close", tab);
26891         tab.purgeListeners();
26892         this.autoSizeTabs();
26893     },
26894
26895     getNextAvailable : function(start){
26896         var items = this.items;
26897         var index = start;
26898         // look for a next tab that will slide over to
26899         // replace the one being removed
26900         while(index < items.length){
26901             var item = items[++index];
26902             if(item && !item.isHidden()){
26903                 return item;
26904             }
26905         }
26906         // if one isn't found select the previous tab (on the left)
26907         index = start;
26908         while(index >= 0){
26909             var item = items[--index];
26910             if(item && !item.isHidden()){
26911                 return item;
26912             }
26913         }
26914         return null;
26915     },
26916
26917     /**
26918      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26919      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26920      */
26921     disableTab : function(id){
26922         var tab = this.items[id];
26923         if(tab && this.active != tab){
26924             tab.disable();
26925         }
26926     },
26927
26928     /**
26929      * Enables a {@link Roo.TabPanelItem} that is disabled.
26930      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26931      */
26932     enableTab : function(id){
26933         var tab = this.items[id];
26934         tab.enable();
26935     },
26936
26937     /**
26938      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26939      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26940      * @return {Roo.TabPanelItem} The TabPanelItem.
26941      */
26942     activate : function(id){
26943         var tab = this.items[id];
26944         if(!tab){
26945             return null;
26946         }
26947         if(tab == this.active || tab.disabled){
26948             return tab;
26949         }
26950         var e = {};
26951         this.fireEvent("beforetabchange", this, e, tab);
26952         if(e.cancel !== true && !tab.disabled){
26953             if(this.active){
26954                 this.active.hide();
26955             }
26956             this.active = this.items[id];
26957             this.active.show();
26958             this.fireEvent("tabchange", this, this.active);
26959         }
26960         return tab;
26961     },
26962
26963     /**
26964      * Gets the active {@link Roo.TabPanelItem}.
26965      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26966      */
26967     getActiveTab : function(){
26968         return this.active;
26969     },
26970
26971     /**
26972      * Updates the tab body element to fit the height of the container element
26973      * for overflow scrolling
26974      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26975      */
26976     syncHeight : function(targetHeight){
26977         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26978         var bm = this.bodyEl.getMargins();
26979         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26980         this.bodyEl.setHeight(newHeight);
26981         return newHeight;
26982     },
26983
26984     onResize : function(){
26985         if(this.monitorResize){
26986             this.autoSizeTabs();
26987         }
26988     },
26989
26990     /**
26991      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26992      */
26993     beginUpdate : function(){
26994         this.updating = true;
26995     },
26996
26997     /**
26998      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26999      */
27000     endUpdate : function(){
27001         this.updating = false;
27002         this.autoSizeTabs();
27003     },
27004
27005     /**
27006      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27007      */
27008     autoSizeTabs : function(){
27009         var count = this.items.length;
27010         var vcount = count - this.hiddenCount;
27011         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27012         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27013         var availWidth = Math.floor(w / vcount);
27014         var b = this.stripBody;
27015         if(b.getWidth() > w){
27016             var tabs = this.items;
27017             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27018             if(availWidth < this.minTabWidth){
27019                 /*if(!this.sleft){    // incomplete scrolling code
27020                     this.createScrollButtons();
27021                 }
27022                 this.showScroll();
27023                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27024             }
27025         }else{
27026             if(this.currentTabWidth < this.preferredTabWidth){
27027                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27028             }
27029         }
27030     },
27031
27032     /**
27033      * Returns the number of tabs in this TabPanel.
27034      * @return {Number}
27035      */
27036      getCount : function(){
27037          return this.items.length;
27038      },
27039
27040     /**
27041      * Resizes all the tabs to the passed width
27042      * @param {Number} The new width
27043      */
27044     setTabWidth : function(width){
27045         this.currentTabWidth = width;
27046         for(var i = 0, len = this.items.length; i < len; i++) {
27047                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27048         }
27049     },
27050
27051     /**
27052      * Destroys this TabPanel
27053      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27054      */
27055     destroy : function(removeEl){
27056         Roo.EventManager.removeResizeListener(this.onResize, this);
27057         for(var i = 0, len = this.items.length; i < len; i++){
27058             this.items[i].purgeListeners();
27059         }
27060         if(removeEl === true){
27061             this.el.update("");
27062             this.el.remove();
27063         }
27064     }
27065 });
27066
27067 /**
27068  * @class Roo.TabPanelItem
27069  * @extends Roo.util.Observable
27070  * Represents an individual item (tab plus body) in a TabPanel.
27071  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27072  * @param {String} id The id of this TabPanelItem
27073  * @param {String} text The text for the tab of this TabPanelItem
27074  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27075  */
27076 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27077     /**
27078      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27079      * @type Roo.TabPanel
27080      */
27081     this.tabPanel = tabPanel;
27082     /**
27083      * The id for this TabPanelItem
27084      * @type String
27085      */
27086     this.id = id;
27087     /** @private */
27088     this.disabled = false;
27089     /** @private */
27090     this.text = text;
27091     /** @private */
27092     this.loaded = false;
27093     this.closable = closable;
27094
27095     /**
27096      * The body element for this TabPanelItem.
27097      * @type Roo.Element
27098      */
27099     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27100     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27101     this.bodyEl.setStyle("display", "block");
27102     this.bodyEl.setStyle("zoom", "1");
27103     this.hideAction();
27104
27105     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27106     /** @private */
27107     this.el = Roo.get(els.el, true);
27108     this.inner = Roo.get(els.inner, true);
27109     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27110     this.pnode = Roo.get(els.el.parentNode, true);
27111     this.el.on("mousedown", this.onTabMouseDown, this);
27112     this.el.on("click", this.onTabClick, this);
27113     /** @private */
27114     if(closable){
27115         var c = Roo.get(els.close, true);
27116         c.dom.title = this.closeText;
27117         c.addClassOnOver("close-over");
27118         c.on("click", this.closeClick, this);
27119      }
27120
27121     this.addEvents({
27122          /**
27123          * @event activate
27124          * Fires when this tab becomes the active tab.
27125          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27126          * @param {Roo.TabPanelItem} this
27127          */
27128         "activate": true,
27129         /**
27130          * @event beforeclose
27131          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27132          * @param {Roo.TabPanelItem} this
27133          * @param {Object} e Set cancel to true on this object to cancel the close.
27134          */
27135         "beforeclose": true,
27136         /**
27137          * @event close
27138          * Fires when this tab is closed.
27139          * @param {Roo.TabPanelItem} this
27140          */
27141          "close": true,
27142         /**
27143          * @event deactivate
27144          * Fires when this tab is no longer the active tab.
27145          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27146          * @param {Roo.TabPanelItem} this
27147          */
27148          "deactivate" : true
27149     });
27150     this.hidden = false;
27151
27152     Roo.TabPanelItem.superclass.constructor.call(this);
27153 };
27154
27155 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27156     purgeListeners : function(){
27157        Roo.util.Observable.prototype.purgeListeners.call(this);
27158        this.el.removeAllListeners();
27159     },
27160     /**
27161      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27162      */
27163     show : function(){
27164         this.pnode.addClass("on");
27165         this.showAction();
27166         if(Roo.isOpera){
27167             this.tabPanel.stripWrap.repaint();
27168         }
27169         this.fireEvent("activate", this.tabPanel, this);
27170     },
27171
27172     /**
27173      * Returns true if this tab is the active tab.
27174      * @return {Boolean}
27175      */
27176     isActive : function(){
27177         return this.tabPanel.getActiveTab() == this;
27178     },
27179
27180     /**
27181      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27182      */
27183     hide : function(){
27184         this.pnode.removeClass("on");
27185         this.hideAction();
27186         this.fireEvent("deactivate", this.tabPanel, this);
27187     },
27188
27189     hideAction : function(){
27190         this.bodyEl.hide();
27191         this.bodyEl.setStyle("position", "absolute");
27192         this.bodyEl.setLeft("-20000px");
27193         this.bodyEl.setTop("-20000px");
27194     },
27195
27196     showAction : function(){
27197         this.bodyEl.setStyle("position", "relative");
27198         this.bodyEl.setTop("");
27199         this.bodyEl.setLeft("");
27200         this.bodyEl.show();
27201     },
27202
27203     /**
27204      * Set the tooltip for the tab.
27205      * @param {String} tooltip The tab's tooltip
27206      */
27207     setTooltip : function(text){
27208         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27209             this.textEl.dom.qtip = text;
27210             this.textEl.dom.removeAttribute('title');
27211         }else{
27212             this.textEl.dom.title = text;
27213         }
27214     },
27215
27216     onTabClick : function(e){
27217         e.preventDefault();
27218         this.tabPanel.activate(this.id);
27219     },
27220
27221     onTabMouseDown : function(e){
27222         e.preventDefault();
27223         this.tabPanel.activate(this.id);
27224     },
27225
27226     getWidth : function(){
27227         return this.inner.getWidth();
27228     },
27229
27230     setWidth : function(width){
27231         var iwidth = width - this.pnode.getPadding("lr");
27232         this.inner.setWidth(iwidth);
27233         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27234         this.pnode.setWidth(width);
27235     },
27236
27237     /**
27238      * Show or hide the tab
27239      * @param {Boolean} hidden True to hide or false to show.
27240      */
27241     setHidden : function(hidden){
27242         this.hidden = hidden;
27243         this.pnode.setStyle("display", hidden ? "none" : "");
27244     },
27245
27246     /**
27247      * Returns true if this tab is "hidden"
27248      * @return {Boolean}
27249      */
27250     isHidden : function(){
27251         return this.hidden;
27252     },
27253
27254     /**
27255      * Returns the text for this tab
27256      * @return {String}
27257      */
27258     getText : function(){
27259         return this.text;
27260     },
27261
27262     autoSize : function(){
27263         //this.el.beginMeasure();
27264         this.textEl.setWidth(1);
27265         /*
27266          *  #2804 [new] Tabs in Roojs
27267          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27268          */
27269         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27270         //this.el.endMeasure();
27271     },
27272
27273     /**
27274      * Sets the text for the tab (Note: this also sets the tooltip text)
27275      * @param {String} text The tab's text and tooltip
27276      */
27277     setText : function(text){
27278         this.text = text;
27279         this.textEl.update(text);
27280         this.setTooltip(text);
27281         if(!this.tabPanel.resizeTabs){
27282             this.autoSize();
27283         }
27284     },
27285     /**
27286      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27287      */
27288     activate : function(){
27289         this.tabPanel.activate(this.id);
27290     },
27291
27292     /**
27293      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27294      */
27295     disable : function(){
27296         if(this.tabPanel.active != this){
27297             this.disabled = true;
27298             this.pnode.addClass("disabled");
27299         }
27300     },
27301
27302     /**
27303      * Enables this TabPanelItem if it was previously disabled.
27304      */
27305     enable : function(){
27306         this.disabled = false;
27307         this.pnode.removeClass("disabled");
27308     },
27309
27310     /**
27311      * Sets the content for this TabPanelItem.
27312      * @param {String} content The content
27313      * @param {Boolean} loadScripts true to look for and load scripts
27314      */
27315     setContent : function(content, loadScripts){
27316         this.bodyEl.update(content, loadScripts);
27317     },
27318
27319     /**
27320      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27321      * @return {Roo.UpdateManager} The UpdateManager
27322      */
27323     getUpdateManager : function(){
27324         return this.bodyEl.getUpdateManager();
27325     },
27326
27327     /**
27328      * Set a URL to be used to load the content for this TabPanelItem.
27329      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27330      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
27331      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
27332      * @return {Roo.UpdateManager} The UpdateManager
27333      */
27334     setUrl : function(url, params, loadOnce){
27335         if(this.refreshDelegate){
27336             this.un('activate', this.refreshDelegate);
27337         }
27338         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27339         this.on("activate", this.refreshDelegate);
27340         return this.bodyEl.getUpdateManager();
27341     },
27342
27343     /** @private */
27344     _handleRefresh : function(url, params, loadOnce){
27345         if(!loadOnce || !this.loaded){
27346             var updater = this.bodyEl.getUpdateManager();
27347             updater.update(url, params, this._setLoaded.createDelegate(this));
27348         }
27349     },
27350
27351     /**
27352      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27353      *   Will fail silently if the setUrl method has not been called.
27354      *   This does not activate the panel, just updates its content.
27355      */
27356     refresh : function(){
27357         if(this.refreshDelegate){
27358            this.loaded = false;
27359            this.refreshDelegate();
27360         }
27361     },
27362
27363     /** @private */
27364     _setLoaded : function(){
27365         this.loaded = true;
27366     },
27367
27368     /** @private */
27369     closeClick : function(e){
27370         var o = {};
27371         e.stopEvent();
27372         this.fireEvent("beforeclose", this, o);
27373         if(o.cancel !== true){
27374             this.tabPanel.removeTab(this.id);
27375         }
27376     },
27377     /**
27378      * The text displayed in the tooltip for the close icon.
27379      * @type String
27380      */
27381     closeText : "Close this tab"
27382 });
27383
27384 /** @private */
27385 Roo.TabPanel.prototype.createStrip = function(container){
27386     var strip = document.createElement("div");
27387     strip.className = "x-tabs-wrap";
27388     container.appendChild(strip);
27389     return strip;
27390 };
27391 /** @private */
27392 Roo.TabPanel.prototype.createStripList = function(strip){
27393     // div wrapper for retard IE
27394     // returns the "tr" element.
27395     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27396         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27397         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27398     return strip.firstChild.firstChild.firstChild.firstChild;
27399 };
27400 /** @private */
27401 Roo.TabPanel.prototype.createBody = function(container){
27402     var body = document.createElement("div");
27403     Roo.id(body, "tab-body");
27404     Roo.fly(body).addClass("x-tabs-body");
27405     container.appendChild(body);
27406     return body;
27407 };
27408 /** @private */
27409 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27410     var body = Roo.getDom(id);
27411     if(!body){
27412         body = document.createElement("div");
27413         body.id = id;
27414     }
27415     Roo.fly(body).addClass("x-tabs-item-body");
27416     bodyEl.insertBefore(body, bodyEl.firstChild);
27417     return body;
27418 };
27419 /** @private */
27420 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27421     var td = document.createElement("td");
27422     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27423     //stripEl.appendChild(td);
27424     if(closable){
27425         td.className = "x-tabs-closable";
27426         if(!this.closeTpl){
27427             this.closeTpl = new Roo.Template(
27428                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27429                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27430                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27431             );
27432         }
27433         var el = this.closeTpl.overwrite(td, {"text": text});
27434         var close = el.getElementsByTagName("div")[0];
27435         var inner = el.getElementsByTagName("em")[0];
27436         return {"el": el, "close": close, "inner": inner};
27437     } else {
27438         if(!this.tabTpl){
27439             this.tabTpl = new Roo.Template(
27440                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27441                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27442             );
27443         }
27444         var el = this.tabTpl.overwrite(td, {"text": text});
27445         var inner = el.getElementsByTagName("em")[0];
27446         return {"el": el, "inner": inner};
27447     }
27448 };/*
27449  * Based on:
27450  * Ext JS Library 1.1.1
27451  * Copyright(c) 2006-2007, Ext JS, LLC.
27452  *
27453  * Originally Released Under LGPL - original licence link has changed is not relivant.
27454  *
27455  * Fork - LGPL
27456  * <script type="text/javascript">
27457  */
27458
27459 /**
27460  * @class Roo.Button
27461  * @extends Roo.util.Observable
27462  * Simple Button class
27463  * @cfg {String} text The button text
27464  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27465  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27466  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27467  * @cfg {Object} scope The scope of the handler
27468  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27469  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27470  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27471  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27472  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27473  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27474    applies if enableToggle = true)
27475  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27476  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27477   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27478  * @constructor
27479  * Create a new button
27480  * @param {Object} config The config object
27481  */
27482 Roo.Button = function(renderTo, config)
27483 {
27484     if (!config) {
27485         config = renderTo;
27486         renderTo = config.renderTo || false;
27487     }
27488     
27489     Roo.apply(this, config);
27490     this.addEvents({
27491         /**
27492              * @event click
27493              * Fires when this button is clicked
27494              * @param {Button} this
27495              * @param {EventObject} e The click event
27496              */
27497             "click" : true,
27498         /**
27499              * @event toggle
27500              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27501              * @param {Button} this
27502              * @param {Boolean} pressed
27503              */
27504             "toggle" : true,
27505         /**
27506              * @event mouseover
27507              * Fires when the mouse hovers over the button
27508              * @param {Button} this
27509              * @param {Event} e The event object
27510              */
27511         'mouseover' : true,
27512         /**
27513              * @event mouseout
27514              * Fires when the mouse exits the button
27515              * @param {Button} this
27516              * @param {Event} e The event object
27517              */
27518         'mouseout': true,
27519          /**
27520              * @event render
27521              * Fires when the button is rendered
27522              * @param {Button} this
27523              */
27524         'render': true
27525     });
27526     if(this.menu){
27527         this.menu = Roo.menu.MenuMgr.get(this.menu);
27528     }
27529     // register listeners first!!  - so render can be captured..
27530     Roo.util.Observable.call(this);
27531     if(renderTo){
27532         this.render(renderTo);
27533     }
27534     
27535   
27536 };
27537
27538 Roo.extend(Roo.Button, Roo.util.Observable, {
27539     /**
27540      * 
27541      */
27542     
27543     /**
27544      * Read-only. True if this button is hidden
27545      * @type Boolean
27546      */
27547     hidden : false,
27548     /**
27549      * Read-only. True if this button is disabled
27550      * @type Boolean
27551      */
27552     disabled : false,
27553     /**
27554      * Read-only. True if this button is pressed (only if enableToggle = true)
27555      * @type Boolean
27556      */
27557     pressed : false,
27558
27559     /**
27560      * @cfg {Number} tabIndex 
27561      * The DOM tabIndex for this button (defaults to undefined)
27562      */
27563     tabIndex : undefined,
27564
27565     /**
27566      * @cfg {Boolean} enableToggle
27567      * True to enable pressed/not pressed toggling (defaults to false)
27568      */
27569     enableToggle: false,
27570     /**
27571      * @cfg {Mixed} menu
27572      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27573      */
27574     menu : undefined,
27575     /**
27576      * @cfg {String} menuAlign
27577      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27578      */
27579     menuAlign : "tl-bl?",
27580
27581     /**
27582      * @cfg {String} iconCls
27583      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27584      */
27585     iconCls : undefined,
27586     /**
27587      * @cfg {String} type
27588      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27589      */
27590     type : 'button',
27591
27592     // private
27593     menuClassTarget: 'tr',
27594
27595     /**
27596      * @cfg {String} clickEvent
27597      * The type of event to map to the button's event handler (defaults to 'click')
27598      */
27599     clickEvent : 'click',
27600
27601     /**
27602      * @cfg {Boolean} handleMouseEvents
27603      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27604      */
27605     handleMouseEvents : true,
27606
27607     /**
27608      * @cfg {String} tooltipType
27609      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27610      */
27611     tooltipType : 'qtip',
27612
27613     /**
27614      * @cfg {String} cls
27615      * A CSS class to apply to the button's main element.
27616      */
27617     
27618     /**
27619      * @cfg {Roo.Template} template (Optional)
27620      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27621      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27622      * require code modifications if required elements (e.g. a button) aren't present.
27623      */
27624
27625     // private
27626     render : function(renderTo){
27627         var btn;
27628         if(this.hideParent){
27629             this.parentEl = Roo.get(renderTo);
27630         }
27631         if(!this.dhconfig){
27632             if(!this.template){
27633                 if(!Roo.Button.buttonTemplate){
27634                     // hideous table template
27635                     Roo.Button.buttonTemplate = new Roo.Template(
27636                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27637                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
27638                         "</tr></tbody></table>");
27639                 }
27640                 this.template = Roo.Button.buttonTemplate;
27641             }
27642             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27643             var btnEl = btn.child("button:first");
27644             btnEl.on('focus', this.onFocus, this);
27645             btnEl.on('blur', this.onBlur, this);
27646             if(this.cls){
27647                 btn.addClass(this.cls);
27648             }
27649             if(this.icon){
27650                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27651             }
27652             if(this.iconCls){
27653                 btnEl.addClass(this.iconCls);
27654                 if(!this.cls){
27655                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27656                 }
27657             }
27658             if(this.tabIndex !== undefined){
27659                 btnEl.dom.tabIndex = this.tabIndex;
27660             }
27661             if(this.tooltip){
27662                 if(typeof this.tooltip == 'object'){
27663                     Roo.QuickTips.tips(Roo.apply({
27664                           target: btnEl.id
27665                     }, this.tooltip));
27666                 } else {
27667                     btnEl.dom[this.tooltipType] = this.tooltip;
27668                 }
27669             }
27670         }else{
27671             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27672         }
27673         this.el = btn;
27674         if(this.id){
27675             this.el.dom.id = this.el.id = this.id;
27676         }
27677         if(this.menu){
27678             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27679             this.menu.on("show", this.onMenuShow, this);
27680             this.menu.on("hide", this.onMenuHide, this);
27681         }
27682         btn.addClass("x-btn");
27683         if(Roo.isIE && !Roo.isIE7){
27684             this.autoWidth.defer(1, this);
27685         }else{
27686             this.autoWidth();
27687         }
27688         if(this.handleMouseEvents){
27689             btn.on("mouseover", this.onMouseOver, this);
27690             btn.on("mouseout", this.onMouseOut, this);
27691             btn.on("mousedown", this.onMouseDown, this);
27692         }
27693         btn.on(this.clickEvent, this.onClick, this);
27694         //btn.on("mouseup", this.onMouseUp, this);
27695         if(this.hidden){
27696             this.hide();
27697         }
27698         if(this.disabled){
27699             this.disable();
27700         }
27701         Roo.ButtonToggleMgr.register(this);
27702         if(this.pressed){
27703             this.el.addClass("x-btn-pressed");
27704         }
27705         if(this.repeat){
27706             var repeater = new Roo.util.ClickRepeater(btn,
27707                 typeof this.repeat == "object" ? this.repeat : {}
27708             );
27709             repeater.on("click", this.onClick,  this);
27710         }
27711         
27712         this.fireEvent('render', this);
27713         
27714     },
27715     /**
27716      * Returns the button's underlying element
27717      * @return {Roo.Element} The element
27718      */
27719     getEl : function(){
27720         return this.el;  
27721     },
27722     
27723     /**
27724      * Destroys this Button and removes any listeners.
27725      */
27726     destroy : function(){
27727         Roo.ButtonToggleMgr.unregister(this);
27728         this.el.removeAllListeners();
27729         this.purgeListeners();
27730         this.el.remove();
27731     },
27732
27733     // private
27734     autoWidth : function(){
27735         if(this.el){
27736             this.el.setWidth("auto");
27737             if(Roo.isIE7 && Roo.isStrict){
27738                 var ib = this.el.child('button');
27739                 if(ib && ib.getWidth() > 20){
27740                     ib.clip();
27741                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27742                 }
27743             }
27744             if(this.minWidth){
27745                 if(this.hidden){
27746                     this.el.beginMeasure();
27747                 }
27748                 if(this.el.getWidth() < this.minWidth){
27749                     this.el.setWidth(this.minWidth);
27750                 }
27751                 if(this.hidden){
27752                     this.el.endMeasure();
27753                 }
27754             }
27755         }
27756     },
27757
27758     /**
27759      * Assigns this button's click handler
27760      * @param {Function} handler The function to call when the button is clicked
27761      * @param {Object} scope (optional) Scope for the function passed in
27762      */
27763     setHandler : function(handler, scope){
27764         this.handler = handler;
27765         this.scope = scope;  
27766     },
27767     
27768     /**
27769      * Sets this button's text
27770      * @param {String} text The button text
27771      */
27772     setText : function(text){
27773         this.text = text;
27774         if(this.el){
27775             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27776         }
27777         this.autoWidth();
27778     },
27779     
27780     /**
27781      * Gets the text for this button
27782      * @return {String} The button text
27783      */
27784     getText : function(){
27785         return this.text;  
27786     },
27787     
27788     /**
27789      * Show this button
27790      */
27791     show: function(){
27792         this.hidden = false;
27793         if(this.el){
27794             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27795         }
27796     },
27797     
27798     /**
27799      * Hide this button
27800      */
27801     hide: function(){
27802         this.hidden = true;
27803         if(this.el){
27804             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27805         }
27806     },
27807     
27808     /**
27809      * Convenience function for boolean show/hide
27810      * @param {Boolean} visible True to show, false to hide
27811      */
27812     setVisible: function(visible){
27813         if(visible) {
27814             this.show();
27815         }else{
27816             this.hide();
27817         }
27818     },
27819     
27820     /**
27821      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27822      * @param {Boolean} state (optional) Force a particular state
27823      */
27824     toggle : function(state){
27825         state = state === undefined ? !this.pressed : state;
27826         if(state != this.pressed){
27827             if(state){
27828                 this.el.addClass("x-btn-pressed");
27829                 this.pressed = true;
27830                 this.fireEvent("toggle", this, true);
27831             }else{
27832                 this.el.removeClass("x-btn-pressed");
27833                 this.pressed = false;
27834                 this.fireEvent("toggle", this, false);
27835             }
27836             if(this.toggleHandler){
27837                 this.toggleHandler.call(this.scope || this, this, state);
27838             }
27839         }
27840     },
27841     
27842     /**
27843      * Focus the button
27844      */
27845     focus : function(){
27846         this.el.child('button:first').focus();
27847     },
27848     
27849     /**
27850      * Disable this button
27851      */
27852     disable : function(){
27853         if(this.el){
27854             this.el.addClass("x-btn-disabled");
27855         }
27856         this.disabled = true;
27857     },
27858     
27859     /**
27860      * Enable this button
27861      */
27862     enable : function(){
27863         if(this.el){
27864             this.el.removeClass("x-btn-disabled");
27865         }
27866         this.disabled = false;
27867     },
27868
27869     /**
27870      * Convenience function for boolean enable/disable
27871      * @param {Boolean} enabled True to enable, false to disable
27872      */
27873     setDisabled : function(v){
27874         this[v !== true ? "enable" : "disable"]();
27875     },
27876
27877     // private
27878     onClick : function(e)
27879     {
27880         if(e){
27881             e.preventDefault();
27882         }
27883         if(e.button != 0){
27884             return;
27885         }
27886         if(!this.disabled){
27887             if(this.enableToggle){
27888                 this.toggle();
27889             }
27890             if(this.menu && !this.menu.isVisible()){
27891                 this.menu.show(this.el, this.menuAlign);
27892             }
27893             this.fireEvent("click", this, e);
27894             if(this.handler){
27895                 this.el.removeClass("x-btn-over");
27896                 this.handler.call(this.scope || this, this, e);
27897             }
27898         }
27899     },
27900     // private
27901     onMouseOver : function(e){
27902         if(!this.disabled){
27903             this.el.addClass("x-btn-over");
27904             this.fireEvent('mouseover', this, e);
27905         }
27906     },
27907     // private
27908     onMouseOut : function(e){
27909         if(!e.within(this.el,  true)){
27910             this.el.removeClass("x-btn-over");
27911             this.fireEvent('mouseout', this, e);
27912         }
27913     },
27914     // private
27915     onFocus : function(e){
27916         if(!this.disabled){
27917             this.el.addClass("x-btn-focus");
27918         }
27919     },
27920     // private
27921     onBlur : function(e){
27922         this.el.removeClass("x-btn-focus");
27923     },
27924     // private
27925     onMouseDown : function(e){
27926         if(!this.disabled && e.button == 0){
27927             this.el.addClass("x-btn-click");
27928             Roo.get(document).on('mouseup', this.onMouseUp, this);
27929         }
27930     },
27931     // private
27932     onMouseUp : function(e){
27933         if(e.button == 0){
27934             this.el.removeClass("x-btn-click");
27935             Roo.get(document).un('mouseup', this.onMouseUp, this);
27936         }
27937     },
27938     // private
27939     onMenuShow : function(e){
27940         this.el.addClass("x-btn-menu-active");
27941     },
27942     // private
27943     onMenuHide : function(e){
27944         this.el.removeClass("x-btn-menu-active");
27945     }   
27946 });
27947
27948 // Private utility class used by Button
27949 Roo.ButtonToggleMgr = function(){
27950    var groups = {};
27951    
27952    function toggleGroup(btn, state){
27953        if(state){
27954            var g = groups[btn.toggleGroup];
27955            for(var i = 0, l = g.length; i < l; i++){
27956                if(g[i] != btn){
27957                    g[i].toggle(false);
27958                }
27959            }
27960        }
27961    }
27962    
27963    return {
27964        register : function(btn){
27965            if(!btn.toggleGroup){
27966                return;
27967            }
27968            var g = groups[btn.toggleGroup];
27969            if(!g){
27970                g = groups[btn.toggleGroup] = [];
27971            }
27972            g.push(btn);
27973            btn.on("toggle", toggleGroup);
27974        },
27975        
27976        unregister : function(btn){
27977            if(!btn.toggleGroup){
27978                return;
27979            }
27980            var g = groups[btn.toggleGroup];
27981            if(g){
27982                g.remove(btn);
27983                btn.un("toggle", toggleGroup);
27984            }
27985        }
27986    };
27987 }();/*
27988  * Based on:
27989  * Ext JS Library 1.1.1
27990  * Copyright(c) 2006-2007, Ext JS, LLC.
27991  *
27992  * Originally Released Under LGPL - original licence link has changed is not relivant.
27993  *
27994  * Fork - LGPL
27995  * <script type="text/javascript">
27996  */
27997  
27998 /**
27999  * @class Roo.SplitButton
28000  * @extends Roo.Button
28001  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28002  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28003  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28004  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28005  * @cfg {String} arrowTooltip The title attribute of the arrow
28006  * @constructor
28007  * Create a new menu button
28008  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28009  * @param {Object} config The config object
28010  */
28011 Roo.SplitButton = function(renderTo, config){
28012     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28013     /**
28014      * @event arrowclick
28015      * Fires when this button's arrow is clicked
28016      * @param {SplitButton} this
28017      * @param {EventObject} e The click event
28018      */
28019     this.addEvents({"arrowclick":true});
28020 };
28021
28022 Roo.extend(Roo.SplitButton, Roo.Button, {
28023     render : function(renderTo){
28024         // this is one sweet looking template!
28025         var tpl = new Roo.Template(
28026             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28027             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28028             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
28029             "</tbody></table></td><td>",
28030             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28031             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
28032             "</tbody></table></td></tr></table>"
28033         );
28034         var btn = tpl.append(renderTo, [this.text, this.type], true);
28035         var btnEl = btn.child("button");
28036         if(this.cls){
28037             btn.addClass(this.cls);
28038         }
28039         if(this.icon){
28040             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28041         }
28042         if(this.iconCls){
28043             btnEl.addClass(this.iconCls);
28044             if(!this.cls){
28045                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28046             }
28047         }
28048         this.el = btn;
28049         if(this.handleMouseEvents){
28050             btn.on("mouseover", this.onMouseOver, this);
28051             btn.on("mouseout", this.onMouseOut, this);
28052             btn.on("mousedown", this.onMouseDown, this);
28053             btn.on("mouseup", this.onMouseUp, this);
28054         }
28055         btn.on(this.clickEvent, this.onClick, this);
28056         if(this.tooltip){
28057             if(typeof this.tooltip == 'object'){
28058                 Roo.QuickTips.tips(Roo.apply({
28059                       target: btnEl.id
28060                 }, this.tooltip));
28061             } else {
28062                 btnEl.dom[this.tooltipType] = this.tooltip;
28063             }
28064         }
28065         if(this.arrowTooltip){
28066             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28067         }
28068         if(this.hidden){
28069             this.hide();
28070         }
28071         if(this.disabled){
28072             this.disable();
28073         }
28074         if(this.pressed){
28075             this.el.addClass("x-btn-pressed");
28076         }
28077         if(Roo.isIE && !Roo.isIE7){
28078             this.autoWidth.defer(1, this);
28079         }else{
28080             this.autoWidth();
28081         }
28082         if(this.menu){
28083             this.menu.on("show", this.onMenuShow, this);
28084             this.menu.on("hide", this.onMenuHide, this);
28085         }
28086         this.fireEvent('render', this);
28087     },
28088
28089     // private
28090     autoWidth : function(){
28091         if(this.el){
28092             var tbl = this.el.child("table:first");
28093             var tbl2 = this.el.child("table:last");
28094             this.el.setWidth("auto");
28095             tbl.setWidth("auto");
28096             if(Roo.isIE7 && Roo.isStrict){
28097                 var ib = this.el.child('button:first');
28098                 if(ib && ib.getWidth() > 20){
28099                     ib.clip();
28100                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28101                 }
28102             }
28103             if(this.minWidth){
28104                 if(this.hidden){
28105                     this.el.beginMeasure();
28106                 }
28107                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28108                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28109                 }
28110                 if(this.hidden){
28111                     this.el.endMeasure();
28112                 }
28113             }
28114             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28115         } 
28116     },
28117     /**
28118      * Sets this button's click handler
28119      * @param {Function} handler The function to call when the button is clicked
28120      * @param {Object} scope (optional) Scope for the function passed above
28121      */
28122     setHandler : function(handler, scope){
28123         this.handler = handler;
28124         this.scope = scope;  
28125     },
28126     
28127     /**
28128      * Sets this button's arrow click handler
28129      * @param {Function} handler The function to call when the arrow is clicked
28130      * @param {Object} scope (optional) Scope for the function passed above
28131      */
28132     setArrowHandler : function(handler, scope){
28133         this.arrowHandler = handler;
28134         this.scope = scope;  
28135     },
28136     
28137     /**
28138      * Focus the button
28139      */
28140     focus : function(){
28141         if(this.el){
28142             this.el.child("button:first").focus();
28143         }
28144     },
28145
28146     // private
28147     onClick : function(e){
28148         e.preventDefault();
28149         if(!this.disabled){
28150             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28151                 if(this.menu && !this.menu.isVisible()){
28152                     this.menu.show(this.el, this.menuAlign);
28153                 }
28154                 this.fireEvent("arrowclick", this, e);
28155                 if(this.arrowHandler){
28156                     this.arrowHandler.call(this.scope || this, this, e);
28157                 }
28158             }else{
28159                 this.fireEvent("click", this, e);
28160                 if(this.handler){
28161                     this.handler.call(this.scope || this, this, e);
28162                 }
28163             }
28164         }
28165     },
28166     // private
28167     onMouseDown : function(e){
28168         if(!this.disabled){
28169             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28170         }
28171     },
28172     // private
28173     onMouseUp : function(e){
28174         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28175     }   
28176 });
28177
28178
28179 // backwards compat
28180 Roo.MenuButton = Roo.SplitButton;/*
28181  * Based on:
28182  * Ext JS Library 1.1.1
28183  * Copyright(c) 2006-2007, Ext JS, LLC.
28184  *
28185  * Originally Released Under LGPL - original licence link has changed is not relivant.
28186  *
28187  * Fork - LGPL
28188  * <script type="text/javascript">
28189  */
28190
28191 /**
28192  * @class Roo.Toolbar
28193  * Basic Toolbar class.
28194  * @constructor
28195  * Creates a new Toolbar
28196  * @param {Object} container The config object
28197  */ 
28198 Roo.Toolbar = function(container, buttons, config)
28199 {
28200     /// old consturctor format still supported..
28201     if(container instanceof Array){ // omit the container for later rendering
28202         buttons = container;
28203         config = buttons;
28204         container = null;
28205     }
28206     if (typeof(container) == 'object' && container.xtype) {
28207         config = container;
28208         container = config.container;
28209         buttons = config.buttons || []; // not really - use items!!
28210     }
28211     var xitems = [];
28212     if (config && config.items) {
28213         xitems = config.items;
28214         delete config.items;
28215     }
28216     Roo.apply(this, config);
28217     this.buttons = buttons;
28218     
28219     if(container){
28220         this.render(container);
28221     }
28222     this.xitems = xitems;
28223     Roo.each(xitems, function(b) {
28224         this.add(b);
28225     }, this);
28226     
28227 };
28228
28229 Roo.Toolbar.prototype = {
28230     /**
28231      * @cfg {Array} items
28232      * array of button configs or elements to add (will be converted to a MixedCollection)
28233      */
28234     
28235     /**
28236      * @cfg {String/HTMLElement/Element} container
28237      * The id or element that will contain the toolbar
28238      */
28239     // private
28240     render : function(ct){
28241         this.el = Roo.get(ct);
28242         if(this.cls){
28243             this.el.addClass(this.cls);
28244         }
28245         // using a table allows for vertical alignment
28246         // 100% width is needed by Safari...
28247         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28248         this.tr = this.el.child("tr", true);
28249         var autoId = 0;
28250         this.items = new Roo.util.MixedCollection(false, function(o){
28251             return o.id || ("item" + (++autoId));
28252         });
28253         if(this.buttons){
28254             this.add.apply(this, this.buttons);
28255             delete this.buttons;
28256         }
28257     },
28258
28259     /**
28260      * Adds element(s) to the toolbar -- this function takes a variable number of 
28261      * arguments of mixed type and adds them to the toolbar.
28262      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28263      * <ul>
28264      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28265      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28266      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28267      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28268      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28269      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28270      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28271      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28272      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28273      * </ul>
28274      * @param {Mixed} arg2
28275      * @param {Mixed} etc.
28276      */
28277     add : function(){
28278         var a = arguments, l = a.length;
28279         for(var i = 0; i < l; i++){
28280             this._add(a[i]);
28281         }
28282     },
28283     // private..
28284     _add : function(el) {
28285         
28286         if (el.xtype) {
28287             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28288         }
28289         
28290         if (el.applyTo){ // some kind of form field
28291             return this.addField(el);
28292         } 
28293         if (el.render){ // some kind of Toolbar.Item
28294             return this.addItem(el);
28295         }
28296         if (typeof el == "string"){ // string
28297             if(el == "separator" || el == "-"){
28298                 return this.addSeparator();
28299             }
28300             if (el == " "){
28301                 return this.addSpacer();
28302             }
28303             if(el == "->"){
28304                 return this.addFill();
28305             }
28306             return this.addText(el);
28307             
28308         }
28309         if(el.tagName){ // element
28310             return this.addElement(el);
28311         }
28312         if(typeof el == "object"){ // must be button config?
28313             return this.addButton(el);
28314         }
28315         // and now what?!?!
28316         return false;
28317         
28318     },
28319     
28320     /**
28321      * Add an Xtype element
28322      * @param {Object} xtype Xtype Object
28323      * @return {Object} created Object
28324      */
28325     addxtype : function(e){
28326         return this.add(e);  
28327     },
28328     
28329     /**
28330      * Returns the Element for this toolbar.
28331      * @return {Roo.Element}
28332      */
28333     getEl : function(){
28334         return this.el;  
28335     },
28336     
28337     /**
28338      * Adds a separator
28339      * @return {Roo.Toolbar.Item} The separator item
28340      */
28341     addSeparator : function(){
28342         return this.addItem(new Roo.Toolbar.Separator());
28343     },
28344
28345     /**
28346      * Adds a spacer element
28347      * @return {Roo.Toolbar.Spacer} The spacer item
28348      */
28349     addSpacer : function(){
28350         return this.addItem(new Roo.Toolbar.Spacer());
28351     },
28352
28353     /**
28354      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28355      * @return {Roo.Toolbar.Fill} The fill item
28356      */
28357     addFill : function(){
28358         return this.addItem(new Roo.Toolbar.Fill());
28359     },
28360
28361     /**
28362      * Adds any standard HTML element to the toolbar
28363      * @param {String/HTMLElement/Element} el The element or id of the element to add
28364      * @return {Roo.Toolbar.Item} The element's item
28365      */
28366     addElement : function(el){
28367         return this.addItem(new Roo.Toolbar.Item(el));
28368     },
28369     /**
28370      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28371      * @type Roo.util.MixedCollection  
28372      */
28373     items : false,
28374      
28375     /**
28376      * Adds any Toolbar.Item or subclass
28377      * @param {Roo.Toolbar.Item} item
28378      * @return {Roo.Toolbar.Item} The item
28379      */
28380     addItem : function(item){
28381         var td = this.nextBlock();
28382         item.render(td);
28383         this.items.add(item);
28384         return item;
28385     },
28386     
28387     /**
28388      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28389      * @param {Object/Array} config A button config or array of configs
28390      * @return {Roo.Toolbar.Button/Array}
28391      */
28392     addButton : function(config){
28393         if(config instanceof Array){
28394             var buttons = [];
28395             for(var i = 0, len = config.length; i < len; i++) {
28396                 buttons.push(this.addButton(config[i]));
28397             }
28398             return buttons;
28399         }
28400         var b = config;
28401         if(!(config instanceof Roo.Toolbar.Button)){
28402             b = config.split ?
28403                 new Roo.Toolbar.SplitButton(config) :
28404                 new Roo.Toolbar.Button(config);
28405         }
28406         var td = this.nextBlock();
28407         b.render(td);
28408         this.items.add(b);
28409         return b;
28410     },
28411     
28412     /**
28413      * Adds text to the toolbar
28414      * @param {String} text The text to add
28415      * @return {Roo.Toolbar.Item} The element's item
28416      */
28417     addText : function(text){
28418         return this.addItem(new Roo.Toolbar.TextItem(text));
28419     },
28420     
28421     /**
28422      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28423      * @param {Number} index The index where the item is to be inserted
28424      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28425      * @return {Roo.Toolbar.Button/Item}
28426      */
28427     insertButton : function(index, item){
28428         if(item instanceof Array){
28429             var buttons = [];
28430             for(var i = 0, len = item.length; i < len; i++) {
28431                buttons.push(this.insertButton(index + i, item[i]));
28432             }
28433             return buttons;
28434         }
28435         if (!(item instanceof Roo.Toolbar.Button)){
28436            item = new Roo.Toolbar.Button(item);
28437         }
28438         var td = document.createElement("td");
28439         this.tr.insertBefore(td, this.tr.childNodes[index]);
28440         item.render(td);
28441         this.items.insert(index, item);
28442         return item;
28443     },
28444     
28445     /**
28446      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28447      * @param {Object} config
28448      * @return {Roo.Toolbar.Item} The element's item
28449      */
28450     addDom : function(config, returnEl){
28451         var td = this.nextBlock();
28452         Roo.DomHelper.overwrite(td, config);
28453         var ti = new Roo.Toolbar.Item(td.firstChild);
28454         ti.render(td);
28455         this.items.add(ti);
28456         return ti;
28457     },
28458
28459     /**
28460      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28461      * @type Roo.util.MixedCollection  
28462      */
28463     fields : false,
28464     
28465     /**
28466      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28467      * Note: the field should not have been rendered yet. For a field that has already been
28468      * rendered, use {@link #addElement}.
28469      * @param {Roo.form.Field} field
28470      * @return {Roo.ToolbarItem}
28471      */
28472      
28473       
28474     addField : function(field) {
28475         if (!this.fields) {
28476             var autoId = 0;
28477             this.fields = new Roo.util.MixedCollection(false, function(o){
28478                 return o.id || ("item" + (++autoId));
28479             });
28480
28481         }
28482         
28483         var td = this.nextBlock();
28484         field.render(td);
28485         var ti = new Roo.Toolbar.Item(td.firstChild);
28486         ti.render(td);
28487         this.items.add(ti);
28488         this.fields.add(field);
28489         return ti;
28490     },
28491     /**
28492      * Hide the toolbar
28493      * @method hide
28494      */
28495      
28496       
28497     hide : function()
28498     {
28499         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28500         this.el.child('div').hide();
28501     },
28502     /**
28503      * Show the toolbar
28504      * @method show
28505      */
28506     show : function()
28507     {
28508         this.el.child('div').show();
28509     },
28510       
28511     // private
28512     nextBlock : function(){
28513         var td = document.createElement("td");
28514         this.tr.appendChild(td);
28515         return td;
28516     },
28517
28518     // private
28519     destroy : function(){
28520         if(this.items){ // rendered?
28521             Roo.destroy.apply(Roo, this.items.items);
28522         }
28523         if(this.fields){ // rendered?
28524             Roo.destroy.apply(Roo, this.fields.items);
28525         }
28526         Roo.Element.uncache(this.el, this.tr);
28527     }
28528 };
28529
28530 /**
28531  * @class Roo.Toolbar.Item
28532  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28533  * @constructor
28534  * Creates a new Item
28535  * @param {HTMLElement} el 
28536  */
28537 Roo.Toolbar.Item = function(el){
28538     var cfg = {};
28539     if (typeof (el.xtype) != 'undefined') {
28540         cfg = el;
28541         el = cfg.el;
28542     }
28543     
28544     this.el = Roo.getDom(el);
28545     this.id = Roo.id(this.el);
28546     this.hidden = false;
28547     
28548     this.addEvents({
28549          /**
28550              * @event render
28551              * Fires when the button is rendered
28552              * @param {Button} this
28553              */
28554         'render': true
28555     });
28556     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28557 };
28558 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28559 //Roo.Toolbar.Item.prototype = {
28560     
28561     /**
28562      * Get this item's HTML Element
28563      * @return {HTMLElement}
28564      */
28565     getEl : function(){
28566        return this.el;  
28567     },
28568
28569     // private
28570     render : function(td){
28571         
28572          this.td = td;
28573         td.appendChild(this.el);
28574         
28575         this.fireEvent('render', this);
28576     },
28577     
28578     /**
28579      * Removes and destroys this item.
28580      */
28581     destroy : function(){
28582         this.td.parentNode.removeChild(this.td);
28583     },
28584     
28585     /**
28586      * Shows this item.
28587      */
28588     show: function(){
28589         this.hidden = false;
28590         this.td.style.display = "";
28591     },
28592     
28593     /**
28594      * Hides this item.
28595      */
28596     hide: function(){
28597         this.hidden = true;
28598         this.td.style.display = "none";
28599     },
28600     
28601     /**
28602      * Convenience function for boolean show/hide.
28603      * @param {Boolean} visible true to show/false to hide
28604      */
28605     setVisible: function(visible){
28606         if(visible) {
28607             this.show();
28608         }else{
28609             this.hide();
28610         }
28611     },
28612     
28613     /**
28614      * Try to focus this item.
28615      */
28616     focus : function(){
28617         Roo.fly(this.el).focus();
28618     },
28619     
28620     /**
28621      * Disables this item.
28622      */
28623     disable : function(){
28624         Roo.fly(this.td).addClass("x-item-disabled");
28625         this.disabled = true;
28626         this.el.disabled = true;
28627     },
28628     
28629     /**
28630      * Enables this item.
28631      */
28632     enable : function(){
28633         Roo.fly(this.td).removeClass("x-item-disabled");
28634         this.disabled = false;
28635         this.el.disabled = false;
28636     }
28637 });
28638
28639
28640 /**
28641  * @class Roo.Toolbar.Separator
28642  * @extends Roo.Toolbar.Item
28643  * A simple toolbar separator class
28644  * @constructor
28645  * Creates a new Separator
28646  */
28647 Roo.Toolbar.Separator = function(cfg){
28648     
28649     var s = document.createElement("span");
28650     s.className = "ytb-sep";
28651     if (cfg) {
28652         cfg.el = s;
28653     }
28654     
28655     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28656 };
28657 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28658     enable:Roo.emptyFn,
28659     disable:Roo.emptyFn,
28660     focus:Roo.emptyFn
28661 });
28662
28663 /**
28664  * @class Roo.Toolbar.Spacer
28665  * @extends Roo.Toolbar.Item
28666  * A simple element that adds extra horizontal space to a toolbar.
28667  * @constructor
28668  * Creates a new Spacer
28669  */
28670 Roo.Toolbar.Spacer = function(cfg){
28671     var s = document.createElement("div");
28672     s.className = "ytb-spacer";
28673     if (cfg) {
28674         cfg.el = s;
28675     }
28676     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28677 };
28678 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28679     enable:Roo.emptyFn,
28680     disable:Roo.emptyFn,
28681     focus:Roo.emptyFn
28682 });
28683
28684 /**
28685  * @class Roo.Toolbar.Fill
28686  * @extends Roo.Toolbar.Spacer
28687  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28688  * @constructor
28689  * Creates a new Spacer
28690  */
28691 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28692     // private
28693     render : function(td){
28694         td.style.width = '100%';
28695         Roo.Toolbar.Fill.superclass.render.call(this, td);
28696     }
28697 });
28698
28699 /**
28700  * @class Roo.Toolbar.TextItem
28701  * @extends Roo.Toolbar.Item
28702  * A simple class that renders text directly into a toolbar.
28703  * @constructor
28704  * Creates a new TextItem
28705  * @param {String} text
28706  */
28707 Roo.Toolbar.TextItem = function(cfg){
28708     var  text = cfg || "";
28709     if (typeof(cfg) == 'object') {
28710         text = cfg.text || "";
28711     }  else {
28712         cfg = null;
28713     }
28714     var s = document.createElement("span");
28715     s.className = "ytb-text";
28716     s.innerHTML = text;
28717     if (cfg) {
28718         cfg.el  = s;
28719     }
28720     
28721     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28722 };
28723 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28724     
28725      
28726     enable:Roo.emptyFn,
28727     disable:Roo.emptyFn,
28728     focus:Roo.emptyFn
28729 });
28730
28731 /**
28732  * @class Roo.Toolbar.Button
28733  * @extends Roo.Button
28734  * A button that renders into a toolbar.
28735  * @constructor
28736  * Creates a new Button
28737  * @param {Object} config A standard {@link Roo.Button} config object
28738  */
28739 Roo.Toolbar.Button = function(config){
28740     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28741 };
28742 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28743     render : function(td){
28744         this.td = td;
28745         Roo.Toolbar.Button.superclass.render.call(this, td);
28746     },
28747     
28748     /**
28749      * Removes and destroys this button
28750      */
28751     destroy : function(){
28752         Roo.Toolbar.Button.superclass.destroy.call(this);
28753         this.td.parentNode.removeChild(this.td);
28754     },
28755     
28756     /**
28757      * Shows this button
28758      */
28759     show: function(){
28760         this.hidden = false;
28761         this.td.style.display = "";
28762     },
28763     
28764     /**
28765      * Hides this button
28766      */
28767     hide: function(){
28768         this.hidden = true;
28769         this.td.style.display = "none";
28770     },
28771
28772     /**
28773      * Disables this item
28774      */
28775     disable : function(){
28776         Roo.fly(this.td).addClass("x-item-disabled");
28777         this.disabled = true;
28778     },
28779
28780     /**
28781      * Enables this item
28782      */
28783     enable : function(){
28784         Roo.fly(this.td).removeClass("x-item-disabled");
28785         this.disabled = false;
28786     }
28787 });
28788 // backwards compat
28789 Roo.ToolbarButton = Roo.Toolbar.Button;
28790
28791 /**
28792  * @class Roo.Toolbar.SplitButton
28793  * @extends Roo.SplitButton
28794  * A menu button that renders into a toolbar.
28795  * @constructor
28796  * Creates a new SplitButton
28797  * @param {Object} config A standard {@link Roo.SplitButton} config object
28798  */
28799 Roo.Toolbar.SplitButton = function(config){
28800     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28801 };
28802 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28803     render : function(td){
28804         this.td = td;
28805         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28806     },
28807     
28808     /**
28809      * Removes and destroys this button
28810      */
28811     destroy : function(){
28812         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28813         this.td.parentNode.removeChild(this.td);
28814     },
28815     
28816     /**
28817      * Shows this button
28818      */
28819     show: function(){
28820         this.hidden = false;
28821         this.td.style.display = "";
28822     },
28823     
28824     /**
28825      * Hides this button
28826      */
28827     hide: function(){
28828         this.hidden = true;
28829         this.td.style.display = "none";
28830     }
28831 });
28832
28833 // backwards compat
28834 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28835  * Based on:
28836  * Ext JS Library 1.1.1
28837  * Copyright(c) 2006-2007, Ext JS, LLC.
28838  *
28839  * Originally Released Under LGPL - original licence link has changed is not relivant.
28840  *
28841  * Fork - LGPL
28842  * <script type="text/javascript">
28843  */
28844  
28845 /**
28846  * @class Roo.PagingToolbar
28847  * @extends Roo.Toolbar
28848  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28849  * @constructor
28850  * Create a new PagingToolbar
28851  * @param {Object} config The config object
28852  */
28853 Roo.PagingToolbar = function(el, ds, config)
28854 {
28855     // old args format still supported... - xtype is prefered..
28856     if (typeof(el) == 'object' && el.xtype) {
28857         // created from xtype...
28858         config = el;
28859         ds = el.dataSource;
28860         el = config.container;
28861     }
28862     var items = [];
28863     if (config.items) {
28864         items = config.items;
28865         config.items = [];
28866     }
28867     
28868     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28869     this.ds = ds;
28870     this.cursor = 0;
28871     this.renderButtons(this.el);
28872     this.bind(ds);
28873     
28874     // supprot items array.
28875    
28876     Roo.each(items, function(e) {
28877         this.add(Roo.factory(e));
28878     },this);
28879     
28880 };
28881
28882 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28883     /**
28884      * @cfg {Roo.data.Store} dataSource
28885      * The underlying data store providing the paged data
28886      */
28887     /**
28888      * @cfg {String/HTMLElement/Element} container
28889      * container The id or element that will contain the toolbar
28890      */
28891     /**
28892      * @cfg {Boolean} displayInfo
28893      * True to display the displayMsg (defaults to false)
28894      */
28895     /**
28896      * @cfg {Number} pageSize
28897      * The number of records to display per page (defaults to 20)
28898      */
28899     pageSize: 20,
28900     /**
28901      * @cfg {String} displayMsg
28902      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28903      */
28904     displayMsg : 'Displaying {0} - {1} of {2}',
28905     /**
28906      * @cfg {String} emptyMsg
28907      * The message to display when no records are found (defaults to "No data to display")
28908      */
28909     emptyMsg : 'No data to display',
28910     /**
28911      * Customizable piece of the default paging text (defaults to "Page")
28912      * @type String
28913      */
28914     beforePageText : "Page",
28915     /**
28916      * Customizable piece of the default paging text (defaults to "of %0")
28917      * @type String
28918      */
28919     afterPageText : "of {0}",
28920     /**
28921      * Customizable piece of the default paging text (defaults to "First Page")
28922      * @type String
28923      */
28924     firstText : "First Page",
28925     /**
28926      * Customizable piece of the default paging text (defaults to "Previous Page")
28927      * @type String
28928      */
28929     prevText : "Previous Page",
28930     /**
28931      * Customizable piece of the default paging text (defaults to "Next Page")
28932      * @type String
28933      */
28934     nextText : "Next Page",
28935     /**
28936      * Customizable piece of the default paging text (defaults to "Last Page")
28937      * @type String
28938      */
28939     lastText : "Last Page",
28940     /**
28941      * Customizable piece of the default paging text (defaults to "Refresh")
28942      * @type String
28943      */
28944     refreshText : "Refresh",
28945
28946     // private
28947     renderButtons : function(el){
28948         Roo.PagingToolbar.superclass.render.call(this, el);
28949         this.first = this.addButton({
28950             tooltip: this.firstText,
28951             cls: "x-btn-icon x-grid-page-first",
28952             disabled: true,
28953             handler: this.onClick.createDelegate(this, ["first"])
28954         });
28955         this.prev = this.addButton({
28956             tooltip: this.prevText,
28957             cls: "x-btn-icon x-grid-page-prev",
28958             disabled: true,
28959             handler: this.onClick.createDelegate(this, ["prev"])
28960         });
28961         //this.addSeparator();
28962         this.add(this.beforePageText);
28963         this.field = Roo.get(this.addDom({
28964            tag: "input",
28965            type: "text",
28966            size: "3",
28967            value: "1",
28968            cls: "x-grid-page-number"
28969         }).el);
28970         this.field.on("keydown", this.onPagingKeydown, this);
28971         this.field.on("focus", function(){this.dom.select();});
28972         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28973         this.field.setHeight(18);
28974         //this.addSeparator();
28975         this.next = this.addButton({
28976             tooltip: this.nextText,
28977             cls: "x-btn-icon x-grid-page-next",
28978             disabled: true,
28979             handler: this.onClick.createDelegate(this, ["next"])
28980         });
28981         this.last = this.addButton({
28982             tooltip: this.lastText,
28983             cls: "x-btn-icon x-grid-page-last",
28984             disabled: true,
28985             handler: this.onClick.createDelegate(this, ["last"])
28986         });
28987         //this.addSeparator();
28988         this.loading = this.addButton({
28989             tooltip: this.refreshText,
28990             cls: "x-btn-icon x-grid-loading",
28991             handler: this.onClick.createDelegate(this, ["refresh"])
28992         });
28993
28994         if(this.displayInfo){
28995             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28996         }
28997     },
28998
28999     // private
29000     updateInfo : function(){
29001         if(this.displayEl){
29002             var count = this.ds.getCount();
29003             var msg = count == 0 ?
29004                 this.emptyMsg :
29005                 String.format(
29006                     this.displayMsg,
29007                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29008                 );
29009             this.displayEl.update(msg);
29010         }
29011     },
29012
29013     // private
29014     onLoad : function(ds, r, o){
29015        this.cursor = o.params ? o.params.start : 0;
29016        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29017
29018        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29019        this.field.dom.value = ap;
29020        this.first.setDisabled(ap == 1);
29021        this.prev.setDisabled(ap == 1);
29022        this.next.setDisabled(ap == ps);
29023        this.last.setDisabled(ap == ps);
29024        this.loading.enable();
29025        this.updateInfo();
29026     },
29027
29028     // private
29029     getPageData : function(){
29030         var total = this.ds.getTotalCount();
29031         return {
29032             total : total,
29033             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29034             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29035         };
29036     },
29037
29038     // private
29039     onLoadError : function(){
29040         this.loading.enable();
29041     },
29042
29043     // private
29044     onPagingKeydown : function(e){
29045         var k = e.getKey();
29046         var d = this.getPageData();
29047         if(k == e.RETURN){
29048             var v = this.field.dom.value, pageNum;
29049             if(!v || isNaN(pageNum = parseInt(v, 10))){
29050                 this.field.dom.value = d.activePage;
29051                 return;
29052             }
29053             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29054             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29055             e.stopEvent();
29056         }
29057         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
29058         {
29059           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29060           this.field.dom.value = pageNum;
29061           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29062           e.stopEvent();
29063         }
29064         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29065         {
29066           var v = this.field.dom.value, pageNum; 
29067           var increment = (e.shiftKey) ? 10 : 1;
29068           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29069             increment *= -1;
29070           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29071             this.field.dom.value = d.activePage;
29072             return;
29073           }
29074           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29075           {
29076             this.field.dom.value = parseInt(v, 10) + increment;
29077             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29078             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29079           }
29080           e.stopEvent();
29081         }
29082     },
29083
29084     // private
29085     beforeLoad : function(){
29086         if(this.loading){
29087             this.loading.disable();
29088         }
29089     },
29090
29091     // private
29092     onClick : function(which){
29093         var ds = this.ds;
29094         switch(which){
29095             case "first":
29096                 ds.load({params:{start: 0, limit: this.pageSize}});
29097             break;
29098             case "prev":
29099                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29100             break;
29101             case "next":
29102                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29103             break;
29104             case "last":
29105                 var total = ds.getTotalCount();
29106                 var extra = total % this.pageSize;
29107                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29108                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29109             break;
29110             case "refresh":
29111                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29112             break;
29113         }
29114     },
29115
29116     /**
29117      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29118      * @param {Roo.data.Store} store The data store to unbind
29119      */
29120     unbind : function(ds){
29121         ds.un("beforeload", this.beforeLoad, this);
29122         ds.un("load", this.onLoad, this);
29123         ds.un("loadexception", this.onLoadError, this);
29124         ds.un("remove", this.updateInfo, this);
29125         ds.un("add", this.updateInfo, this);
29126         this.ds = undefined;
29127     },
29128
29129     /**
29130      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29131      * @param {Roo.data.Store} store The data store to bind
29132      */
29133     bind : function(ds){
29134         ds.on("beforeload", this.beforeLoad, this);
29135         ds.on("load", this.onLoad, this);
29136         ds.on("loadexception", this.onLoadError, this);
29137         ds.on("remove", this.updateInfo, this);
29138         ds.on("add", this.updateInfo, this);
29139         this.ds = ds;
29140     }
29141 });/*
29142  * Based on:
29143  * Ext JS Library 1.1.1
29144  * Copyright(c) 2006-2007, Ext JS, LLC.
29145  *
29146  * Originally Released Under LGPL - original licence link has changed is not relivant.
29147  *
29148  * Fork - LGPL
29149  * <script type="text/javascript">
29150  */
29151
29152 /**
29153  * @class Roo.Resizable
29154  * @extends Roo.util.Observable
29155  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29156  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29157  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
29158  * the element will be wrapped for you automatically.</p>
29159  * <p>Here is the list of valid resize handles:</p>
29160  * <pre>
29161 Value   Description
29162 ------  -------------------
29163  'n'     north
29164  's'     south
29165  'e'     east
29166  'w'     west
29167  'nw'    northwest
29168  'sw'    southwest
29169  'se'    southeast
29170  'ne'    northeast
29171  'hd'    horizontal drag
29172  'all'   all
29173 </pre>
29174  * <p>Here's an example showing the creation of a typical Resizable:</p>
29175  * <pre><code>
29176 var resizer = new Roo.Resizable("element-id", {
29177     handles: 'all',
29178     minWidth: 200,
29179     minHeight: 100,
29180     maxWidth: 500,
29181     maxHeight: 400,
29182     pinned: true
29183 });
29184 resizer.on("resize", myHandler);
29185 </code></pre>
29186  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29187  * resizer.east.setDisplayed(false);</p>
29188  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29189  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29190  * resize operation's new size (defaults to [0, 0])
29191  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29192  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29193  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29194  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29195  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29196  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29197  * @cfg {Number} width The width of the element in pixels (defaults to null)
29198  * @cfg {Number} height The height of the element in pixels (defaults to null)
29199  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29200  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29201  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29202  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29203  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29204  * in favor of the handles config option (defaults to false)
29205  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29206  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29207  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29208  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29209  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29210  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29211  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29212  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29213  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29214  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29215  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29216  * @constructor
29217  * Create a new resizable component
29218  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29219  * @param {Object} config configuration options
29220   */
29221 Roo.Resizable = function(el, config)
29222 {
29223     this.el = Roo.get(el);
29224
29225     if(config && config.wrap){
29226         config.resizeChild = this.el;
29227         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29228         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29229         this.el.setStyle("overflow", "hidden");
29230         this.el.setPositioning(config.resizeChild.getPositioning());
29231         config.resizeChild.clearPositioning();
29232         if(!config.width || !config.height){
29233             var csize = config.resizeChild.getSize();
29234             this.el.setSize(csize.width, csize.height);
29235         }
29236         if(config.pinned && !config.adjustments){
29237             config.adjustments = "auto";
29238         }
29239     }
29240
29241     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29242     this.proxy.unselectable();
29243     this.proxy.enableDisplayMode('block');
29244
29245     Roo.apply(this, config);
29246
29247     if(this.pinned){
29248         this.disableTrackOver = true;
29249         this.el.addClass("x-resizable-pinned");
29250     }
29251     // if the element isn't positioned, make it relative
29252     var position = this.el.getStyle("position");
29253     if(position != "absolute" && position != "fixed"){
29254         this.el.setStyle("position", "relative");
29255     }
29256     if(!this.handles){ // no handles passed, must be legacy style
29257         this.handles = 's,e,se';
29258         if(this.multiDirectional){
29259             this.handles += ',n,w';
29260         }
29261     }
29262     if(this.handles == "all"){
29263         this.handles = "n s e w ne nw se sw";
29264     }
29265     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29266     var ps = Roo.Resizable.positions;
29267     for(var i = 0, len = hs.length; i < len; i++){
29268         if(hs[i] && ps[hs[i]]){
29269             var pos = ps[hs[i]];
29270             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29271         }
29272     }
29273     // legacy
29274     this.corner = this.southeast;
29275     
29276     // updateBox = the box can move..
29277     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29278         this.updateBox = true;
29279     }
29280
29281     this.activeHandle = null;
29282
29283     if(this.resizeChild){
29284         if(typeof this.resizeChild == "boolean"){
29285             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29286         }else{
29287             this.resizeChild = Roo.get(this.resizeChild, true);
29288         }
29289     }
29290     
29291     if(this.adjustments == "auto"){
29292         var rc = this.resizeChild;
29293         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29294         if(rc && (hw || hn)){
29295             rc.position("relative");
29296             rc.setLeft(hw ? hw.el.getWidth() : 0);
29297             rc.setTop(hn ? hn.el.getHeight() : 0);
29298         }
29299         this.adjustments = [
29300             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29301             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29302         ];
29303     }
29304
29305     if(this.draggable){
29306         this.dd = this.dynamic ?
29307             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29308         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29309     }
29310
29311     // public events
29312     this.addEvents({
29313         /**
29314          * @event beforeresize
29315          * Fired before resize is allowed. Set enabled to false to cancel resize.
29316          * @param {Roo.Resizable} this
29317          * @param {Roo.EventObject} e The mousedown event
29318          */
29319         "beforeresize" : true,
29320         /**
29321          * @event resizing
29322          * Fired a resizing.
29323          * @param {Roo.Resizable} this
29324          * @param {Number} x The new x position
29325          * @param {Number} y The new y position
29326          * @param {Number} w The new w width
29327          * @param {Number} h The new h hight
29328          * @param {Roo.EventObject} e The mouseup event
29329          */
29330         "resizing" : true,
29331         /**
29332          * @event resize
29333          * Fired after a resize.
29334          * @param {Roo.Resizable} this
29335          * @param {Number} width The new width
29336          * @param {Number} height The new height
29337          * @param {Roo.EventObject} e The mouseup event
29338          */
29339         "resize" : true
29340     });
29341
29342     if(this.width !== null && this.height !== null){
29343         this.resizeTo(this.width, this.height);
29344     }else{
29345         this.updateChildSize();
29346     }
29347     if(Roo.isIE){
29348         this.el.dom.style.zoom = 1;
29349     }
29350     Roo.Resizable.superclass.constructor.call(this);
29351 };
29352
29353 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29354         resizeChild : false,
29355         adjustments : [0, 0],
29356         minWidth : 5,
29357         minHeight : 5,
29358         maxWidth : 10000,
29359         maxHeight : 10000,
29360         enabled : true,
29361         animate : false,
29362         duration : .35,
29363         dynamic : false,
29364         handles : false,
29365         multiDirectional : false,
29366         disableTrackOver : false,
29367         easing : 'easeOutStrong',
29368         widthIncrement : 0,
29369         heightIncrement : 0,
29370         pinned : false,
29371         width : null,
29372         height : null,
29373         preserveRatio : false,
29374         transparent: false,
29375         minX: 0,
29376         minY: 0,
29377         draggable: false,
29378
29379         /**
29380          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29381          */
29382         constrainTo: undefined,
29383         /**
29384          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29385          */
29386         resizeRegion: undefined,
29387
29388
29389     /**
29390      * Perform a manual resize
29391      * @param {Number} width
29392      * @param {Number} height
29393      */
29394     resizeTo : function(width, height){
29395         this.el.setSize(width, height);
29396         this.updateChildSize();
29397         this.fireEvent("resize", this, width, height, null);
29398     },
29399
29400     // private
29401     startSizing : function(e, handle){
29402         this.fireEvent("beforeresize", this, e);
29403         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29404
29405             if(!this.overlay){
29406                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29407                 this.overlay.unselectable();
29408                 this.overlay.enableDisplayMode("block");
29409                 this.overlay.on("mousemove", this.onMouseMove, this);
29410                 this.overlay.on("mouseup", this.onMouseUp, this);
29411             }
29412             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29413
29414             this.resizing = true;
29415             this.startBox = this.el.getBox();
29416             this.startPoint = e.getXY();
29417             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29418                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29419
29420             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29421             this.overlay.show();
29422
29423             if(this.constrainTo) {
29424                 var ct = Roo.get(this.constrainTo);
29425                 this.resizeRegion = ct.getRegion().adjust(
29426                     ct.getFrameWidth('t'),
29427                     ct.getFrameWidth('l'),
29428                     -ct.getFrameWidth('b'),
29429                     -ct.getFrameWidth('r')
29430                 );
29431             }
29432
29433             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29434             this.proxy.show();
29435             this.proxy.setBox(this.startBox);
29436             if(!this.dynamic){
29437                 this.proxy.setStyle('visibility', 'visible');
29438             }
29439         }
29440     },
29441
29442     // private
29443     onMouseDown : function(handle, e){
29444         if(this.enabled){
29445             e.stopEvent();
29446             this.activeHandle = handle;
29447             this.startSizing(e, handle);
29448         }
29449     },
29450
29451     // private
29452     onMouseUp : function(e){
29453         var size = this.resizeElement();
29454         this.resizing = false;
29455         this.handleOut();
29456         this.overlay.hide();
29457         this.proxy.hide();
29458         this.fireEvent("resize", this, size.width, size.height, e);
29459     },
29460
29461     // private
29462     updateChildSize : function(){
29463         
29464         if(this.resizeChild){
29465             var el = this.el;
29466             var child = this.resizeChild;
29467             var adj = this.adjustments;
29468             if(el.dom.offsetWidth){
29469                 var b = el.getSize(true);
29470                 child.setSize(b.width+adj[0], b.height+adj[1]);
29471             }
29472             // Second call here for IE
29473             // The first call enables instant resizing and
29474             // the second call corrects scroll bars if they
29475             // exist
29476             if(Roo.isIE){
29477                 setTimeout(function(){
29478                     if(el.dom.offsetWidth){
29479                         var b = el.getSize(true);
29480                         child.setSize(b.width+adj[0], b.height+adj[1]);
29481                     }
29482                 }, 10);
29483             }
29484         }
29485     },
29486
29487     // private
29488     snap : function(value, inc, min){
29489         if(!inc || !value) return value;
29490         var newValue = value;
29491         var m = value % inc;
29492         if(m > 0){
29493             if(m > (inc/2)){
29494                 newValue = value + (inc-m);
29495             }else{
29496                 newValue = value - m;
29497             }
29498         }
29499         return Math.max(min, newValue);
29500     },
29501
29502     // private
29503     resizeElement : function(){
29504         var box = this.proxy.getBox();
29505         if(this.updateBox){
29506             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29507         }else{
29508             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29509         }
29510         this.updateChildSize();
29511         if(!this.dynamic){
29512             this.proxy.hide();
29513         }
29514         return box;
29515     },
29516
29517     // private
29518     constrain : function(v, diff, m, mx){
29519         if(v - diff < m){
29520             diff = v - m;
29521         }else if(v - diff > mx){
29522             diff = mx - v;
29523         }
29524         return diff;
29525     },
29526
29527     // private
29528     onMouseMove : function(e){
29529         
29530         if(this.enabled){
29531             try{// try catch so if something goes wrong the user doesn't get hung
29532
29533             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29534                 return;
29535             }
29536
29537             //var curXY = this.startPoint;
29538             var curSize = this.curSize || this.startBox;
29539             var x = this.startBox.x, y = this.startBox.y;
29540             var ox = x, oy = y;
29541             var w = curSize.width, h = curSize.height;
29542             var ow = w, oh = h;
29543             var mw = this.minWidth, mh = this.minHeight;
29544             var mxw = this.maxWidth, mxh = this.maxHeight;
29545             var wi = this.widthIncrement;
29546             var hi = this.heightIncrement;
29547
29548             var eventXY = e.getXY();
29549             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29550             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29551
29552             var pos = this.activeHandle.position;
29553
29554             switch(pos){
29555                 case "east":
29556                     w += diffX;
29557                     w = Math.min(Math.max(mw, w), mxw);
29558                     break;
29559              
29560                 case "south":
29561                     h += diffY;
29562                     h = Math.min(Math.max(mh, h), mxh);
29563                     break;
29564                 case "southeast":
29565                     w += diffX;
29566                     h += diffY;
29567                     w = Math.min(Math.max(mw, w), mxw);
29568                     h = Math.min(Math.max(mh, h), mxh);
29569                     break;
29570                 case "north":
29571                     diffY = this.constrain(h, diffY, mh, mxh);
29572                     y += diffY;
29573                     h -= diffY;
29574                     break;
29575                 case "hdrag":
29576                     
29577                     if (wi) {
29578                         var adiffX = Math.abs(diffX);
29579                         var sub = (adiffX % wi); // how much 
29580                         if (sub > (wi/2)) { // far enough to snap
29581                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29582                         } else {
29583                             // remove difference.. 
29584                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29585                         }
29586                     }
29587                     x += diffX;
29588                     x = Math.max(this.minX, x);
29589                     break;
29590                 case "west":
29591                     diffX = this.constrain(w, diffX, mw, mxw);
29592                     x += diffX;
29593                     w -= diffX;
29594                     break;
29595                 case "northeast":
29596                     w += diffX;
29597                     w = Math.min(Math.max(mw, w), mxw);
29598                     diffY = this.constrain(h, diffY, mh, mxh);
29599                     y += diffY;
29600                     h -= diffY;
29601                     break;
29602                 case "northwest":
29603                     diffX = this.constrain(w, diffX, mw, mxw);
29604                     diffY = this.constrain(h, diffY, mh, mxh);
29605                     y += diffY;
29606                     h -= diffY;
29607                     x += diffX;
29608                     w -= diffX;
29609                     break;
29610                case "southwest":
29611                     diffX = this.constrain(w, diffX, mw, mxw);
29612                     h += diffY;
29613                     h = Math.min(Math.max(mh, h), mxh);
29614                     x += diffX;
29615                     w -= diffX;
29616                     break;
29617             }
29618
29619             var sw = this.snap(w, wi, mw);
29620             var sh = this.snap(h, hi, mh);
29621             if(sw != w || sh != h){
29622                 switch(pos){
29623                     case "northeast":
29624                         y -= sh - h;
29625                     break;
29626                     case "north":
29627                         y -= sh - h;
29628                         break;
29629                     case "southwest":
29630                         x -= sw - w;
29631                     break;
29632                     case "west":
29633                         x -= sw - w;
29634                         break;
29635                     case "northwest":
29636                         x -= sw - w;
29637                         y -= sh - h;
29638                     break;
29639                 }
29640                 w = sw;
29641                 h = sh;
29642             }
29643
29644             if(this.preserveRatio){
29645                 switch(pos){
29646                     case "southeast":
29647                     case "east":
29648                         h = oh * (w/ow);
29649                         h = Math.min(Math.max(mh, h), mxh);
29650                         w = ow * (h/oh);
29651                        break;
29652                     case "south":
29653                         w = ow * (h/oh);
29654                         w = Math.min(Math.max(mw, w), mxw);
29655                         h = oh * (w/ow);
29656                         break;
29657                     case "northeast":
29658                         w = ow * (h/oh);
29659                         w = Math.min(Math.max(mw, w), mxw);
29660                         h = oh * (w/ow);
29661                     break;
29662                     case "north":
29663                         var tw = w;
29664                         w = ow * (h/oh);
29665                         w = Math.min(Math.max(mw, w), mxw);
29666                         h = oh * (w/ow);
29667                         x += (tw - w) / 2;
29668                         break;
29669                     case "southwest":
29670                         h = oh * (w/ow);
29671                         h = Math.min(Math.max(mh, h), mxh);
29672                         var tw = w;
29673                         w = ow * (h/oh);
29674                         x += tw - w;
29675                         break;
29676                     case "west":
29677                         var th = h;
29678                         h = oh * (w/ow);
29679                         h = Math.min(Math.max(mh, h), mxh);
29680                         y += (th - h) / 2;
29681                         var tw = w;
29682                         w = ow * (h/oh);
29683                         x += tw - w;
29684                        break;
29685                     case "northwest":
29686                         var tw = w;
29687                         var th = h;
29688                         h = oh * (w/ow);
29689                         h = Math.min(Math.max(mh, h), mxh);
29690                         w = ow * (h/oh);
29691                         y += th - h;
29692                         x += tw - w;
29693                        break;
29694
29695                 }
29696             }
29697             if (pos == 'hdrag') {
29698                 w = ow;
29699             }
29700             this.proxy.setBounds(x, y, w, h);
29701             if(this.dynamic){
29702                 this.resizeElement();
29703             }
29704             }catch(e){}
29705         }
29706         this.fireEvent("resizing", this, x, y, w, h, e);
29707     },
29708
29709     // private
29710     handleOver : function(){
29711         if(this.enabled){
29712             this.el.addClass("x-resizable-over");
29713         }
29714     },
29715
29716     // private
29717     handleOut : function(){
29718         if(!this.resizing){
29719             this.el.removeClass("x-resizable-over");
29720         }
29721     },
29722
29723     /**
29724      * Returns the element this component is bound to.
29725      * @return {Roo.Element}
29726      */
29727     getEl : function(){
29728         return this.el;
29729     },
29730
29731     /**
29732      * Returns the resizeChild element (or null).
29733      * @return {Roo.Element}
29734      */
29735     getResizeChild : function(){
29736         return this.resizeChild;
29737     },
29738     groupHandler : function()
29739     {
29740         
29741     },
29742     /**
29743      * Destroys this resizable. If the element was wrapped and
29744      * removeEl is not true then the element remains.
29745      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29746      */
29747     destroy : function(removeEl){
29748         this.proxy.remove();
29749         if(this.overlay){
29750             this.overlay.removeAllListeners();
29751             this.overlay.remove();
29752         }
29753         var ps = Roo.Resizable.positions;
29754         for(var k in ps){
29755             if(typeof ps[k] != "function" && this[ps[k]]){
29756                 var h = this[ps[k]];
29757                 h.el.removeAllListeners();
29758                 h.el.remove();
29759             }
29760         }
29761         if(removeEl){
29762             this.el.update("");
29763             this.el.remove();
29764         }
29765     }
29766 });
29767
29768 // private
29769 // hash to map config positions to true positions
29770 Roo.Resizable.positions = {
29771     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29772     hd: "hdrag"
29773 };
29774
29775 // private
29776 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29777     if(!this.tpl){
29778         // only initialize the template if resizable is used
29779         var tpl = Roo.DomHelper.createTemplate(
29780             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29781         );
29782         tpl.compile();
29783         Roo.Resizable.Handle.prototype.tpl = tpl;
29784     }
29785     this.position = pos;
29786     this.rz = rz;
29787     // show north drag fro topdra
29788     var handlepos = pos == 'hdrag' ? 'north' : pos;
29789     
29790     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29791     if (pos == 'hdrag') {
29792         this.el.setStyle('cursor', 'pointer');
29793     }
29794     this.el.unselectable();
29795     if(transparent){
29796         this.el.setOpacity(0);
29797     }
29798     this.el.on("mousedown", this.onMouseDown, this);
29799     if(!disableTrackOver){
29800         this.el.on("mouseover", this.onMouseOver, this);
29801         this.el.on("mouseout", this.onMouseOut, this);
29802     }
29803 };
29804
29805 // private
29806 Roo.Resizable.Handle.prototype = {
29807     afterResize : function(rz){
29808         Roo.log('after?');
29809         // do nothing
29810     },
29811     // private
29812     onMouseDown : function(e){
29813         this.rz.onMouseDown(this, e);
29814     },
29815     // private
29816     onMouseOver : function(e){
29817         this.rz.handleOver(this, e);
29818     },
29819     // private
29820     onMouseOut : function(e){
29821         this.rz.handleOut(this, e);
29822     }
29823 };/*
29824  * Based on:
29825  * Ext JS Library 1.1.1
29826  * Copyright(c) 2006-2007, Ext JS, LLC.
29827  *
29828  * Originally Released Under LGPL - original licence link has changed is not relivant.
29829  *
29830  * Fork - LGPL
29831  * <script type="text/javascript">
29832  */
29833
29834 /**
29835  * @class Roo.Editor
29836  * @extends Roo.Component
29837  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29838  * @constructor
29839  * Create a new Editor
29840  * @param {Roo.form.Field} field The Field object (or descendant)
29841  * @param {Object} config The config object
29842  */
29843 Roo.Editor = function(field, config){
29844     Roo.Editor.superclass.constructor.call(this, config);
29845     this.field = field;
29846     this.addEvents({
29847         /**
29848              * @event beforestartedit
29849              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29850              * false from the handler of this event.
29851              * @param {Editor} this
29852              * @param {Roo.Element} boundEl The underlying element bound to this editor
29853              * @param {Mixed} value The field value being set
29854              */
29855         "beforestartedit" : true,
29856         /**
29857              * @event startedit
29858              * Fires when this editor is displayed
29859              * @param {Roo.Element} boundEl The underlying element bound to this editor
29860              * @param {Mixed} value The starting field value
29861              */
29862         "startedit" : true,
29863         /**
29864              * @event beforecomplete
29865              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29866              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29867              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29868              * event will not fire since no edit actually occurred.
29869              * @param {Editor} this
29870              * @param {Mixed} value The current field value
29871              * @param {Mixed} startValue The original field value
29872              */
29873         "beforecomplete" : true,
29874         /**
29875              * @event complete
29876              * Fires after editing is complete and any changed value has been written to the underlying field.
29877              * @param {Editor} this
29878              * @param {Mixed} value The current field value
29879              * @param {Mixed} startValue The original field value
29880              */
29881         "complete" : true,
29882         /**
29883          * @event specialkey
29884          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29885          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29886          * @param {Roo.form.Field} this
29887          * @param {Roo.EventObject} e The event object
29888          */
29889         "specialkey" : true
29890     });
29891 };
29892
29893 Roo.extend(Roo.Editor, Roo.Component, {
29894     /**
29895      * @cfg {Boolean/String} autosize
29896      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29897      * or "height" to adopt the height only (defaults to false)
29898      */
29899     /**
29900      * @cfg {Boolean} revertInvalid
29901      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29902      * validation fails (defaults to true)
29903      */
29904     /**
29905      * @cfg {Boolean} ignoreNoChange
29906      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29907      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29908      * will never be ignored.
29909      */
29910     /**
29911      * @cfg {Boolean} hideEl
29912      * False to keep the bound element visible while the editor is displayed (defaults to true)
29913      */
29914     /**
29915      * @cfg {Mixed} value
29916      * The data value of the underlying field (defaults to "")
29917      */
29918     value : "",
29919     /**
29920      * @cfg {String} alignment
29921      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29922      */
29923     alignment: "c-c?",
29924     /**
29925      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29926      * for bottom-right shadow (defaults to "frame")
29927      */
29928     shadow : "frame",
29929     /**
29930      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29931      */
29932     constrain : false,
29933     /**
29934      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29935      */
29936     completeOnEnter : false,
29937     /**
29938      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29939      */
29940     cancelOnEsc : false,
29941     /**
29942      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29943      */
29944     updateEl : false,
29945
29946     // private
29947     onRender : function(ct, position){
29948         this.el = new Roo.Layer({
29949             shadow: this.shadow,
29950             cls: "x-editor",
29951             parentEl : ct,
29952             shim : this.shim,
29953             shadowOffset:4,
29954             id: this.id,
29955             constrain: this.constrain
29956         });
29957         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29958         if(this.field.msgTarget != 'title'){
29959             this.field.msgTarget = 'qtip';
29960         }
29961         this.field.render(this.el);
29962         if(Roo.isGecko){
29963             this.field.el.dom.setAttribute('autocomplete', 'off');
29964         }
29965         this.field.on("specialkey", this.onSpecialKey, this);
29966         if(this.swallowKeys){
29967             this.field.el.swallowEvent(['keydown','keypress']);
29968         }
29969         this.field.show();
29970         this.field.on("blur", this.onBlur, this);
29971         if(this.field.grow){
29972             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29973         }
29974     },
29975
29976     onSpecialKey : function(field, e)
29977     {
29978         //Roo.log('editor onSpecialKey');
29979         if(this.completeOnEnter && e.getKey() == e.ENTER){
29980             e.stopEvent();
29981             this.completeEdit();
29982             return;
29983         }
29984         // do not fire special key otherwise it might hide close the editor...
29985         if(e.getKey() == e.ENTER){    
29986             return;
29987         }
29988         if(this.cancelOnEsc && e.getKey() == e.ESC){
29989             this.cancelEdit();
29990             return;
29991         } 
29992         this.fireEvent('specialkey', field, e);
29993     
29994     },
29995
29996     /**
29997      * Starts the editing process and shows the editor.
29998      * @param {String/HTMLElement/Element} el The element to edit
29999      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30000       * to the innerHTML of el.
30001      */
30002     startEdit : function(el, value){
30003         if(this.editing){
30004             this.completeEdit();
30005         }
30006         this.boundEl = Roo.get(el);
30007         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30008         if(!this.rendered){
30009             this.render(this.parentEl || document.body);
30010         }
30011         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30012             return;
30013         }
30014         this.startValue = v;
30015         this.field.setValue(v);
30016         if(this.autoSize){
30017             var sz = this.boundEl.getSize();
30018             switch(this.autoSize){
30019                 case "width":
30020                 this.setSize(sz.width,  "");
30021                 break;
30022                 case "height":
30023                 this.setSize("",  sz.height);
30024                 break;
30025                 default:
30026                 this.setSize(sz.width,  sz.height);
30027             }
30028         }
30029         this.el.alignTo(this.boundEl, this.alignment);
30030         this.editing = true;
30031         if(Roo.QuickTips){
30032             Roo.QuickTips.disable();
30033         }
30034         this.show();
30035     },
30036
30037     /**
30038      * Sets the height and width of this editor.
30039      * @param {Number} width The new width
30040      * @param {Number} height The new height
30041      */
30042     setSize : function(w, h){
30043         this.field.setSize(w, h);
30044         if(this.el){
30045             this.el.sync();
30046         }
30047     },
30048
30049     /**
30050      * Realigns the editor to the bound field based on the current alignment config value.
30051      */
30052     realign : function(){
30053         this.el.alignTo(this.boundEl, this.alignment);
30054     },
30055
30056     /**
30057      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30058      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30059      */
30060     completeEdit : function(remainVisible){
30061         if(!this.editing){
30062             return;
30063         }
30064         var v = this.getValue();
30065         if(this.revertInvalid !== false && !this.field.isValid()){
30066             v = this.startValue;
30067             this.cancelEdit(true);
30068         }
30069         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30070             this.editing = false;
30071             this.hide();
30072             return;
30073         }
30074         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30075             this.editing = false;
30076             if(this.updateEl && this.boundEl){
30077                 this.boundEl.update(v);
30078             }
30079             if(remainVisible !== true){
30080                 this.hide();
30081             }
30082             this.fireEvent("complete", this, v, this.startValue);
30083         }
30084     },
30085
30086     // private
30087     onShow : function(){
30088         this.el.show();
30089         if(this.hideEl !== false){
30090             this.boundEl.hide();
30091         }
30092         this.field.show();
30093         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30094             this.fixIEFocus = true;
30095             this.deferredFocus.defer(50, this);
30096         }else{
30097             this.field.focus();
30098         }
30099         this.fireEvent("startedit", this.boundEl, this.startValue);
30100     },
30101
30102     deferredFocus : function(){
30103         if(this.editing){
30104             this.field.focus();
30105         }
30106     },
30107
30108     /**
30109      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30110      * reverted to the original starting value.
30111      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30112      * cancel (defaults to false)
30113      */
30114     cancelEdit : function(remainVisible){
30115         if(this.editing){
30116             this.setValue(this.startValue);
30117             if(remainVisible !== true){
30118                 this.hide();
30119             }
30120         }
30121     },
30122
30123     // private
30124     onBlur : function(){
30125         if(this.allowBlur !== true && this.editing){
30126             this.completeEdit();
30127         }
30128     },
30129
30130     // private
30131     onHide : function(){
30132         if(this.editing){
30133             this.completeEdit();
30134             return;
30135         }
30136         this.field.blur();
30137         if(this.field.collapse){
30138             this.field.collapse();
30139         }
30140         this.el.hide();
30141         if(this.hideEl !== false){
30142             this.boundEl.show();
30143         }
30144         if(Roo.QuickTips){
30145             Roo.QuickTips.enable();
30146         }
30147     },
30148
30149     /**
30150      * Sets the data value of the editor
30151      * @param {Mixed} value Any valid value supported by the underlying field
30152      */
30153     setValue : function(v){
30154         this.field.setValue(v);
30155     },
30156
30157     /**
30158      * Gets the data value of the editor
30159      * @return {Mixed} The data value
30160      */
30161     getValue : function(){
30162         return this.field.getValue();
30163     }
30164 });/*
30165  * Based on:
30166  * Ext JS Library 1.1.1
30167  * Copyright(c) 2006-2007, Ext JS, LLC.
30168  *
30169  * Originally Released Under LGPL - original licence link has changed is not relivant.
30170  *
30171  * Fork - LGPL
30172  * <script type="text/javascript">
30173  */
30174  
30175 /**
30176  * @class Roo.BasicDialog
30177  * @extends Roo.util.Observable
30178  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30179  * <pre><code>
30180 var dlg = new Roo.BasicDialog("my-dlg", {
30181     height: 200,
30182     width: 300,
30183     minHeight: 100,
30184     minWidth: 150,
30185     modal: true,
30186     proxyDrag: true,
30187     shadow: true
30188 });
30189 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30190 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30191 dlg.addButton('Cancel', dlg.hide, dlg);
30192 dlg.show();
30193 </code></pre>
30194   <b>A Dialog should always be a direct child of the body element.</b>
30195  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30196  * @cfg {String} title Default text to display in the title bar (defaults to null)
30197  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30198  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30199  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30200  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30201  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30202  * (defaults to null with no animation)
30203  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30204  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30205  * property for valid values (defaults to 'all')
30206  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30207  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30208  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30209  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30210  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30211  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30212  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30213  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30214  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30215  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30216  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30217  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30218  * draggable = true (defaults to false)
30219  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30220  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30221  * shadow (defaults to false)
30222  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30223  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30224  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30225  * @cfg {Array} buttons Array of buttons
30226  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30227  * @constructor
30228  * Create a new BasicDialog.
30229  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30230  * @param {Object} config Configuration options
30231  */
30232 Roo.BasicDialog = function(el, config){
30233     this.el = Roo.get(el);
30234     var dh = Roo.DomHelper;
30235     if(!this.el && config && config.autoCreate){
30236         if(typeof config.autoCreate == "object"){
30237             if(!config.autoCreate.id){
30238                 config.autoCreate.id = el;
30239             }
30240             this.el = dh.append(document.body,
30241                         config.autoCreate, true);
30242         }else{
30243             this.el = dh.append(document.body,
30244                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30245         }
30246     }
30247     el = this.el;
30248     el.setDisplayed(true);
30249     el.hide = this.hideAction;
30250     this.id = el.id;
30251     el.addClass("x-dlg");
30252
30253     Roo.apply(this, config);
30254
30255     this.proxy = el.createProxy("x-dlg-proxy");
30256     this.proxy.hide = this.hideAction;
30257     this.proxy.setOpacity(.5);
30258     this.proxy.hide();
30259
30260     if(config.width){
30261         el.setWidth(config.width);
30262     }
30263     if(config.height){
30264         el.setHeight(config.height);
30265     }
30266     this.size = el.getSize();
30267     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30268         this.xy = [config.x,config.y];
30269     }else{
30270         this.xy = el.getCenterXY(true);
30271     }
30272     /** The header element @type Roo.Element */
30273     this.header = el.child("> .x-dlg-hd");
30274     /** The body element @type Roo.Element */
30275     this.body = el.child("> .x-dlg-bd");
30276     /** The footer element @type Roo.Element */
30277     this.footer = el.child("> .x-dlg-ft");
30278
30279     if(!this.header){
30280         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30281     }
30282     if(!this.body){
30283         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30284     }
30285
30286     this.header.unselectable();
30287     if(this.title){
30288         this.header.update(this.title);
30289     }
30290     // this element allows the dialog to be focused for keyboard event
30291     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30292     this.focusEl.swallowEvent("click", true);
30293
30294     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30295
30296     // wrap the body and footer for special rendering
30297     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30298     if(this.footer){
30299         this.bwrap.dom.appendChild(this.footer.dom);
30300     }
30301
30302     this.bg = this.el.createChild({
30303         tag: "div", cls:"x-dlg-bg",
30304         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30305     });
30306     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30307
30308
30309     if(this.autoScroll !== false && !this.autoTabs){
30310         this.body.setStyle("overflow", "auto");
30311     }
30312
30313     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30314
30315     if(this.closable !== false){
30316         this.el.addClass("x-dlg-closable");
30317         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30318         this.close.on("click", this.closeClick, this);
30319         this.close.addClassOnOver("x-dlg-close-over");
30320     }
30321     if(this.collapsible !== false){
30322         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30323         this.collapseBtn.on("click", this.collapseClick, this);
30324         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30325         this.header.on("dblclick", this.collapseClick, this);
30326     }
30327     if(this.resizable !== false){
30328         this.el.addClass("x-dlg-resizable");
30329         this.resizer = new Roo.Resizable(el, {
30330             minWidth: this.minWidth || 80,
30331             minHeight:this.minHeight || 80,
30332             handles: this.resizeHandles || "all",
30333             pinned: true
30334         });
30335         this.resizer.on("beforeresize", this.beforeResize, this);
30336         this.resizer.on("resize", this.onResize, this);
30337     }
30338     if(this.draggable !== false){
30339         el.addClass("x-dlg-draggable");
30340         if (!this.proxyDrag) {
30341             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30342         }
30343         else {
30344             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30345         }
30346         dd.setHandleElId(this.header.id);
30347         dd.endDrag = this.endMove.createDelegate(this);
30348         dd.startDrag = this.startMove.createDelegate(this);
30349         dd.onDrag = this.onDrag.createDelegate(this);
30350         dd.scroll = false;
30351         this.dd = dd;
30352     }
30353     if(this.modal){
30354         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30355         this.mask.enableDisplayMode("block");
30356         this.mask.hide();
30357         this.el.addClass("x-dlg-modal");
30358     }
30359     if(this.shadow){
30360         this.shadow = new Roo.Shadow({
30361             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30362             offset : this.shadowOffset
30363         });
30364     }else{
30365         this.shadowOffset = 0;
30366     }
30367     if(Roo.useShims && this.shim !== false){
30368         this.shim = this.el.createShim();
30369         this.shim.hide = this.hideAction;
30370         this.shim.hide();
30371     }else{
30372         this.shim = false;
30373     }
30374     if(this.autoTabs){
30375         this.initTabs();
30376     }
30377     if (this.buttons) { 
30378         var bts= this.buttons;
30379         this.buttons = [];
30380         Roo.each(bts, function(b) {
30381             this.addButton(b);
30382         }, this);
30383     }
30384     
30385     
30386     this.addEvents({
30387         /**
30388          * @event keydown
30389          * Fires when a key is pressed
30390          * @param {Roo.BasicDialog} this
30391          * @param {Roo.EventObject} e
30392          */
30393         "keydown" : true,
30394         /**
30395          * @event move
30396          * Fires when this dialog is moved by the user.
30397          * @param {Roo.BasicDialog} this
30398          * @param {Number} x The new page X
30399          * @param {Number} y The new page Y
30400          */
30401         "move" : true,
30402         /**
30403          * @event resize
30404          * Fires when this dialog is resized by the user.
30405          * @param {Roo.BasicDialog} this
30406          * @param {Number} width The new width
30407          * @param {Number} height The new height
30408          */
30409         "resize" : true,
30410         /**
30411          * @event beforehide
30412          * Fires before this dialog is hidden.
30413          * @param {Roo.BasicDialog} this
30414          */
30415         "beforehide" : true,
30416         /**
30417          * @event hide
30418          * Fires when this dialog is hidden.
30419          * @param {Roo.BasicDialog} this
30420          */
30421         "hide" : true,
30422         /**
30423          * @event beforeshow
30424          * Fires before this dialog is shown.
30425          * @param {Roo.BasicDialog} this
30426          */
30427         "beforeshow" : true,
30428         /**
30429          * @event show
30430          * Fires when this dialog is shown.
30431          * @param {Roo.BasicDialog} this
30432          */
30433         "show" : true
30434     });
30435     el.on("keydown", this.onKeyDown, this);
30436     el.on("mousedown", this.toFront, this);
30437     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30438     this.el.hide();
30439     Roo.DialogManager.register(this);
30440     Roo.BasicDialog.superclass.constructor.call(this);
30441 };
30442
30443 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30444     shadowOffset: Roo.isIE ? 6 : 5,
30445     minHeight: 80,
30446     minWidth: 200,
30447     minButtonWidth: 75,
30448     defaultButton: null,
30449     buttonAlign: "right",
30450     tabTag: 'div',
30451     firstShow: true,
30452
30453     /**
30454      * Sets the dialog title text
30455      * @param {String} text The title text to display
30456      * @return {Roo.BasicDialog} this
30457      */
30458     setTitle : function(text){
30459         this.header.update(text);
30460         return this;
30461     },
30462
30463     // private
30464     closeClick : function(){
30465         this.hide();
30466     },
30467
30468     // private
30469     collapseClick : function(){
30470         this[this.collapsed ? "expand" : "collapse"]();
30471     },
30472
30473     /**
30474      * Collapses the dialog to its minimized state (only the title bar is visible).
30475      * Equivalent to the user clicking the collapse dialog button.
30476      */
30477     collapse : function(){
30478         if(!this.collapsed){
30479             this.collapsed = true;
30480             this.el.addClass("x-dlg-collapsed");
30481             this.restoreHeight = this.el.getHeight();
30482             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30483         }
30484     },
30485
30486     /**
30487      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30488      * clicking the expand dialog button.
30489      */
30490     expand : function(){
30491         if(this.collapsed){
30492             this.collapsed = false;
30493             this.el.removeClass("x-dlg-collapsed");
30494             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30495         }
30496     },
30497
30498     /**
30499      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30500      * @return {Roo.TabPanel} The tabs component
30501      */
30502     initTabs : function(){
30503         var tabs = this.getTabs();
30504         while(tabs.getTab(0)){
30505             tabs.removeTab(0);
30506         }
30507         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30508             var dom = el.dom;
30509             tabs.addTab(Roo.id(dom), dom.title);
30510             dom.title = "";
30511         });
30512         tabs.activate(0);
30513         return tabs;
30514     },
30515
30516     // private
30517     beforeResize : function(){
30518         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30519     },
30520
30521     // private
30522     onResize : function(){
30523         this.refreshSize();
30524         this.syncBodyHeight();
30525         this.adjustAssets();
30526         this.focus();
30527         this.fireEvent("resize", this, this.size.width, this.size.height);
30528     },
30529
30530     // private
30531     onKeyDown : function(e){
30532         if(this.isVisible()){
30533             this.fireEvent("keydown", this, e);
30534         }
30535     },
30536
30537     /**
30538      * Resizes the dialog.
30539      * @param {Number} width
30540      * @param {Number} height
30541      * @return {Roo.BasicDialog} this
30542      */
30543     resizeTo : function(width, height){
30544         this.el.setSize(width, height);
30545         this.size = {width: width, height: height};
30546         this.syncBodyHeight();
30547         if(this.fixedcenter){
30548             this.center();
30549         }
30550         if(this.isVisible()){
30551             this.constrainXY();
30552             this.adjustAssets();
30553         }
30554         this.fireEvent("resize", this, width, height);
30555         return this;
30556     },
30557
30558
30559     /**
30560      * Resizes the dialog to fit the specified content size.
30561      * @param {Number} width
30562      * @param {Number} height
30563      * @return {Roo.BasicDialog} this
30564      */
30565     setContentSize : function(w, h){
30566         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30567         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30568         //if(!this.el.isBorderBox()){
30569             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30570             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30571         //}
30572         if(this.tabs){
30573             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30574             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30575         }
30576         this.resizeTo(w, h);
30577         return this;
30578     },
30579
30580     /**
30581      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30582      * executed in response to a particular key being pressed while the dialog is active.
30583      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30584      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30585      * @param {Function} fn The function to call
30586      * @param {Object} scope (optional) The scope of the function
30587      * @return {Roo.BasicDialog} this
30588      */
30589     addKeyListener : function(key, fn, scope){
30590         var keyCode, shift, ctrl, alt;
30591         if(typeof key == "object" && !(key instanceof Array)){
30592             keyCode = key["key"];
30593             shift = key["shift"];
30594             ctrl = key["ctrl"];
30595             alt = key["alt"];
30596         }else{
30597             keyCode = key;
30598         }
30599         var handler = function(dlg, e){
30600             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30601                 var k = e.getKey();
30602                 if(keyCode instanceof Array){
30603                     for(var i = 0, len = keyCode.length; i < len; i++){
30604                         if(keyCode[i] == k){
30605                           fn.call(scope || window, dlg, k, e);
30606                           return;
30607                         }
30608                     }
30609                 }else{
30610                     if(k == keyCode){
30611                         fn.call(scope || window, dlg, k, e);
30612                     }
30613                 }
30614             }
30615         };
30616         this.on("keydown", handler);
30617         return this;
30618     },
30619
30620     /**
30621      * Returns the TabPanel component (creates it if it doesn't exist).
30622      * Note: If you wish to simply check for the existence of tabs without creating them,
30623      * check for a null 'tabs' property.
30624      * @return {Roo.TabPanel} The tabs component
30625      */
30626     getTabs : function(){
30627         if(!this.tabs){
30628             this.el.addClass("x-dlg-auto-tabs");
30629             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30630             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30631         }
30632         return this.tabs;
30633     },
30634
30635     /**
30636      * Adds a button to the footer section of the dialog.
30637      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30638      * object or a valid Roo.DomHelper element config
30639      * @param {Function} handler The function called when the button is clicked
30640      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30641      * @return {Roo.Button} The new button
30642      */
30643     addButton : function(config, handler, scope){
30644         var dh = Roo.DomHelper;
30645         if(!this.footer){
30646             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30647         }
30648         if(!this.btnContainer){
30649             var tb = this.footer.createChild({
30650
30651                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30652                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30653             }, null, true);
30654             this.btnContainer = tb.firstChild.firstChild.firstChild;
30655         }
30656         var bconfig = {
30657             handler: handler,
30658             scope: scope,
30659             minWidth: this.minButtonWidth,
30660             hideParent:true
30661         };
30662         if(typeof config == "string"){
30663             bconfig.text = config;
30664         }else{
30665             if(config.tag){
30666                 bconfig.dhconfig = config;
30667             }else{
30668                 Roo.apply(bconfig, config);
30669             }
30670         }
30671         var fc = false;
30672         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30673             bconfig.position = Math.max(0, bconfig.position);
30674             fc = this.btnContainer.childNodes[bconfig.position];
30675         }
30676          
30677         var btn = new Roo.Button(
30678             fc ? 
30679                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30680                 : this.btnContainer.appendChild(document.createElement("td")),
30681             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30682             bconfig
30683         );
30684         this.syncBodyHeight();
30685         if(!this.buttons){
30686             /**
30687              * Array of all the buttons that have been added to this dialog via addButton
30688              * @type Array
30689              */
30690             this.buttons = [];
30691         }
30692         this.buttons.push(btn);
30693         return btn;
30694     },
30695
30696     /**
30697      * Sets the default button to be focused when the dialog is displayed.
30698      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30699      * @return {Roo.BasicDialog} this
30700      */
30701     setDefaultButton : function(btn){
30702         this.defaultButton = btn;
30703         return this;
30704     },
30705
30706     // private
30707     getHeaderFooterHeight : function(safe){
30708         var height = 0;
30709         if(this.header){
30710            height += this.header.getHeight();
30711         }
30712         if(this.footer){
30713            var fm = this.footer.getMargins();
30714             height += (this.footer.getHeight()+fm.top+fm.bottom);
30715         }
30716         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30717         height += this.centerBg.getPadding("tb");
30718         return height;
30719     },
30720
30721     // private
30722     syncBodyHeight : function()
30723     {
30724         var bd = this.body, // the text
30725             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30726             bw = this.bwrap;
30727         var height = this.size.height - this.getHeaderFooterHeight(false);
30728         bd.setHeight(height-bd.getMargins("tb"));
30729         var hh = this.header.getHeight();
30730         var h = this.size.height-hh;
30731         cb.setHeight(h);
30732         
30733         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30734         bw.setHeight(h-cb.getPadding("tb"));
30735         
30736         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30737         bd.setWidth(bw.getWidth(true));
30738         if(this.tabs){
30739             this.tabs.syncHeight();
30740             if(Roo.isIE){
30741                 this.tabs.el.repaint();
30742             }
30743         }
30744     },
30745
30746     /**
30747      * Restores the previous state of the dialog if Roo.state is configured.
30748      * @return {Roo.BasicDialog} this
30749      */
30750     restoreState : function(){
30751         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30752         if(box && box.width){
30753             this.xy = [box.x, box.y];
30754             this.resizeTo(box.width, box.height);
30755         }
30756         return this;
30757     },
30758
30759     // private
30760     beforeShow : function(){
30761         this.expand();
30762         if(this.fixedcenter){
30763             this.xy = this.el.getCenterXY(true);
30764         }
30765         if(this.modal){
30766             Roo.get(document.body).addClass("x-body-masked");
30767             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30768             this.mask.show();
30769         }
30770         this.constrainXY();
30771     },
30772
30773     // private
30774     animShow : function(){
30775         var b = Roo.get(this.animateTarget).getBox();
30776         this.proxy.setSize(b.width, b.height);
30777         this.proxy.setLocation(b.x, b.y);
30778         this.proxy.show();
30779         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30780                     true, .35, this.showEl.createDelegate(this));
30781     },
30782
30783     /**
30784      * Shows the dialog.
30785      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30786      * @return {Roo.BasicDialog} this
30787      */
30788     show : function(animateTarget){
30789         if (this.fireEvent("beforeshow", this) === false){
30790             return;
30791         }
30792         if(this.syncHeightBeforeShow){
30793             this.syncBodyHeight();
30794         }else if(this.firstShow){
30795             this.firstShow = false;
30796             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30797         }
30798         this.animateTarget = animateTarget || this.animateTarget;
30799         if(!this.el.isVisible()){
30800             this.beforeShow();
30801             if(this.animateTarget && Roo.get(this.animateTarget)){
30802                 this.animShow();
30803             }else{
30804                 this.showEl();
30805             }
30806         }
30807         return this;
30808     },
30809
30810     // private
30811     showEl : function(){
30812         this.proxy.hide();
30813         this.el.setXY(this.xy);
30814         this.el.show();
30815         this.adjustAssets(true);
30816         this.toFront();
30817         this.focus();
30818         // IE peekaboo bug - fix found by Dave Fenwick
30819         if(Roo.isIE){
30820             this.el.repaint();
30821         }
30822         this.fireEvent("show", this);
30823     },
30824
30825     /**
30826      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30827      * dialog itself will receive focus.
30828      */
30829     focus : function(){
30830         if(this.defaultButton){
30831             this.defaultButton.focus();
30832         }else{
30833             this.focusEl.focus();
30834         }
30835     },
30836
30837     // private
30838     constrainXY : function(){
30839         if(this.constraintoviewport !== false){
30840             if(!this.viewSize){
30841                 if(this.container){
30842                     var s = this.container.getSize();
30843                     this.viewSize = [s.width, s.height];
30844                 }else{
30845                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30846                 }
30847             }
30848             var s = Roo.get(this.container||document).getScroll();
30849
30850             var x = this.xy[0], y = this.xy[1];
30851             var w = this.size.width, h = this.size.height;
30852             var vw = this.viewSize[0], vh = this.viewSize[1];
30853             // only move it if it needs it
30854             var moved = false;
30855             // first validate right/bottom
30856             if(x + w > vw+s.left){
30857                 x = vw - w;
30858                 moved = true;
30859             }
30860             if(y + h > vh+s.top){
30861                 y = vh - h;
30862                 moved = true;
30863             }
30864             // then make sure top/left isn't negative
30865             if(x < s.left){
30866                 x = s.left;
30867                 moved = true;
30868             }
30869             if(y < s.top){
30870                 y = s.top;
30871                 moved = true;
30872             }
30873             if(moved){
30874                 // cache xy
30875                 this.xy = [x, y];
30876                 if(this.isVisible()){
30877                     this.el.setLocation(x, y);
30878                     this.adjustAssets();
30879                 }
30880             }
30881         }
30882     },
30883
30884     // private
30885     onDrag : function(){
30886         if(!this.proxyDrag){
30887             this.xy = this.el.getXY();
30888             this.adjustAssets();
30889         }
30890     },
30891
30892     // private
30893     adjustAssets : function(doShow){
30894         var x = this.xy[0], y = this.xy[1];
30895         var w = this.size.width, h = this.size.height;
30896         if(doShow === true){
30897             if(this.shadow){
30898                 this.shadow.show(this.el);
30899             }
30900             if(this.shim){
30901                 this.shim.show();
30902             }
30903         }
30904         if(this.shadow && this.shadow.isVisible()){
30905             this.shadow.show(this.el);
30906         }
30907         if(this.shim && this.shim.isVisible()){
30908             this.shim.setBounds(x, y, w, h);
30909         }
30910     },
30911
30912     // private
30913     adjustViewport : function(w, h){
30914         if(!w || !h){
30915             w = Roo.lib.Dom.getViewWidth();
30916             h = Roo.lib.Dom.getViewHeight();
30917         }
30918         // cache the size
30919         this.viewSize = [w, h];
30920         if(this.modal && this.mask.isVisible()){
30921             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30922             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30923         }
30924         if(this.isVisible()){
30925             this.constrainXY();
30926         }
30927     },
30928
30929     /**
30930      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30931      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30932      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30933      */
30934     destroy : function(removeEl){
30935         if(this.isVisible()){
30936             this.animateTarget = null;
30937             this.hide();
30938         }
30939         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30940         if(this.tabs){
30941             this.tabs.destroy(removeEl);
30942         }
30943         Roo.destroy(
30944              this.shim,
30945              this.proxy,
30946              this.resizer,
30947              this.close,
30948              this.mask
30949         );
30950         if(this.dd){
30951             this.dd.unreg();
30952         }
30953         if(this.buttons){
30954            for(var i = 0, len = this.buttons.length; i < len; i++){
30955                this.buttons[i].destroy();
30956            }
30957         }
30958         this.el.removeAllListeners();
30959         if(removeEl === true){
30960             this.el.update("");
30961             this.el.remove();
30962         }
30963         Roo.DialogManager.unregister(this);
30964     },
30965
30966     // private
30967     startMove : function(){
30968         if(this.proxyDrag){
30969             this.proxy.show();
30970         }
30971         if(this.constraintoviewport !== false){
30972             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30973         }
30974     },
30975
30976     // private
30977     endMove : function(){
30978         if(!this.proxyDrag){
30979             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30980         }else{
30981             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30982             this.proxy.hide();
30983         }
30984         this.refreshSize();
30985         this.adjustAssets();
30986         this.focus();
30987         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30988     },
30989
30990     /**
30991      * Brings this dialog to the front of any other visible dialogs
30992      * @return {Roo.BasicDialog} this
30993      */
30994     toFront : function(){
30995         Roo.DialogManager.bringToFront(this);
30996         return this;
30997     },
30998
30999     /**
31000      * Sends this dialog to the back (under) of any other visible dialogs
31001      * @return {Roo.BasicDialog} this
31002      */
31003     toBack : function(){
31004         Roo.DialogManager.sendToBack(this);
31005         return this;
31006     },
31007
31008     /**
31009      * Centers this dialog in the viewport
31010      * @return {Roo.BasicDialog} this
31011      */
31012     center : function(){
31013         var xy = this.el.getCenterXY(true);
31014         this.moveTo(xy[0], xy[1]);
31015         return this;
31016     },
31017
31018     /**
31019      * Moves the dialog's top-left corner to the specified point
31020      * @param {Number} x
31021      * @param {Number} y
31022      * @return {Roo.BasicDialog} this
31023      */
31024     moveTo : function(x, y){
31025         this.xy = [x,y];
31026         if(this.isVisible()){
31027             this.el.setXY(this.xy);
31028             this.adjustAssets();
31029         }
31030         return this;
31031     },
31032
31033     /**
31034      * Aligns the dialog to the specified element
31035      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31036      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31037      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31038      * @return {Roo.BasicDialog} this
31039      */
31040     alignTo : function(element, position, offsets){
31041         this.xy = this.el.getAlignToXY(element, position, offsets);
31042         if(this.isVisible()){
31043             this.el.setXY(this.xy);
31044             this.adjustAssets();
31045         }
31046         return this;
31047     },
31048
31049     /**
31050      * Anchors an element to another element and realigns it when the window is resized.
31051      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31052      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31053      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31054      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31055      * is a number, it is used as the buffer delay (defaults to 50ms).
31056      * @return {Roo.BasicDialog} this
31057      */
31058     anchorTo : function(el, alignment, offsets, monitorScroll){
31059         var action = function(){
31060             this.alignTo(el, alignment, offsets);
31061         };
31062         Roo.EventManager.onWindowResize(action, this);
31063         var tm = typeof monitorScroll;
31064         if(tm != 'undefined'){
31065             Roo.EventManager.on(window, 'scroll', action, this,
31066                 {buffer: tm == 'number' ? monitorScroll : 50});
31067         }
31068         action.call(this);
31069         return this;
31070     },
31071
31072     /**
31073      * Returns true if the dialog is visible
31074      * @return {Boolean}
31075      */
31076     isVisible : function(){
31077         return this.el.isVisible();
31078     },
31079
31080     // private
31081     animHide : function(callback){
31082         var b = Roo.get(this.animateTarget).getBox();
31083         this.proxy.show();
31084         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31085         this.el.hide();
31086         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31087                     this.hideEl.createDelegate(this, [callback]));
31088     },
31089
31090     /**
31091      * Hides the dialog.
31092      * @param {Function} callback (optional) Function to call when the dialog is hidden
31093      * @return {Roo.BasicDialog} this
31094      */
31095     hide : function(callback){
31096         if (this.fireEvent("beforehide", this) === false){
31097             return;
31098         }
31099         if(this.shadow){
31100             this.shadow.hide();
31101         }
31102         if(this.shim) {
31103           this.shim.hide();
31104         }
31105         // sometimes animateTarget seems to get set.. causing problems...
31106         // this just double checks..
31107         if(this.animateTarget && Roo.get(this.animateTarget)) {
31108            this.animHide(callback);
31109         }else{
31110             this.el.hide();
31111             this.hideEl(callback);
31112         }
31113         return this;
31114     },
31115
31116     // private
31117     hideEl : function(callback){
31118         this.proxy.hide();
31119         if(this.modal){
31120             this.mask.hide();
31121             Roo.get(document.body).removeClass("x-body-masked");
31122         }
31123         this.fireEvent("hide", this);
31124         if(typeof callback == "function"){
31125             callback();
31126         }
31127     },
31128
31129     // private
31130     hideAction : function(){
31131         this.setLeft("-10000px");
31132         this.setTop("-10000px");
31133         this.setStyle("visibility", "hidden");
31134     },
31135
31136     // private
31137     refreshSize : function(){
31138         this.size = this.el.getSize();
31139         this.xy = this.el.getXY();
31140         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31141     },
31142
31143     // private
31144     // z-index is managed by the DialogManager and may be overwritten at any time
31145     setZIndex : function(index){
31146         if(this.modal){
31147             this.mask.setStyle("z-index", index);
31148         }
31149         if(this.shim){
31150             this.shim.setStyle("z-index", ++index);
31151         }
31152         if(this.shadow){
31153             this.shadow.setZIndex(++index);
31154         }
31155         this.el.setStyle("z-index", ++index);
31156         if(this.proxy){
31157             this.proxy.setStyle("z-index", ++index);
31158         }
31159         if(this.resizer){
31160             this.resizer.proxy.setStyle("z-index", ++index);
31161         }
31162
31163         this.lastZIndex = index;
31164     },
31165
31166     /**
31167      * Returns the element for this dialog
31168      * @return {Roo.Element} The underlying dialog Element
31169      */
31170     getEl : function(){
31171         return this.el;
31172     }
31173 });
31174
31175 /**
31176  * @class Roo.DialogManager
31177  * Provides global access to BasicDialogs that have been created and
31178  * support for z-indexing (layering) multiple open dialogs.
31179  */
31180 Roo.DialogManager = function(){
31181     var list = {};
31182     var accessList = [];
31183     var front = null;
31184
31185     // private
31186     var sortDialogs = function(d1, d2){
31187         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31188     };
31189
31190     // private
31191     var orderDialogs = function(){
31192         accessList.sort(sortDialogs);
31193         var seed = Roo.DialogManager.zseed;
31194         for(var i = 0, len = accessList.length; i < len; i++){
31195             var dlg = accessList[i];
31196             if(dlg){
31197                 dlg.setZIndex(seed + (i*10));
31198             }
31199         }
31200     };
31201
31202     return {
31203         /**
31204          * The starting z-index for BasicDialogs (defaults to 9000)
31205          * @type Number The z-index value
31206          */
31207         zseed : 9000,
31208
31209         // private
31210         register : function(dlg){
31211             list[dlg.id] = dlg;
31212             accessList.push(dlg);
31213         },
31214
31215         // private
31216         unregister : function(dlg){
31217             delete list[dlg.id];
31218             var i=0;
31219             var len=0;
31220             if(!accessList.indexOf){
31221                 for(  i = 0, len = accessList.length; i < len; i++){
31222                     if(accessList[i] == dlg){
31223                         accessList.splice(i, 1);
31224                         return;
31225                     }
31226                 }
31227             }else{
31228                  i = accessList.indexOf(dlg);
31229                 if(i != -1){
31230                     accessList.splice(i, 1);
31231                 }
31232             }
31233         },
31234
31235         /**
31236          * Gets a registered dialog by id
31237          * @param {String/Object} id The id of the dialog or a dialog
31238          * @return {Roo.BasicDialog} this
31239          */
31240         get : function(id){
31241             return typeof id == "object" ? id : list[id];
31242         },
31243
31244         /**
31245          * Brings the specified dialog to the front
31246          * @param {String/Object} dlg The id of the dialog or a dialog
31247          * @return {Roo.BasicDialog} this
31248          */
31249         bringToFront : function(dlg){
31250             dlg = this.get(dlg);
31251             if(dlg != front){
31252                 front = dlg;
31253                 dlg._lastAccess = new Date().getTime();
31254                 orderDialogs();
31255             }
31256             return dlg;
31257         },
31258
31259         /**
31260          * Sends the specified dialog to the back
31261          * @param {String/Object} dlg The id of the dialog or a dialog
31262          * @return {Roo.BasicDialog} this
31263          */
31264         sendToBack : function(dlg){
31265             dlg = this.get(dlg);
31266             dlg._lastAccess = -(new Date().getTime());
31267             orderDialogs();
31268             return dlg;
31269         },
31270
31271         /**
31272          * Hides all dialogs
31273          */
31274         hideAll : function(){
31275             for(var id in list){
31276                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31277                     list[id].hide();
31278                 }
31279             }
31280         }
31281     };
31282 }();
31283
31284 /**
31285  * @class Roo.LayoutDialog
31286  * @extends Roo.BasicDialog
31287  * Dialog which provides adjustments for working with a layout in a Dialog.
31288  * Add your necessary layout config options to the dialog's config.<br>
31289  * Example usage (including a nested layout):
31290  * <pre><code>
31291 if(!dialog){
31292     dialog = new Roo.LayoutDialog("download-dlg", {
31293         modal: true,
31294         width:600,
31295         height:450,
31296         shadow:true,
31297         minWidth:500,
31298         minHeight:350,
31299         autoTabs:true,
31300         proxyDrag:true,
31301         // layout config merges with the dialog config
31302         center:{
31303             tabPosition: "top",
31304             alwaysShowTabs: true
31305         }
31306     });
31307     dialog.addKeyListener(27, dialog.hide, dialog);
31308     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31309     dialog.addButton("Build It!", this.getDownload, this);
31310
31311     // we can even add nested layouts
31312     var innerLayout = new Roo.BorderLayout("dl-inner", {
31313         east: {
31314             initialSize: 200,
31315             autoScroll:true,
31316             split:true
31317         },
31318         center: {
31319             autoScroll:true
31320         }
31321     });
31322     innerLayout.beginUpdate();
31323     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31324     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31325     innerLayout.endUpdate(true);
31326
31327     var layout = dialog.getLayout();
31328     layout.beginUpdate();
31329     layout.add("center", new Roo.ContentPanel("standard-panel",
31330                         {title: "Download the Source", fitToFrame:true}));
31331     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31332                {title: "Build your own roo.js"}));
31333     layout.getRegion("center").showPanel(sp);
31334     layout.endUpdate();
31335 }
31336 </code></pre>
31337     * @constructor
31338     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31339     * @param {Object} config configuration options
31340   */
31341 Roo.LayoutDialog = function(el, cfg){
31342     
31343     var config=  cfg;
31344     if (typeof(cfg) == 'undefined') {
31345         config = Roo.apply({}, el);
31346         // not sure why we use documentElement here.. - it should always be body.
31347         // IE7 borks horribly if we use documentElement.
31348         // webkit also does not like documentElement - it creates a body element...
31349         el = Roo.get( document.body || document.documentElement ).createChild();
31350         //config.autoCreate = true;
31351     }
31352     
31353     
31354     config.autoTabs = false;
31355     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31356     this.body.setStyle({overflow:"hidden", position:"relative"});
31357     this.layout = new Roo.BorderLayout(this.body.dom, config);
31358     this.layout.monitorWindowResize = false;
31359     this.el.addClass("x-dlg-auto-layout");
31360     // fix case when center region overwrites center function
31361     this.center = Roo.BasicDialog.prototype.center;
31362     this.on("show", this.layout.layout, this.layout, true);
31363     if (config.items) {
31364         var xitems = config.items;
31365         delete config.items;
31366         Roo.each(xitems, this.addxtype, this);
31367     }
31368     
31369     
31370 };
31371 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31372     /**
31373      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31374      * @deprecated
31375      */
31376     endUpdate : function(){
31377         this.layout.endUpdate();
31378     },
31379
31380     /**
31381      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31382      *  @deprecated
31383      */
31384     beginUpdate : function(){
31385         this.layout.beginUpdate();
31386     },
31387
31388     /**
31389      * Get the BorderLayout for this dialog
31390      * @return {Roo.BorderLayout}
31391      */
31392     getLayout : function(){
31393         return this.layout;
31394     },
31395
31396     showEl : function(){
31397         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31398         if(Roo.isIE7){
31399             this.layout.layout();
31400         }
31401     },
31402
31403     // private
31404     // Use the syncHeightBeforeShow config option to control this automatically
31405     syncBodyHeight : function(){
31406         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31407         if(this.layout){this.layout.layout();}
31408     },
31409     
31410       /**
31411      * Add an xtype element (actually adds to the layout.)
31412      * @return {Object} xdata xtype object data.
31413      */
31414     
31415     addxtype : function(c) {
31416         return this.layout.addxtype(c);
31417     }
31418 });/*
31419  * Based on:
31420  * Ext JS Library 1.1.1
31421  * Copyright(c) 2006-2007, Ext JS, LLC.
31422  *
31423  * Originally Released Under LGPL - original licence link has changed is not relivant.
31424  *
31425  * Fork - LGPL
31426  * <script type="text/javascript">
31427  */
31428  
31429 /**
31430  * @class Roo.MessageBox
31431  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31432  * Example usage:
31433  *<pre><code>
31434 // Basic alert:
31435 Roo.Msg.alert('Status', 'Changes saved successfully.');
31436
31437 // Prompt for user data:
31438 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31439     if (btn == 'ok'){
31440         // process text value...
31441     }
31442 });
31443
31444 // Show a dialog using config options:
31445 Roo.Msg.show({
31446    title:'Save Changes?',
31447    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31448    buttons: Roo.Msg.YESNOCANCEL,
31449    fn: processResult,
31450    animEl: 'elId'
31451 });
31452 </code></pre>
31453  * @singleton
31454  */
31455 Roo.MessageBox = function(){
31456     var dlg, opt, mask, waitTimer;
31457     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31458     var buttons, activeTextEl, bwidth;
31459
31460     // private
31461     var handleButton = function(button){
31462         dlg.hide();
31463         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31464     };
31465
31466     // private
31467     var handleHide = function(){
31468         if(opt && opt.cls){
31469             dlg.el.removeClass(opt.cls);
31470         }
31471         if(waitTimer){
31472             Roo.TaskMgr.stop(waitTimer);
31473             waitTimer = null;
31474         }
31475     };
31476
31477     // private
31478     var updateButtons = function(b){
31479         var width = 0;
31480         if(!b){
31481             buttons["ok"].hide();
31482             buttons["cancel"].hide();
31483             buttons["yes"].hide();
31484             buttons["no"].hide();
31485             dlg.footer.dom.style.display = 'none';
31486             return width;
31487         }
31488         dlg.footer.dom.style.display = '';
31489         for(var k in buttons){
31490             if(typeof buttons[k] != "function"){
31491                 if(b[k]){
31492                     buttons[k].show();
31493                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31494                     width += buttons[k].el.getWidth()+15;
31495                 }else{
31496                     buttons[k].hide();
31497                 }
31498             }
31499         }
31500         return width;
31501     };
31502
31503     // private
31504     var handleEsc = function(d, k, e){
31505         if(opt && opt.closable !== false){
31506             dlg.hide();
31507         }
31508         if(e){
31509             e.stopEvent();
31510         }
31511     };
31512
31513     return {
31514         /**
31515          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31516          * @return {Roo.BasicDialog} The BasicDialog element
31517          */
31518         getDialog : function(){
31519            if(!dlg){
31520                 dlg = new Roo.BasicDialog("x-msg-box", {
31521                     autoCreate : true,
31522                     shadow: true,
31523                     draggable: true,
31524                     resizable:false,
31525                     constraintoviewport:false,
31526                     fixedcenter:true,
31527                     collapsible : false,
31528                     shim:true,
31529                     modal: true,
31530                     width:400, height:100,
31531                     buttonAlign:"center",
31532                     closeClick : function(){
31533                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31534                             handleButton("no");
31535                         }else{
31536                             handleButton("cancel");
31537                         }
31538                     }
31539                 });
31540                 dlg.on("hide", handleHide);
31541                 mask = dlg.mask;
31542                 dlg.addKeyListener(27, handleEsc);
31543                 buttons = {};
31544                 var bt = this.buttonText;
31545                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31546                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31547                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31548                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31549                 bodyEl = dlg.body.createChild({
31550
31551                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
31552                 });
31553                 msgEl = bodyEl.dom.firstChild;
31554                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31555                 textboxEl.enableDisplayMode();
31556                 textboxEl.addKeyListener([10,13], function(){
31557                     if(dlg.isVisible() && opt && opt.buttons){
31558                         if(opt.buttons.ok){
31559                             handleButton("ok");
31560                         }else if(opt.buttons.yes){
31561                             handleButton("yes");
31562                         }
31563                     }
31564                 });
31565                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31566                 textareaEl.enableDisplayMode();
31567                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31568                 progressEl.enableDisplayMode();
31569                 var pf = progressEl.dom.firstChild;
31570                 if (pf) {
31571                     pp = Roo.get(pf.firstChild);
31572                     pp.setHeight(pf.offsetHeight);
31573                 }
31574                 
31575             }
31576             return dlg;
31577         },
31578
31579         /**
31580          * Updates the message box body text
31581          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31582          * the XHTML-compliant non-breaking space character '&amp;#160;')
31583          * @return {Roo.MessageBox} This message box
31584          */
31585         updateText : function(text){
31586             if(!dlg.isVisible() && !opt.width){
31587                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31588             }
31589             msgEl.innerHTML = text || '&#160;';
31590       
31591             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31592             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31593             var w = Math.max(
31594                     Math.min(opt.width || cw , this.maxWidth), 
31595                     Math.max(opt.minWidth || this.minWidth, bwidth)
31596             );
31597             if(opt.prompt){
31598                 activeTextEl.setWidth(w);
31599             }
31600             if(dlg.isVisible()){
31601                 dlg.fixedcenter = false;
31602             }
31603             // to big, make it scroll. = But as usual stupid IE does not support
31604             // !important..
31605             
31606             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31607                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31608                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31609             } else {
31610                 bodyEl.dom.style.height = '';
31611                 bodyEl.dom.style.overflowY = '';
31612             }
31613             if (cw > w) {
31614                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31615             } else {
31616                 bodyEl.dom.style.overflowX = '';
31617             }
31618             
31619             dlg.setContentSize(w, bodyEl.getHeight());
31620             if(dlg.isVisible()){
31621                 dlg.fixedcenter = true;
31622             }
31623             return this;
31624         },
31625
31626         /**
31627          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31628          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31629          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31630          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31631          * @return {Roo.MessageBox} This message box
31632          */
31633         updateProgress : function(value, text){
31634             if(text){
31635                 this.updateText(text);
31636             }
31637             if (pp) { // weird bug on my firefox - for some reason this is not defined
31638                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31639             }
31640             return this;
31641         },        
31642
31643         /**
31644          * Returns true if the message box is currently displayed
31645          * @return {Boolean} True if the message box is visible, else false
31646          */
31647         isVisible : function(){
31648             return dlg && dlg.isVisible();  
31649         },
31650
31651         /**
31652          * Hides the message box if it is displayed
31653          */
31654         hide : function(){
31655             if(this.isVisible()){
31656                 dlg.hide();
31657             }  
31658         },
31659
31660         /**
31661          * Displays a new message box, or reinitializes an existing message box, based on the config options
31662          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31663          * The following config object properties are supported:
31664          * <pre>
31665 Property    Type             Description
31666 ----------  ---------------  ------------------------------------------------------------------------------------
31667 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31668                                    closes (defaults to undefined)
31669 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31670                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31671 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31672                                    progress and wait dialogs will ignore this property and always hide the
31673                                    close button as they can only be closed programmatically.
31674 cls               String           A custom CSS class to apply to the message box element
31675 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31676                                    displayed (defaults to 75)
31677 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31678                                    function will be btn (the name of the button that was clicked, if applicable,
31679                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31680                                    Progress and wait dialogs will ignore this option since they do not respond to
31681                                    user actions and can only be closed programmatically, so any required function
31682                                    should be called by the same code after it closes the dialog.
31683 icon              String           A CSS class that provides a background image to be used as an icon for
31684                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31685 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31686 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31687 modal             Boolean          False to allow user interaction with the page while the message box is
31688                                    displayed (defaults to true)
31689 msg               String           A string that will replace the existing message box body text (defaults
31690                                    to the XHTML-compliant non-breaking space character '&#160;')
31691 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31692 progress          Boolean          True to display a progress bar (defaults to false)
31693 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31694 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31695 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31696 title             String           The title text
31697 value             String           The string value to set into the active textbox element if displayed
31698 wait              Boolean          True to display a progress bar (defaults to false)
31699 width             Number           The width of the dialog in pixels
31700 </pre>
31701          *
31702          * Example usage:
31703          * <pre><code>
31704 Roo.Msg.show({
31705    title: 'Address',
31706    msg: 'Please enter your address:',
31707    width: 300,
31708    buttons: Roo.MessageBox.OKCANCEL,
31709    multiline: true,
31710    fn: saveAddress,
31711    animEl: 'addAddressBtn'
31712 });
31713 </code></pre>
31714          * @param {Object} config Configuration options
31715          * @return {Roo.MessageBox} This message box
31716          */
31717         show : function(options)
31718         {
31719             
31720             // this causes nightmares if you show one dialog after another
31721             // especially on callbacks..
31722              
31723             if(this.isVisible()){
31724                 
31725                 this.hide();
31726                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31727                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31728                 Roo.log("New Dialog Message:" +  options.msg )
31729                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31730                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31731                 
31732             }
31733             var d = this.getDialog();
31734             opt = options;
31735             d.setTitle(opt.title || "&#160;");
31736             d.close.setDisplayed(opt.closable !== false);
31737             activeTextEl = textboxEl;
31738             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31739             if(opt.prompt){
31740                 if(opt.multiline){
31741                     textboxEl.hide();
31742                     textareaEl.show();
31743                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31744                         opt.multiline : this.defaultTextHeight);
31745                     activeTextEl = textareaEl;
31746                 }else{
31747                     textboxEl.show();
31748                     textareaEl.hide();
31749                 }
31750             }else{
31751                 textboxEl.hide();
31752                 textareaEl.hide();
31753             }
31754             progressEl.setDisplayed(opt.progress === true);
31755             this.updateProgress(0);
31756             activeTextEl.dom.value = opt.value || "";
31757             if(opt.prompt){
31758                 dlg.setDefaultButton(activeTextEl);
31759             }else{
31760                 var bs = opt.buttons;
31761                 var db = null;
31762                 if(bs && bs.ok){
31763                     db = buttons["ok"];
31764                 }else if(bs && bs.yes){
31765                     db = buttons["yes"];
31766                 }
31767                 dlg.setDefaultButton(db);
31768             }
31769             bwidth = updateButtons(opt.buttons);
31770             this.updateText(opt.msg);
31771             if(opt.cls){
31772                 d.el.addClass(opt.cls);
31773             }
31774             d.proxyDrag = opt.proxyDrag === true;
31775             d.modal = opt.modal !== false;
31776             d.mask = opt.modal !== false ? mask : false;
31777             if(!d.isVisible()){
31778                 // force it to the end of the z-index stack so it gets a cursor in FF
31779                 document.body.appendChild(dlg.el.dom);
31780                 d.animateTarget = null;
31781                 d.show(options.animEl);
31782             }
31783             return this;
31784         },
31785
31786         /**
31787          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31788          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31789          * and closing the message box when the process is complete.
31790          * @param {String} title The title bar text
31791          * @param {String} msg The message box body text
31792          * @return {Roo.MessageBox} This message box
31793          */
31794         progress : function(title, msg){
31795             this.show({
31796                 title : title,
31797                 msg : msg,
31798                 buttons: false,
31799                 progress:true,
31800                 closable:false,
31801                 minWidth: this.minProgressWidth,
31802                 modal : true
31803             });
31804             return this;
31805         },
31806
31807         /**
31808          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31809          * If a callback function is passed it will be called after the user clicks the button, and the
31810          * id of the button that was clicked will be passed as the only parameter to the callback
31811          * (could also be the top-right close button).
31812          * @param {String} title The title bar text
31813          * @param {String} msg The message box body text
31814          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31815          * @param {Object} scope (optional) The scope of the callback function
31816          * @return {Roo.MessageBox} This message box
31817          */
31818         alert : function(title, msg, fn, scope){
31819             this.show({
31820                 title : title,
31821                 msg : msg,
31822                 buttons: this.OK,
31823                 fn: fn,
31824                 scope : scope,
31825                 modal : true
31826             });
31827             return this;
31828         },
31829
31830         /**
31831          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31832          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31833          * You are responsible for closing the message box when the process is complete.
31834          * @param {String} msg The message box body text
31835          * @param {String} title (optional) The title bar text
31836          * @return {Roo.MessageBox} This message box
31837          */
31838         wait : function(msg, title){
31839             this.show({
31840                 title : title,
31841                 msg : msg,
31842                 buttons: false,
31843                 closable:false,
31844                 progress:true,
31845                 modal:true,
31846                 width:300,
31847                 wait:true
31848             });
31849             waitTimer = Roo.TaskMgr.start({
31850                 run: function(i){
31851                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31852                 },
31853                 interval: 1000
31854             });
31855             return this;
31856         },
31857
31858         /**
31859          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31860          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31861          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31862          * @param {String} title The title bar text
31863          * @param {String} msg The message box body text
31864          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31865          * @param {Object} scope (optional) The scope of the callback function
31866          * @return {Roo.MessageBox} This message box
31867          */
31868         confirm : function(title, msg, fn, scope){
31869             this.show({
31870                 title : title,
31871                 msg : msg,
31872                 buttons: this.YESNO,
31873                 fn: fn,
31874                 scope : scope,
31875                 modal : true
31876             });
31877             return this;
31878         },
31879
31880         /**
31881          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31882          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31883          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31884          * (could also be the top-right close button) and the text that was entered will be passed as the two
31885          * parameters to the callback.
31886          * @param {String} title The title bar text
31887          * @param {String} msg The message box body text
31888          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31889          * @param {Object} scope (optional) The scope of the callback function
31890          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31891          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31892          * @return {Roo.MessageBox} This message box
31893          */
31894         prompt : function(title, msg, fn, scope, multiline){
31895             this.show({
31896                 title : title,
31897                 msg : msg,
31898                 buttons: this.OKCANCEL,
31899                 fn: fn,
31900                 minWidth:250,
31901                 scope : scope,
31902                 prompt:true,
31903                 multiline: multiline,
31904                 modal : true
31905             });
31906             return this;
31907         },
31908
31909         /**
31910          * Button config that displays a single OK button
31911          * @type Object
31912          */
31913         OK : {ok:true},
31914         /**
31915          * Button config that displays Yes and No buttons
31916          * @type Object
31917          */
31918         YESNO : {yes:true, no:true},
31919         /**
31920          * Button config that displays OK and Cancel buttons
31921          * @type Object
31922          */
31923         OKCANCEL : {ok:true, cancel:true},
31924         /**
31925          * Button config that displays Yes, No and Cancel buttons
31926          * @type Object
31927          */
31928         YESNOCANCEL : {yes:true, no:true, cancel:true},
31929
31930         /**
31931          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31932          * @type Number
31933          */
31934         defaultTextHeight : 75,
31935         /**
31936          * The maximum width in pixels of the message box (defaults to 600)
31937          * @type Number
31938          */
31939         maxWidth : 600,
31940         /**
31941          * The minimum width in pixels of the message box (defaults to 100)
31942          * @type Number
31943          */
31944         minWidth : 100,
31945         /**
31946          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31947          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31948          * @type Number
31949          */
31950         minProgressWidth : 250,
31951         /**
31952          * An object containing the default button text strings that can be overriden for localized language support.
31953          * Supported properties are: ok, cancel, yes and no.
31954          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31955          * @type Object
31956          */
31957         buttonText : {
31958             ok : "OK",
31959             cancel : "Cancel",
31960             yes : "Yes",
31961             no : "No"
31962         }
31963     };
31964 }();
31965
31966 /**
31967  * Shorthand for {@link Roo.MessageBox}
31968  */
31969 Roo.Msg = Roo.MessageBox;/*
31970  * Based on:
31971  * Ext JS Library 1.1.1
31972  * Copyright(c) 2006-2007, Ext JS, LLC.
31973  *
31974  * Originally Released Under LGPL - original licence link has changed is not relivant.
31975  *
31976  * Fork - LGPL
31977  * <script type="text/javascript">
31978  */
31979 /**
31980  * @class Roo.QuickTips
31981  * Provides attractive and customizable tooltips for any element.
31982  * @singleton
31983  */
31984 Roo.QuickTips = function(){
31985     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31986     var ce, bd, xy, dd;
31987     var visible = false, disabled = true, inited = false;
31988     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31989     
31990     var onOver = function(e){
31991         if(disabled){
31992             return;
31993         }
31994         var t = e.getTarget();
31995         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31996             return;
31997         }
31998         if(ce && t == ce.el){
31999             clearTimeout(hideProc);
32000             return;
32001         }
32002         if(t && tagEls[t.id]){
32003             tagEls[t.id].el = t;
32004             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32005             return;
32006         }
32007         var ttp, et = Roo.fly(t);
32008         var ns = cfg.namespace;
32009         if(tm.interceptTitles && t.title){
32010             ttp = t.title;
32011             t.qtip = ttp;
32012             t.removeAttribute("title");
32013             e.preventDefault();
32014         }else{
32015             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32016         }
32017         if(ttp){
32018             showProc = show.defer(tm.showDelay, tm, [{
32019                 el: t, 
32020                 text: ttp, 
32021                 width: et.getAttributeNS(ns, cfg.width),
32022                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32023                 title: et.getAttributeNS(ns, cfg.title),
32024                     cls: et.getAttributeNS(ns, cfg.cls)
32025             }]);
32026         }
32027     };
32028     
32029     var onOut = function(e){
32030         clearTimeout(showProc);
32031         var t = e.getTarget();
32032         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32033             hideProc = setTimeout(hide, tm.hideDelay);
32034         }
32035     };
32036     
32037     var onMove = function(e){
32038         if(disabled){
32039             return;
32040         }
32041         xy = e.getXY();
32042         xy[1] += 18;
32043         if(tm.trackMouse && ce){
32044             el.setXY(xy);
32045         }
32046     };
32047     
32048     var onDown = function(e){
32049         clearTimeout(showProc);
32050         clearTimeout(hideProc);
32051         if(!e.within(el)){
32052             if(tm.hideOnClick){
32053                 hide();
32054                 tm.disable();
32055                 tm.enable.defer(100, tm);
32056             }
32057         }
32058     };
32059     
32060     var getPad = function(){
32061         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32062     };
32063
32064     var show = function(o){
32065         if(disabled){
32066             return;
32067         }
32068         clearTimeout(dismissProc);
32069         ce = o;
32070         if(removeCls){ // in case manually hidden
32071             el.removeClass(removeCls);
32072             removeCls = null;
32073         }
32074         if(ce.cls){
32075             el.addClass(ce.cls);
32076             removeCls = ce.cls;
32077         }
32078         if(ce.title){
32079             tipTitle.update(ce.title);
32080             tipTitle.show();
32081         }else{
32082             tipTitle.update('');
32083             tipTitle.hide();
32084         }
32085         el.dom.style.width  = tm.maxWidth+'px';
32086         //tipBody.dom.style.width = '';
32087         tipBodyText.update(o.text);
32088         var p = getPad(), w = ce.width;
32089         if(!w){
32090             var td = tipBodyText.dom;
32091             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32092             if(aw > tm.maxWidth){
32093                 w = tm.maxWidth;
32094             }else if(aw < tm.minWidth){
32095                 w = tm.minWidth;
32096             }else{
32097                 w = aw;
32098             }
32099         }
32100         //tipBody.setWidth(w);
32101         el.setWidth(parseInt(w, 10) + p);
32102         if(ce.autoHide === false){
32103             close.setDisplayed(true);
32104             if(dd){
32105                 dd.unlock();
32106             }
32107         }else{
32108             close.setDisplayed(false);
32109             if(dd){
32110                 dd.lock();
32111             }
32112         }
32113         if(xy){
32114             el.avoidY = xy[1]-18;
32115             el.setXY(xy);
32116         }
32117         if(tm.animate){
32118             el.setOpacity(.1);
32119             el.setStyle("visibility", "visible");
32120             el.fadeIn({callback: afterShow});
32121         }else{
32122             afterShow();
32123         }
32124     };
32125     
32126     var afterShow = function(){
32127         if(ce){
32128             el.show();
32129             esc.enable();
32130             if(tm.autoDismiss && ce.autoHide !== false){
32131                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32132             }
32133         }
32134     };
32135     
32136     var hide = function(noanim){
32137         clearTimeout(dismissProc);
32138         clearTimeout(hideProc);
32139         ce = null;
32140         if(el.isVisible()){
32141             esc.disable();
32142             if(noanim !== true && tm.animate){
32143                 el.fadeOut({callback: afterHide});
32144             }else{
32145                 afterHide();
32146             } 
32147         }
32148     };
32149     
32150     var afterHide = function(){
32151         el.hide();
32152         if(removeCls){
32153             el.removeClass(removeCls);
32154             removeCls = null;
32155         }
32156     };
32157     
32158     return {
32159         /**
32160         * @cfg {Number} minWidth
32161         * The minimum width of the quick tip (defaults to 40)
32162         */
32163        minWidth : 40,
32164         /**
32165         * @cfg {Number} maxWidth
32166         * The maximum width of the quick tip (defaults to 300)
32167         */
32168        maxWidth : 300,
32169         /**
32170         * @cfg {Boolean} interceptTitles
32171         * True to automatically use the element's DOM title value if available (defaults to false)
32172         */
32173        interceptTitles : false,
32174         /**
32175         * @cfg {Boolean} trackMouse
32176         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32177         */
32178        trackMouse : false,
32179         /**
32180         * @cfg {Boolean} hideOnClick
32181         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32182         */
32183        hideOnClick : true,
32184         /**
32185         * @cfg {Number} showDelay
32186         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32187         */
32188        showDelay : 500,
32189         /**
32190         * @cfg {Number} hideDelay
32191         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32192         */
32193        hideDelay : 200,
32194         /**
32195         * @cfg {Boolean} autoHide
32196         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32197         * Used in conjunction with hideDelay.
32198         */
32199        autoHide : true,
32200         /**
32201         * @cfg {Boolean}
32202         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32203         * (defaults to true).  Used in conjunction with autoDismissDelay.
32204         */
32205        autoDismiss : true,
32206         /**
32207         * @cfg {Number}
32208         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32209         */
32210        autoDismissDelay : 5000,
32211        /**
32212         * @cfg {Boolean} animate
32213         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32214         */
32215        animate : false,
32216
32217        /**
32218         * @cfg {String} title
32219         * Title text to display (defaults to '').  This can be any valid HTML markup.
32220         */
32221         title: '',
32222        /**
32223         * @cfg {String} text
32224         * Body text to display (defaults to '').  This can be any valid HTML markup.
32225         */
32226         text : '',
32227        /**
32228         * @cfg {String} cls
32229         * A CSS class to apply to the base quick tip element (defaults to '').
32230         */
32231         cls : '',
32232        /**
32233         * @cfg {Number} width
32234         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32235         * minWidth or maxWidth.
32236         */
32237         width : null,
32238
32239     /**
32240      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32241      * or display QuickTips in a page.
32242      */
32243        init : function(){
32244           tm = Roo.QuickTips;
32245           cfg = tm.tagConfig;
32246           if(!inited){
32247               if(!Roo.isReady){ // allow calling of init() before onReady
32248                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32249                   return;
32250               }
32251               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32252               el.fxDefaults = {stopFx: true};
32253               // maximum custom styling
32254               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
32255               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
32256               tipTitle = el.child('h3');
32257               tipTitle.enableDisplayMode("block");
32258               tipBody = el.child('div.x-tip-bd');
32259               tipBodyText = el.child('div.x-tip-bd-inner');
32260               //bdLeft = el.child('div.x-tip-bd-left');
32261               //bdRight = el.child('div.x-tip-bd-right');
32262               close = el.child('div.x-tip-close');
32263               close.enableDisplayMode("block");
32264               close.on("click", hide);
32265               var d = Roo.get(document);
32266               d.on("mousedown", onDown);
32267               d.on("mouseover", onOver);
32268               d.on("mouseout", onOut);
32269               d.on("mousemove", onMove);
32270               esc = d.addKeyListener(27, hide);
32271               esc.disable();
32272               if(Roo.dd.DD){
32273                   dd = el.initDD("default", null, {
32274                       onDrag : function(){
32275                           el.sync();  
32276                       }
32277                   });
32278                   dd.setHandleElId(tipTitle.id);
32279                   dd.lock();
32280               }
32281               inited = true;
32282           }
32283           this.enable(); 
32284        },
32285
32286     /**
32287      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32288      * are supported:
32289      * <pre>
32290 Property    Type                   Description
32291 ----------  ---------------------  ------------------------------------------------------------------------
32292 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32293      * </ul>
32294      * @param {Object} config The config object
32295      */
32296        register : function(config){
32297            var cs = config instanceof Array ? config : arguments;
32298            for(var i = 0, len = cs.length; i < len; i++) {
32299                var c = cs[i];
32300                var target = c.target;
32301                if(target){
32302                    if(target instanceof Array){
32303                        for(var j = 0, jlen = target.length; j < jlen; j++){
32304                            tagEls[target[j]] = c;
32305                        }
32306                    }else{
32307                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32308                    }
32309                }
32310            }
32311        },
32312
32313     /**
32314      * Removes this quick tip from its element and destroys it.
32315      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32316      */
32317        unregister : function(el){
32318            delete tagEls[Roo.id(el)];
32319        },
32320
32321     /**
32322      * Enable this quick tip.
32323      */
32324        enable : function(){
32325            if(inited && disabled){
32326                locks.pop();
32327                if(locks.length < 1){
32328                    disabled = false;
32329                }
32330            }
32331        },
32332
32333     /**
32334      * Disable this quick tip.
32335      */
32336        disable : function(){
32337           disabled = true;
32338           clearTimeout(showProc);
32339           clearTimeout(hideProc);
32340           clearTimeout(dismissProc);
32341           if(ce){
32342               hide(true);
32343           }
32344           locks.push(1);
32345        },
32346
32347     /**
32348      * Returns true if the quick tip is enabled, else false.
32349      */
32350        isEnabled : function(){
32351             return !disabled;
32352        },
32353
32354         // private
32355        tagConfig : {
32356            namespace : "ext",
32357            attribute : "qtip",
32358            width : "width",
32359            target : "target",
32360            title : "qtitle",
32361            hide : "hide",
32362            cls : "qclass"
32363        }
32364    };
32365 }();
32366
32367 // backwards compat
32368 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32369  * Based on:
32370  * Ext JS Library 1.1.1
32371  * Copyright(c) 2006-2007, Ext JS, LLC.
32372  *
32373  * Originally Released Under LGPL - original licence link has changed is not relivant.
32374  *
32375  * Fork - LGPL
32376  * <script type="text/javascript">
32377  */
32378  
32379
32380 /**
32381  * @class Roo.tree.TreePanel
32382  * @extends Roo.data.Tree
32383
32384  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32385  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32386  * @cfg {Boolean} enableDD true to enable drag and drop
32387  * @cfg {Boolean} enableDrag true to enable just drag
32388  * @cfg {Boolean} enableDrop true to enable just drop
32389  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32390  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32391  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32392  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32393  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32394  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32395  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32396  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32397  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32398  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32399  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32400  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32401  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32402  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32403  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32404  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32405  * 
32406  * @constructor
32407  * @param {String/HTMLElement/Element} el The container element
32408  * @param {Object} config
32409  */
32410 Roo.tree.TreePanel = function(el, config){
32411     var root = false;
32412     var loader = false;
32413     if (config.root) {
32414         root = config.root;
32415         delete config.root;
32416     }
32417     if (config.loader) {
32418         loader = config.loader;
32419         delete config.loader;
32420     }
32421     
32422     Roo.apply(this, config);
32423     Roo.tree.TreePanel.superclass.constructor.call(this);
32424     this.el = Roo.get(el);
32425     this.el.addClass('x-tree');
32426     //console.log(root);
32427     if (root) {
32428         this.setRootNode( Roo.factory(root, Roo.tree));
32429     }
32430     if (loader) {
32431         this.loader = Roo.factory(loader, Roo.tree);
32432     }
32433    /**
32434     * Read-only. The id of the container element becomes this TreePanel's id.
32435     */
32436     this.id = this.el.id;
32437     this.addEvents({
32438         /**
32439         * @event beforeload
32440         * Fires before a node is loaded, return false to cancel
32441         * @param {Node} node The node being loaded
32442         */
32443         "beforeload" : true,
32444         /**
32445         * @event load
32446         * Fires when a node is loaded
32447         * @param {Node} node The node that was loaded
32448         */
32449         "load" : true,
32450         /**
32451         * @event textchange
32452         * Fires when the text for a node is changed
32453         * @param {Node} node The node
32454         * @param {String} text The new text
32455         * @param {String} oldText The old text
32456         */
32457         "textchange" : true,
32458         /**
32459         * @event beforeexpand
32460         * Fires before a node is expanded, return false to cancel.
32461         * @param {Node} node The node
32462         * @param {Boolean} deep
32463         * @param {Boolean} anim
32464         */
32465         "beforeexpand" : true,
32466         /**
32467         * @event beforecollapse
32468         * Fires before a node is collapsed, return false to cancel.
32469         * @param {Node} node The node
32470         * @param {Boolean} deep
32471         * @param {Boolean} anim
32472         */
32473         "beforecollapse" : true,
32474         /**
32475         * @event expand
32476         * Fires when a node is expanded
32477         * @param {Node} node The node
32478         */
32479         "expand" : true,
32480         /**
32481         * @event disabledchange
32482         * Fires when the disabled status of a node changes
32483         * @param {Node} node The node
32484         * @param {Boolean} disabled
32485         */
32486         "disabledchange" : true,
32487         /**
32488         * @event collapse
32489         * Fires when a node is collapsed
32490         * @param {Node} node The node
32491         */
32492         "collapse" : true,
32493         /**
32494         * @event beforeclick
32495         * Fires before click processing on a node. Return false to cancel the default action.
32496         * @param {Node} node The node
32497         * @param {Roo.EventObject} e The event object
32498         */
32499         "beforeclick":true,
32500         /**
32501         * @event checkchange
32502         * Fires when a node with a checkbox's checked property changes
32503         * @param {Node} this This node
32504         * @param {Boolean} checked
32505         */
32506         "checkchange":true,
32507         /**
32508         * @event click
32509         * Fires when a node is clicked
32510         * @param {Node} node The node
32511         * @param {Roo.EventObject} e The event object
32512         */
32513         "click":true,
32514         /**
32515         * @event dblclick
32516         * Fires when a node is double clicked
32517         * @param {Node} node The node
32518         * @param {Roo.EventObject} e The event object
32519         */
32520         "dblclick":true,
32521         /**
32522         * @event contextmenu
32523         * Fires when a node is right clicked
32524         * @param {Node} node The node
32525         * @param {Roo.EventObject} e The event object
32526         */
32527         "contextmenu":true,
32528         /**
32529         * @event beforechildrenrendered
32530         * Fires right before the child nodes for a node are rendered
32531         * @param {Node} node The node
32532         */
32533         "beforechildrenrendered":true,
32534         /**
32535         * @event startdrag
32536         * Fires when a node starts being dragged
32537         * @param {Roo.tree.TreePanel} this
32538         * @param {Roo.tree.TreeNode} node
32539         * @param {event} e The raw browser event
32540         */ 
32541        "startdrag" : true,
32542        /**
32543         * @event enddrag
32544         * Fires when a drag operation is complete
32545         * @param {Roo.tree.TreePanel} this
32546         * @param {Roo.tree.TreeNode} node
32547         * @param {event} e The raw browser event
32548         */
32549        "enddrag" : true,
32550        /**
32551         * @event dragdrop
32552         * Fires when a dragged node is dropped on a valid DD target
32553         * @param {Roo.tree.TreePanel} this
32554         * @param {Roo.tree.TreeNode} node
32555         * @param {DD} dd The dd it was dropped on
32556         * @param {event} e The raw browser event
32557         */
32558        "dragdrop" : true,
32559        /**
32560         * @event beforenodedrop
32561         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32562         * passed to handlers has the following properties:<br />
32563         * <ul style="padding:5px;padding-left:16px;">
32564         * <li>tree - The TreePanel</li>
32565         * <li>target - The node being targeted for the drop</li>
32566         * <li>data - The drag data from the drag source</li>
32567         * <li>point - The point of the drop - append, above or below</li>
32568         * <li>source - The drag source</li>
32569         * <li>rawEvent - Raw mouse event</li>
32570         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32571         * to be inserted by setting them on this object.</li>
32572         * <li>cancel - Set this to true to cancel the drop.</li>
32573         * </ul>
32574         * @param {Object} dropEvent
32575         */
32576        "beforenodedrop" : true,
32577        /**
32578         * @event nodedrop
32579         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32580         * passed to handlers has the following properties:<br />
32581         * <ul style="padding:5px;padding-left:16px;">
32582         * <li>tree - The TreePanel</li>
32583         * <li>target - The node being targeted for the drop</li>
32584         * <li>data - The drag data from the drag source</li>
32585         * <li>point - The point of the drop - append, above or below</li>
32586         * <li>source - The drag source</li>
32587         * <li>rawEvent - Raw mouse event</li>
32588         * <li>dropNode - Dropped node(s).</li>
32589         * </ul>
32590         * @param {Object} dropEvent
32591         */
32592        "nodedrop" : true,
32593         /**
32594         * @event nodedragover
32595         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32596         * passed to handlers has the following properties:<br />
32597         * <ul style="padding:5px;padding-left:16px;">
32598         * <li>tree - The TreePanel</li>
32599         * <li>target - The node being targeted for the drop</li>
32600         * <li>data - The drag data from the drag source</li>
32601         * <li>point - The point of the drop - append, above or below</li>
32602         * <li>source - The drag source</li>
32603         * <li>rawEvent - Raw mouse event</li>
32604         * <li>dropNode - Drop node(s) provided by the source.</li>
32605         * <li>cancel - Set this to true to signal drop not allowed.</li>
32606         * </ul>
32607         * @param {Object} dragOverEvent
32608         */
32609        "nodedragover" : true
32610         
32611     });
32612     if(this.singleExpand){
32613        this.on("beforeexpand", this.restrictExpand, this);
32614     }
32615     if (this.editor) {
32616         this.editor.tree = this;
32617         this.editor = Roo.factory(this.editor, Roo.tree);
32618     }
32619     
32620     if (this.selModel) {
32621         this.selModel = Roo.factory(this.selModel, Roo.tree);
32622     }
32623    
32624 };
32625 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32626     rootVisible : true,
32627     animate: Roo.enableFx,
32628     lines : true,
32629     enableDD : false,
32630     hlDrop : Roo.enableFx,
32631   
32632     renderer: false,
32633     
32634     rendererTip: false,
32635     // private
32636     restrictExpand : function(node){
32637         var p = node.parentNode;
32638         if(p){
32639             if(p.expandedChild && p.expandedChild.parentNode == p){
32640                 p.expandedChild.collapse();
32641             }
32642             p.expandedChild = node;
32643         }
32644     },
32645
32646     // private override
32647     setRootNode : function(node){
32648         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32649         if(!this.rootVisible){
32650             node.ui = new Roo.tree.RootTreeNodeUI(node);
32651         }
32652         return node;
32653     },
32654
32655     /**
32656      * Returns the container element for this TreePanel
32657      */
32658     getEl : function(){
32659         return this.el;
32660     },
32661
32662     /**
32663      * Returns the default TreeLoader for this TreePanel
32664      */
32665     getLoader : function(){
32666         return this.loader;
32667     },
32668
32669     /**
32670      * Expand all nodes
32671      */
32672     expandAll : function(){
32673         this.root.expand(true);
32674     },
32675
32676     /**
32677      * Collapse all nodes
32678      */
32679     collapseAll : function(){
32680         this.root.collapse(true);
32681     },
32682
32683     /**
32684      * Returns the selection model used by this TreePanel
32685      */
32686     getSelectionModel : function(){
32687         if(!this.selModel){
32688             this.selModel = new Roo.tree.DefaultSelectionModel();
32689         }
32690         return this.selModel;
32691     },
32692
32693     /**
32694      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32695      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32696      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32697      * @return {Array}
32698      */
32699     getChecked : function(a, startNode){
32700         startNode = startNode || this.root;
32701         var r = [];
32702         var f = function(){
32703             if(this.attributes.checked){
32704                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32705             }
32706         }
32707         startNode.cascade(f);
32708         return r;
32709     },
32710
32711     /**
32712      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32713      * @param {String} path
32714      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32715      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32716      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32717      */
32718     expandPath : function(path, attr, callback){
32719         attr = attr || "id";
32720         var keys = path.split(this.pathSeparator);
32721         var curNode = this.root;
32722         if(curNode.attributes[attr] != keys[1]){ // invalid root
32723             if(callback){
32724                 callback(false, null);
32725             }
32726             return;
32727         }
32728         var index = 1;
32729         var f = function(){
32730             if(++index == keys.length){
32731                 if(callback){
32732                     callback(true, curNode);
32733                 }
32734                 return;
32735             }
32736             var c = curNode.findChild(attr, keys[index]);
32737             if(!c){
32738                 if(callback){
32739                     callback(false, curNode);
32740                 }
32741                 return;
32742             }
32743             curNode = c;
32744             c.expand(false, false, f);
32745         };
32746         curNode.expand(false, false, f);
32747     },
32748
32749     /**
32750      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32751      * @param {String} path
32752      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32753      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32754      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32755      */
32756     selectPath : function(path, attr, callback){
32757         attr = attr || "id";
32758         var keys = path.split(this.pathSeparator);
32759         var v = keys.pop();
32760         if(keys.length > 0){
32761             var f = function(success, node){
32762                 if(success && node){
32763                     var n = node.findChild(attr, v);
32764                     if(n){
32765                         n.select();
32766                         if(callback){
32767                             callback(true, n);
32768                         }
32769                     }else if(callback){
32770                         callback(false, n);
32771                     }
32772                 }else{
32773                     if(callback){
32774                         callback(false, n);
32775                     }
32776                 }
32777             };
32778             this.expandPath(keys.join(this.pathSeparator), attr, f);
32779         }else{
32780             this.root.select();
32781             if(callback){
32782                 callback(true, this.root);
32783             }
32784         }
32785     },
32786
32787     getTreeEl : function(){
32788         return this.el;
32789     },
32790
32791     /**
32792      * Trigger rendering of this TreePanel
32793      */
32794     render : function(){
32795         if (this.innerCt) {
32796             return this; // stop it rendering more than once!!
32797         }
32798         
32799         this.innerCt = this.el.createChild({tag:"ul",
32800                cls:"x-tree-root-ct " +
32801                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32802
32803         if(this.containerScroll){
32804             Roo.dd.ScrollManager.register(this.el);
32805         }
32806         if((this.enableDD || this.enableDrop) && !this.dropZone){
32807            /**
32808             * The dropZone used by this tree if drop is enabled
32809             * @type Roo.tree.TreeDropZone
32810             */
32811              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32812                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32813            });
32814         }
32815         if((this.enableDD || this.enableDrag) && !this.dragZone){
32816            /**
32817             * The dragZone used by this tree if drag is enabled
32818             * @type Roo.tree.TreeDragZone
32819             */
32820             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32821                ddGroup: this.ddGroup || "TreeDD",
32822                scroll: this.ddScroll
32823            });
32824         }
32825         this.getSelectionModel().init(this);
32826         if (!this.root) {
32827             Roo.log("ROOT not set in tree");
32828             return this;
32829         }
32830         this.root.render();
32831         if(!this.rootVisible){
32832             this.root.renderChildren();
32833         }
32834         return this;
32835     }
32836 });/*
32837  * Based on:
32838  * Ext JS Library 1.1.1
32839  * Copyright(c) 2006-2007, Ext JS, LLC.
32840  *
32841  * Originally Released Under LGPL - original licence link has changed is not relivant.
32842  *
32843  * Fork - LGPL
32844  * <script type="text/javascript">
32845  */
32846  
32847
32848 /**
32849  * @class Roo.tree.DefaultSelectionModel
32850  * @extends Roo.util.Observable
32851  * The default single selection for a TreePanel.
32852  * @param {Object} cfg Configuration
32853  */
32854 Roo.tree.DefaultSelectionModel = function(cfg){
32855    this.selNode = null;
32856    
32857    
32858    
32859    this.addEvents({
32860        /**
32861         * @event selectionchange
32862         * Fires when the selected node changes
32863         * @param {DefaultSelectionModel} this
32864         * @param {TreeNode} node the new selection
32865         */
32866        "selectionchange" : true,
32867
32868        /**
32869         * @event beforeselect
32870         * Fires before the selected node changes, return false to cancel the change
32871         * @param {DefaultSelectionModel} this
32872         * @param {TreeNode} node the new selection
32873         * @param {TreeNode} node the old selection
32874         */
32875        "beforeselect" : true
32876    });
32877    
32878     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32879 };
32880
32881 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32882     init : function(tree){
32883         this.tree = tree;
32884         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32885         tree.on("click", this.onNodeClick, this);
32886     },
32887     
32888     onNodeClick : function(node, e){
32889         if (e.ctrlKey && this.selNode == node)  {
32890             this.unselect(node);
32891             return;
32892         }
32893         this.select(node);
32894     },
32895     
32896     /**
32897      * Select a node.
32898      * @param {TreeNode} node The node to select
32899      * @return {TreeNode} The selected node
32900      */
32901     select : function(node){
32902         var last = this.selNode;
32903         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32904             if(last){
32905                 last.ui.onSelectedChange(false);
32906             }
32907             this.selNode = node;
32908             node.ui.onSelectedChange(true);
32909             this.fireEvent("selectionchange", this, node, last);
32910         }
32911         return node;
32912     },
32913     
32914     /**
32915      * Deselect a node.
32916      * @param {TreeNode} node The node to unselect
32917      */
32918     unselect : function(node){
32919         if(this.selNode == node){
32920             this.clearSelections();
32921         }    
32922     },
32923     
32924     /**
32925      * Clear all selections
32926      */
32927     clearSelections : function(){
32928         var n = this.selNode;
32929         if(n){
32930             n.ui.onSelectedChange(false);
32931             this.selNode = null;
32932             this.fireEvent("selectionchange", this, null);
32933         }
32934         return n;
32935     },
32936     
32937     /**
32938      * Get the selected node
32939      * @return {TreeNode} The selected node
32940      */
32941     getSelectedNode : function(){
32942         return this.selNode;    
32943     },
32944     
32945     /**
32946      * Returns true if the node is selected
32947      * @param {TreeNode} node The node to check
32948      * @return {Boolean}
32949      */
32950     isSelected : function(node){
32951         return this.selNode == node;  
32952     },
32953
32954     /**
32955      * Selects the node above the selected node in the tree, intelligently walking the nodes
32956      * @return TreeNode The new selection
32957      */
32958     selectPrevious : function(){
32959         var s = this.selNode || this.lastSelNode;
32960         if(!s){
32961             return null;
32962         }
32963         var ps = s.previousSibling;
32964         if(ps){
32965             if(!ps.isExpanded() || ps.childNodes.length < 1){
32966                 return this.select(ps);
32967             } else{
32968                 var lc = ps.lastChild;
32969                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32970                     lc = lc.lastChild;
32971                 }
32972                 return this.select(lc);
32973             }
32974         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32975             return this.select(s.parentNode);
32976         }
32977         return null;
32978     },
32979
32980     /**
32981      * Selects the node above the selected node in the tree, intelligently walking the nodes
32982      * @return TreeNode The new selection
32983      */
32984     selectNext : function(){
32985         var s = this.selNode || this.lastSelNode;
32986         if(!s){
32987             return null;
32988         }
32989         if(s.firstChild && s.isExpanded()){
32990              return this.select(s.firstChild);
32991          }else if(s.nextSibling){
32992              return this.select(s.nextSibling);
32993          }else if(s.parentNode){
32994             var newS = null;
32995             s.parentNode.bubble(function(){
32996                 if(this.nextSibling){
32997                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32998                     return false;
32999                 }
33000             });
33001             return newS;
33002          }
33003         return null;
33004     },
33005
33006     onKeyDown : function(e){
33007         var s = this.selNode || this.lastSelNode;
33008         // undesirable, but required
33009         var sm = this;
33010         if(!s){
33011             return;
33012         }
33013         var k = e.getKey();
33014         switch(k){
33015              case e.DOWN:
33016                  e.stopEvent();
33017                  this.selectNext();
33018              break;
33019              case e.UP:
33020                  e.stopEvent();
33021                  this.selectPrevious();
33022              break;
33023              case e.RIGHT:
33024                  e.preventDefault();
33025                  if(s.hasChildNodes()){
33026                      if(!s.isExpanded()){
33027                          s.expand();
33028                      }else if(s.firstChild){
33029                          this.select(s.firstChild, e);
33030                      }
33031                  }
33032              break;
33033              case e.LEFT:
33034                  e.preventDefault();
33035                  if(s.hasChildNodes() && s.isExpanded()){
33036                      s.collapse();
33037                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33038                      this.select(s.parentNode, e);
33039                  }
33040              break;
33041         };
33042     }
33043 });
33044
33045 /**
33046  * @class Roo.tree.MultiSelectionModel
33047  * @extends Roo.util.Observable
33048  * Multi selection for a TreePanel.
33049  * @param {Object} cfg Configuration
33050  */
33051 Roo.tree.MultiSelectionModel = function(){
33052    this.selNodes = [];
33053    this.selMap = {};
33054    this.addEvents({
33055        /**
33056         * @event selectionchange
33057         * Fires when the selected nodes change
33058         * @param {MultiSelectionModel} this
33059         * @param {Array} nodes Array of the selected nodes
33060         */
33061        "selectionchange" : true
33062    });
33063    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33064    
33065 };
33066
33067 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33068     init : function(tree){
33069         this.tree = tree;
33070         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33071         tree.on("click", this.onNodeClick, this);
33072     },
33073     
33074     onNodeClick : function(node, e){
33075         this.select(node, e, e.ctrlKey);
33076     },
33077     
33078     /**
33079      * Select a node.
33080      * @param {TreeNode} node The node to select
33081      * @param {EventObject} e (optional) An event associated with the selection
33082      * @param {Boolean} keepExisting True to retain existing selections
33083      * @return {TreeNode} The selected node
33084      */
33085     select : function(node, e, keepExisting){
33086         if(keepExisting !== true){
33087             this.clearSelections(true);
33088         }
33089         if(this.isSelected(node)){
33090             this.lastSelNode = node;
33091             return node;
33092         }
33093         this.selNodes.push(node);
33094         this.selMap[node.id] = node;
33095         this.lastSelNode = node;
33096         node.ui.onSelectedChange(true);
33097         this.fireEvent("selectionchange", this, this.selNodes);
33098         return node;
33099     },
33100     
33101     /**
33102      * Deselect a node.
33103      * @param {TreeNode} node The node to unselect
33104      */
33105     unselect : function(node){
33106         if(this.selMap[node.id]){
33107             node.ui.onSelectedChange(false);
33108             var sn = this.selNodes;
33109             var index = -1;
33110             if(sn.indexOf){
33111                 index = sn.indexOf(node);
33112             }else{
33113                 for(var i = 0, len = sn.length; i < len; i++){
33114                     if(sn[i] == node){
33115                         index = i;
33116                         break;
33117                     }
33118                 }
33119             }
33120             if(index != -1){
33121                 this.selNodes.splice(index, 1);
33122             }
33123             delete this.selMap[node.id];
33124             this.fireEvent("selectionchange", this, this.selNodes);
33125         }
33126     },
33127     
33128     /**
33129      * Clear all selections
33130      */
33131     clearSelections : function(suppressEvent){
33132         var sn = this.selNodes;
33133         if(sn.length > 0){
33134             for(var i = 0, len = sn.length; i < len; i++){
33135                 sn[i].ui.onSelectedChange(false);
33136             }
33137             this.selNodes = [];
33138             this.selMap = {};
33139             if(suppressEvent !== true){
33140                 this.fireEvent("selectionchange", this, this.selNodes);
33141             }
33142         }
33143     },
33144     
33145     /**
33146      * Returns true if the node is selected
33147      * @param {TreeNode} node The node to check
33148      * @return {Boolean}
33149      */
33150     isSelected : function(node){
33151         return this.selMap[node.id] ? true : false;  
33152     },
33153     
33154     /**
33155      * Returns an array of the selected nodes
33156      * @return {Array}
33157      */
33158     getSelectedNodes : function(){
33159         return this.selNodes;    
33160     },
33161
33162     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33163
33164     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33165
33166     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33167 });/*
33168  * Based on:
33169  * Ext JS Library 1.1.1
33170  * Copyright(c) 2006-2007, Ext JS, LLC.
33171  *
33172  * Originally Released Under LGPL - original licence link has changed is not relivant.
33173  *
33174  * Fork - LGPL
33175  * <script type="text/javascript">
33176  */
33177  
33178 /**
33179  * @class Roo.tree.TreeNode
33180  * @extends Roo.data.Node
33181  * @cfg {String} text The text for this node
33182  * @cfg {Boolean} expanded true to start the node expanded
33183  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33184  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33185  * @cfg {Boolean} disabled true to start the node disabled
33186  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33187  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33188  * @cfg {String} cls A css class to be added to the node
33189  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33190  * @cfg {String} href URL of the link used for the node (defaults to #)
33191  * @cfg {String} hrefTarget target frame for the link
33192  * @cfg {String} qtip An Ext QuickTip for the node
33193  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33194  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33195  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33196  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33197  * (defaults to undefined with no checkbox rendered)
33198  * @constructor
33199  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33200  */
33201 Roo.tree.TreeNode = function(attributes){
33202     attributes = attributes || {};
33203     if(typeof attributes == "string"){
33204         attributes = {text: attributes};
33205     }
33206     this.childrenRendered = false;
33207     this.rendered = false;
33208     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33209     this.expanded = attributes.expanded === true;
33210     this.isTarget = attributes.isTarget !== false;
33211     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33212     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33213
33214     /**
33215      * Read-only. The text for this node. To change it use setText().
33216      * @type String
33217      */
33218     this.text = attributes.text;
33219     /**
33220      * True if this node is disabled.
33221      * @type Boolean
33222      */
33223     this.disabled = attributes.disabled === true;
33224
33225     this.addEvents({
33226         /**
33227         * @event textchange
33228         * Fires when the text for this node is changed
33229         * @param {Node} this This node
33230         * @param {String} text The new text
33231         * @param {String} oldText The old text
33232         */
33233         "textchange" : true,
33234         /**
33235         * @event beforeexpand
33236         * Fires before this node is expanded, return false to cancel.
33237         * @param {Node} this This node
33238         * @param {Boolean} deep
33239         * @param {Boolean} anim
33240         */
33241         "beforeexpand" : true,
33242         /**
33243         * @event beforecollapse
33244         * Fires before this node is collapsed, return false to cancel.
33245         * @param {Node} this This node
33246         * @param {Boolean} deep
33247         * @param {Boolean} anim
33248         */
33249         "beforecollapse" : true,
33250         /**
33251         * @event expand
33252         * Fires when this node is expanded
33253         * @param {Node} this This node
33254         */
33255         "expand" : true,
33256         /**
33257         * @event disabledchange
33258         * Fires when the disabled status of this node changes
33259         * @param {Node} this This node
33260         * @param {Boolean} disabled
33261         */
33262         "disabledchange" : true,
33263         /**
33264         * @event collapse
33265         * Fires when this node is collapsed
33266         * @param {Node} this This node
33267         */
33268         "collapse" : true,
33269         /**
33270         * @event beforeclick
33271         * Fires before click processing. Return false to cancel the default action.
33272         * @param {Node} this This node
33273         * @param {Roo.EventObject} e The event object
33274         */
33275         "beforeclick":true,
33276         /**
33277         * @event checkchange
33278         * Fires when a node with a checkbox's checked property changes
33279         * @param {Node} this This node
33280         * @param {Boolean} checked
33281         */
33282         "checkchange":true,
33283         /**
33284         * @event click
33285         * Fires when this node is clicked
33286         * @param {Node} this This node
33287         * @param {Roo.EventObject} e The event object
33288         */
33289         "click":true,
33290         /**
33291         * @event dblclick
33292         * Fires when this node is double clicked
33293         * @param {Node} this This node
33294         * @param {Roo.EventObject} e The event object
33295         */
33296         "dblclick":true,
33297         /**
33298         * @event contextmenu
33299         * Fires when this node is right clicked
33300         * @param {Node} this This node
33301         * @param {Roo.EventObject} e The event object
33302         */
33303         "contextmenu":true,
33304         /**
33305         * @event beforechildrenrendered
33306         * Fires right before the child nodes for this node are rendered
33307         * @param {Node} this This node
33308         */
33309         "beforechildrenrendered":true
33310     });
33311
33312     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33313
33314     /**
33315      * Read-only. The UI for this node
33316      * @type TreeNodeUI
33317      */
33318     this.ui = new uiClass(this);
33319     
33320     // finally support items[]
33321     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33322         return;
33323     }
33324     
33325     
33326     Roo.each(this.attributes.items, function(c) {
33327         this.appendChild(Roo.factory(c,Roo.Tree));
33328     }, this);
33329     delete this.attributes.items;
33330     
33331     
33332     
33333 };
33334 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33335     preventHScroll: true,
33336     /**
33337      * Returns true if this node is expanded
33338      * @return {Boolean}
33339      */
33340     isExpanded : function(){
33341         return this.expanded;
33342     },
33343
33344     /**
33345      * Returns the UI object for this node
33346      * @return {TreeNodeUI}
33347      */
33348     getUI : function(){
33349         return this.ui;
33350     },
33351
33352     // private override
33353     setFirstChild : function(node){
33354         var of = this.firstChild;
33355         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33356         if(this.childrenRendered && of && node != of){
33357             of.renderIndent(true, true);
33358         }
33359         if(this.rendered){
33360             this.renderIndent(true, true);
33361         }
33362     },
33363
33364     // private override
33365     setLastChild : function(node){
33366         var ol = this.lastChild;
33367         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33368         if(this.childrenRendered && ol && node != ol){
33369             ol.renderIndent(true, true);
33370         }
33371         if(this.rendered){
33372             this.renderIndent(true, true);
33373         }
33374     },
33375
33376     // these methods are overridden to provide lazy rendering support
33377     // private override
33378     appendChild : function()
33379     {
33380         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33381         if(node && this.childrenRendered){
33382             node.render();
33383         }
33384         this.ui.updateExpandIcon();
33385         return node;
33386     },
33387
33388     // private override
33389     removeChild : function(node){
33390         this.ownerTree.getSelectionModel().unselect(node);
33391         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33392         // if it's been rendered remove dom node
33393         if(this.childrenRendered){
33394             node.ui.remove();
33395         }
33396         if(this.childNodes.length < 1){
33397             this.collapse(false, false);
33398         }else{
33399             this.ui.updateExpandIcon();
33400         }
33401         if(!this.firstChild) {
33402             this.childrenRendered = false;
33403         }
33404         return node;
33405     },
33406
33407     // private override
33408     insertBefore : function(node, refNode){
33409         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33410         if(newNode && refNode && this.childrenRendered){
33411             node.render();
33412         }
33413         this.ui.updateExpandIcon();
33414         return newNode;
33415     },
33416
33417     /**
33418      * Sets the text for this node
33419      * @param {String} text
33420      */
33421     setText : function(text){
33422         var oldText = this.text;
33423         this.text = text;
33424         this.attributes.text = text;
33425         if(this.rendered){ // event without subscribing
33426             this.ui.onTextChange(this, text, oldText);
33427         }
33428         this.fireEvent("textchange", this, text, oldText);
33429     },
33430
33431     /**
33432      * Triggers selection of this node
33433      */
33434     select : function(){
33435         this.getOwnerTree().getSelectionModel().select(this);
33436     },
33437
33438     /**
33439      * Triggers deselection of this node
33440      */
33441     unselect : function(){
33442         this.getOwnerTree().getSelectionModel().unselect(this);
33443     },
33444
33445     /**
33446      * Returns true if this node is selected
33447      * @return {Boolean}
33448      */
33449     isSelected : function(){
33450         return this.getOwnerTree().getSelectionModel().isSelected(this);
33451     },
33452
33453     /**
33454      * Expand this node.
33455      * @param {Boolean} deep (optional) True to expand all children as well
33456      * @param {Boolean} anim (optional) false to cancel the default animation
33457      * @param {Function} callback (optional) A callback to be called when
33458      * expanding this node completes (does not wait for deep expand to complete).
33459      * Called with 1 parameter, this node.
33460      */
33461     expand : function(deep, anim, callback){
33462         if(!this.expanded){
33463             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33464                 return;
33465             }
33466             if(!this.childrenRendered){
33467                 this.renderChildren();
33468             }
33469             this.expanded = true;
33470             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33471                 this.ui.animExpand(function(){
33472                     this.fireEvent("expand", this);
33473                     if(typeof callback == "function"){
33474                         callback(this);
33475                     }
33476                     if(deep === true){
33477                         this.expandChildNodes(true);
33478                     }
33479                 }.createDelegate(this));
33480                 return;
33481             }else{
33482                 this.ui.expand();
33483                 this.fireEvent("expand", this);
33484                 if(typeof callback == "function"){
33485                     callback(this);
33486                 }
33487             }
33488         }else{
33489            if(typeof callback == "function"){
33490                callback(this);
33491            }
33492         }
33493         if(deep === true){
33494             this.expandChildNodes(true);
33495         }
33496     },
33497
33498     isHiddenRoot : function(){
33499         return this.isRoot && !this.getOwnerTree().rootVisible;
33500     },
33501
33502     /**
33503      * Collapse this node.
33504      * @param {Boolean} deep (optional) True to collapse all children as well
33505      * @param {Boolean} anim (optional) false to cancel the default animation
33506      */
33507     collapse : function(deep, anim){
33508         if(this.expanded && !this.isHiddenRoot()){
33509             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33510                 return;
33511             }
33512             this.expanded = false;
33513             if((this.getOwnerTree().animate && anim !== false) || anim){
33514                 this.ui.animCollapse(function(){
33515                     this.fireEvent("collapse", this);
33516                     if(deep === true){
33517                         this.collapseChildNodes(true);
33518                     }
33519                 }.createDelegate(this));
33520                 return;
33521             }else{
33522                 this.ui.collapse();
33523                 this.fireEvent("collapse", this);
33524             }
33525         }
33526         if(deep === true){
33527             var cs = this.childNodes;
33528             for(var i = 0, len = cs.length; i < len; i++) {
33529                 cs[i].collapse(true, false);
33530             }
33531         }
33532     },
33533
33534     // private
33535     delayedExpand : function(delay){
33536         if(!this.expandProcId){
33537             this.expandProcId = this.expand.defer(delay, this);
33538         }
33539     },
33540
33541     // private
33542     cancelExpand : function(){
33543         if(this.expandProcId){
33544             clearTimeout(this.expandProcId);
33545         }
33546         this.expandProcId = false;
33547     },
33548
33549     /**
33550      * Toggles expanded/collapsed state of the node
33551      */
33552     toggle : function(){
33553         if(this.expanded){
33554             this.collapse();
33555         }else{
33556             this.expand();
33557         }
33558     },
33559
33560     /**
33561      * Ensures all parent nodes are expanded
33562      */
33563     ensureVisible : function(callback){
33564         var tree = this.getOwnerTree();
33565         tree.expandPath(this.parentNode.getPath(), false, function(){
33566             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33567             Roo.callback(callback);
33568         }.createDelegate(this));
33569     },
33570
33571     /**
33572      * Expand all child nodes
33573      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33574      */
33575     expandChildNodes : function(deep){
33576         var cs = this.childNodes;
33577         for(var i = 0, len = cs.length; i < len; i++) {
33578                 cs[i].expand(deep);
33579         }
33580     },
33581
33582     /**
33583      * Collapse all child nodes
33584      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33585      */
33586     collapseChildNodes : function(deep){
33587         var cs = this.childNodes;
33588         for(var i = 0, len = cs.length; i < len; i++) {
33589                 cs[i].collapse(deep);
33590         }
33591     },
33592
33593     /**
33594      * Disables this node
33595      */
33596     disable : function(){
33597         this.disabled = true;
33598         this.unselect();
33599         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33600             this.ui.onDisableChange(this, true);
33601         }
33602         this.fireEvent("disabledchange", this, true);
33603     },
33604
33605     /**
33606      * Enables this node
33607      */
33608     enable : function(){
33609         this.disabled = false;
33610         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33611             this.ui.onDisableChange(this, false);
33612         }
33613         this.fireEvent("disabledchange", this, false);
33614     },
33615
33616     // private
33617     renderChildren : function(suppressEvent){
33618         if(suppressEvent !== false){
33619             this.fireEvent("beforechildrenrendered", this);
33620         }
33621         var cs = this.childNodes;
33622         for(var i = 0, len = cs.length; i < len; i++){
33623             cs[i].render(true);
33624         }
33625         this.childrenRendered = true;
33626     },
33627
33628     // private
33629     sort : function(fn, scope){
33630         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33631         if(this.childrenRendered){
33632             var cs = this.childNodes;
33633             for(var i = 0, len = cs.length; i < len; i++){
33634                 cs[i].render(true);
33635             }
33636         }
33637     },
33638
33639     // private
33640     render : function(bulkRender){
33641         this.ui.render(bulkRender);
33642         if(!this.rendered){
33643             this.rendered = true;
33644             if(this.expanded){
33645                 this.expanded = false;
33646                 this.expand(false, false);
33647             }
33648         }
33649     },
33650
33651     // private
33652     renderIndent : function(deep, refresh){
33653         if(refresh){
33654             this.ui.childIndent = null;
33655         }
33656         this.ui.renderIndent();
33657         if(deep === true && this.childrenRendered){
33658             var cs = this.childNodes;
33659             for(var i = 0, len = cs.length; i < len; i++){
33660                 cs[i].renderIndent(true, refresh);
33661             }
33662         }
33663     }
33664 });/*
33665  * Based on:
33666  * Ext JS Library 1.1.1
33667  * Copyright(c) 2006-2007, Ext JS, LLC.
33668  *
33669  * Originally Released Under LGPL - original licence link has changed is not relivant.
33670  *
33671  * Fork - LGPL
33672  * <script type="text/javascript">
33673  */
33674  
33675 /**
33676  * @class Roo.tree.AsyncTreeNode
33677  * @extends Roo.tree.TreeNode
33678  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33679  * @constructor
33680  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33681  */
33682  Roo.tree.AsyncTreeNode = function(config){
33683     this.loaded = false;
33684     this.loading = false;
33685     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33686     /**
33687     * @event beforeload
33688     * Fires before this node is loaded, return false to cancel
33689     * @param {Node} this This node
33690     */
33691     this.addEvents({'beforeload':true, 'load': true});
33692     /**
33693     * @event load
33694     * Fires when this node is loaded
33695     * @param {Node} this This node
33696     */
33697     /**
33698      * The loader used by this node (defaults to using the tree's defined loader)
33699      * @type TreeLoader
33700      * @property loader
33701      */
33702 };
33703 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33704     expand : function(deep, anim, callback){
33705         if(this.loading){ // if an async load is already running, waiting til it's done
33706             var timer;
33707             var f = function(){
33708                 if(!this.loading){ // done loading
33709                     clearInterval(timer);
33710                     this.expand(deep, anim, callback);
33711                 }
33712             }.createDelegate(this);
33713             timer = setInterval(f, 200);
33714             return;
33715         }
33716         if(!this.loaded){
33717             if(this.fireEvent("beforeload", this) === false){
33718                 return;
33719             }
33720             this.loading = true;
33721             this.ui.beforeLoad(this);
33722             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33723             if(loader){
33724                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33725                 return;
33726             }
33727         }
33728         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33729     },
33730     
33731     /**
33732      * Returns true if this node is currently loading
33733      * @return {Boolean}
33734      */
33735     isLoading : function(){
33736         return this.loading;  
33737     },
33738     
33739     loadComplete : function(deep, anim, callback){
33740         this.loading = false;
33741         this.loaded = true;
33742         this.ui.afterLoad(this);
33743         this.fireEvent("load", this);
33744         this.expand(deep, anim, callback);
33745     },
33746     
33747     /**
33748      * Returns true if this node has been loaded
33749      * @return {Boolean}
33750      */
33751     isLoaded : function(){
33752         return this.loaded;
33753     },
33754     
33755     hasChildNodes : function(){
33756         if(!this.isLeaf() && !this.loaded){
33757             return true;
33758         }else{
33759             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33760         }
33761     },
33762
33763     /**
33764      * Trigger a reload for this node
33765      * @param {Function} callback
33766      */
33767     reload : function(callback){
33768         this.collapse(false, false);
33769         while(this.firstChild){
33770             this.removeChild(this.firstChild);
33771         }
33772         this.childrenRendered = false;
33773         this.loaded = false;
33774         if(this.isHiddenRoot()){
33775             this.expanded = false;
33776         }
33777         this.expand(false, false, callback);
33778     }
33779 });/*
33780  * Based on:
33781  * Ext JS Library 1.1.1
33782  * Copyright(c) 2006-2007, Ext JS, LLC.
33783  *
33784  * Originally Released Under LGPL - original licence link has changed is not relivant.
33785  *
33786  * Fork - LGPL
33787  * <script type="text/javascript">
33788  */
33789  
33790 /**
33791  * @class Roo.tree.TreeNodeUI
33792  * @constructor
33793  * @param {Object} node The node to render
33794  * The TreeNode UI implementation is separate from the
33795  * tree implementation. Unless you are customizing the tree UI,
33796  * you should never have to use this directly.
33797  */
33798 Roo.tree.TreeNodeUI = function(node){
33799     this.node = node;
33800     this.rendered = false;
33801     this.animating = false;
33802     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33803 };
33804
33805 Roo.tree.TreeNodeUI.prototype = {
33806     removeChild : function(node){
33807         if(this.rendered){
33808             this.ctNode.removeChild(node.ui.getEl());
33809         }
33810     },
33811
33812     beforeLoad : function(){
33813          this.addClass("x-tree-node-loading");
33814     },
33815
33816     afterLoad : function(){
33817          this.removeClass("x-tree-node-loading");
33818     },
33819
33820     onTextChange : function(node, text, oldText){
33821         if(this.rendered){
33822             this.textNode.innerHTML = text;
33823         }
33824     },
33825
33826     onDisableChange : function(node, state){
33827         this.disabled = state;
33828         if(state){
33829             this.addClass("x-tree-node-disabled");
33830         }else{
33831             this.removeClass("x-tree-node-disabled");
33832         }
33833     },
33834
33835     onSelectedChange : function(state){
33836         if(state){
33837             this.focus();
33838             this.addClass("x-tree-selected");
33839         }else{
33840             //this.blur();
33841             this.removeClass("x-tree-selected");
33842         }
33843     },
33844
33845     onMove : function(tree, node, oldParent, newParent, index, refNode){
33846         this.childIndent = null;
33847         if(this.rendered){
33848             var targetNode = newParent.ui.getContainer();
33849             if(!targetNode){//target not rendered
33850                 this.holder = document.createElement("div");
33851                 this.holder.appendChild(this.wrap);
33852                 return;
33853             }
33854             var insertBefore = refNode ? refNode.ui.getEl() : null;
33855             if(insertBefore){
33856                 targetNode.insertBefore(this.wrap, insertBefore);
33857             }else{
33858                 targetNode.appendChild(this.wrap);
33859             }
33860             this.node.renderIndent(true);
33861         }
33862     },
33863
33864     addClass : function(cls){
33865         if(this.elNode){
33866             Roo.fly(this.elNode).addClass(cls);
33867         }
33868     },
33869
33870     removeClass : function(cls){
33871         if(this.elNode){
33872             Roo.fly(this.elNode).removeClass(cls);
33873         }
33874     },
33875
33876     remove : function(){
33877         if(this.rendered){
33878             this.holder = document.createElement("div");
33879             this.holder.appendChild(this.wrap);
33880         }
33881     },
33882
33883     fireEvent : function(){
33884         return this.node.fireEvent.apply(this.node, arguments);
33885     },
33886
33887     initEvents : function(){
33888         this.node.on("move", this.onMove, this);
33889         var E = Roo.EventManager;
33890         var a = this.anchor;
33891
33892         var el = Roo.fly(a, '_treeui');
33893
33894         if(Roo.isOpera){ // opera render bug ignores the CSS
33895             el.setStyle("text-decoration", "none");
33896         }
33897
33898         el.on("click", this.onClick, this);
33899         el.on("dblclick", this.onDblClick, this);
33900
33901         if(this.checkbox){
33902             Roo.EventManager.on(this.checkbox,
33903                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33904         }
33905
33906         el.on("contextmenu", this.onContextMenu, this);
33907
33908         var icon = Roo.fly(this.iconNode);
33909         icon.on("click", this.onClick, this);
33910         icon.on("dblclick", this.onDblClick, this);
33911         icon.on("contextmenu", this.onContextMenu, this);
33912         E.on(this.ecNode, "click", this.ecClick, this, true);
33913
33914         if(this.node.disabled){
33915             this.addClass("x-tree-node-disabled");
33916         }
33917         if(this.node.hidden){
33918             this.addClass("x-tree-node-disabled");
33919         }
33920         var ot = this.node.getOwnerTree();
33921         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33922         if(dd && (!this.node.isRoot || ot.rootVisible)){
33923             Roo.dd.Registry.register(this.elNode, {
33924                 node: this.node,
33925                 handles: this.getDDHandles(),
33926                 isHandle: false
33927             });
33928         }
33929     },
33930
33931     getDDHandles : function(){
33932         return [this.iconNode, this.textNode];
33933     },
33934
33935     hide : function(){
33936         if(this.rendered){
33937             this.wrap.style.display = "none";
33938         }
33939     },
33940
33941     show : function(){
33942         if(this.rendered){
33943             this.wrap.style.display = "";
33944         }
33945     },
33946
33947     onContextMenu : function(e){
33948         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33949             e.preventDefault();
33950             this.focus();
33951             this.fireEvent("contextmenu", this.node, e);
33952         }
33953     },
33954
33955     onClick : function(e){
33956         if(this.dropping){
33957             e.stopEvent();
33958             return;
33959         }
33960         if(this.fireEvent("beforeclick", this.node, e) !== false){
33961             if(!this.disabled && this.node.attributes.href){
33962                 this.fireEvent("click", this.node, e);
33963                 return;
33964             }
33965             e.preventDefault();
33966             if(this.disabled){
33967                 return;
33968             }
33969
33970             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33971                 this.node.toggle();
33972             }
33973
33974             this.fireEvent("click", this.node, e);
33975         }else{
33976             e.stopEvent();
33977         }
33978     },
33979
33980     onDblClick : function(e){
33981         e.preventDefault();
33982         if(this.disabled){
33983             return;
33984         }
33985         if(this.checkbox){
33986             this.toggleCheck();
33987         }
33988         if(!this.animating && this.node.hasChildNodes()){
33989             this.node.toggle();
33990         }
33991         this.fireEvent("dblclick", this.node, e);
33992     },
33993
33994     onCheckChange : function(){
33995         var checked = this.checkbox.checked;
33996         this.node.attributes.checked = checked;
33997         this.fireEvent('checkchange', this.node, checked);
33998     },
33999
34000     ecClick : function(e){
34001         if(!this.animating && this.node.hasChildNodes()){
34002             this.node.toggle();
34003         }
34004     },
34005
34006     startDrop : function(){
34007         this.dropping = true;
34008     },
34009
34010     // delayed drop so the click event doesn't get fired on a drop
34011     endDrop : function(){
34012        setTimeout(function(){
34013            this.dropping = false;
34014        }.createDelegate(this), 50);
34015     },
34016
34017     expand : function(){
34018         this.updateExpandIcon();
34019         this.ctNode.style.display = "";
34020     },
34021
34022     focus : function(){
34023         if(!this.node.preventHScroll){
34024             try{this.anchor.focus();
34025             }catch(e){}
34026         }else if(!Roo.isIE){
34027             try{
34028                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34029                 var l = noscroll.scrollLeft;
34030                 this.anchor.focus();
34031                 noscroll.scrollLeft = l;
34032             }catch(e){}
34033         }
34034     },
34035
34036     toggleCheck : function(value){
34037         var cb = this.checkbox;
34038         if(cb){
34039             cb.checked = (value === undefined ? !cb.checked : value);
34040         }
34041     },
34042
34043     blur : function(){
34044         try{
34045             this.anchor.blur();
34046         }catch(e){}
34047     },
34048
34049     animExpand : function(callback){
34050         var ct = Roo.get(this.ctNode);
34051         ct.stopFx();
34052         if(!this.node.hasChildNodes()){
34053             this.updateExpandIcon();
34054             this.ctNode.style.display = "";
34055             Roo.callback(callback);
34056             return;
34057         }
34058         this.animating = true;
34059         this.updateExpandIcon();
34060
34061         ct.slideIn('t', {
34062            callback : function(){
34063                this.animating = false;
34064                Roo.callback(callback);
34065             },
34066             scope: this,
34067             duration: this.node.ownerTree.duration || .25
34068         });
34069     },
34070
34071     highlight : function(){
34072         var tree = this.node.getOwnerTree();
34073         Roo.fly(this.wrap).highlight(
34074             tree.hlColor || "C3DAF9",
34075             {endColor: tree.hlBaseColor}
34076         );
34077     },
34078
34079     collapse : function(){
34080         this.updateExpandIcon();
34081         this.ctNode.style.display = "none";
34082     },
34083
34084     animCollapse : function(callback){
34085         var ct = Roo.get(this.ctNode);
34086         ct.enableDisplayMode('block');
34087         ct.stopFx();
34088
34089         this.animating = true;
34090         this.updateExpandIcon();
34091
34092         ct.slideOut('t', {
34093             callback : function(){
34094                this.animating = false;
34095                Roo.callback(callback);
34096             },
34097             scope: this,
34098             duration: this.node.ownerTree.duration || .25
34099         });
34100     },
34101
34102     getContainer : function(){
34103         return this.ctNode;
34104     },
34105
34106     getEl : function(){
34107         return this.wrap;
34108     },
34109
34110     appendDDGhost : function(ghostNode){
34111         ghostNode.appendChild(this.elNode.cloneNode(true));
34112     },
34113
34114     getDDRepairXY : function(){
34115         return Roo.lib.Dom.getXY(this.iconNode);
34116     },
34117
34118     onRender : function(){
34119         this.render();
34120     },
34121
34122     render : function(bulkRender){
34123         var n = this.node, a = n.attributes;
34124         var targetNode = n.parentNode ?
34125               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34126
34127         if(!this.rendered){
34128             this.rendered = true;
34129
34130             this.renderElements(n, a, targetNode, bulkRender);
34131
34132             if(a.qtip){
34133                if(this.textNode.setAttributeNS){
34134                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34135                    if(a.qtipTitle){
34136                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34137                    }
34138                }else{
34139                    this.textNode.setAttribute("ext:qtip", a.qtip);
34140                    if(a.qtipTitle){
34141                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34142                    }
34143                }
34144             }else if(a.qtipCfg){
34145                 a.qtipCfg.target = Roo.id(this.textNode);
34146                 Roo.QuickTips.register(a.qtipCfg);
34147             }
34148             this.initEvents();
34149             if(!this.node.expanded){
34150                 this.updateExpandIcon();
34151             }
34152         }else{
34153             if(bulkRender === true) {
34154                 targetNode.appendChild(this.wrap);
34155             }
34156         }
34157     },
34158
34159     renderElements : function(n, a, targetNode, bulkRender)
34160     {
34161         // add some indent caching, this helps performance when rendering a large tree
34162         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34163         var t = n.getOwnerTree();
34164         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34165         if (typeof(n.attributes.html) != 'undefined') {
34166             txt = n.attributes.html;
34167         }
34168         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34169         var cb = typeof a.checked == 'boolean';
34170         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34171         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34172             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34173             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34174             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34175             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34176             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34177              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34178                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34179             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34180             "</li>"];
34181
34182         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34183             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34184                                 n.nextSibling.ui.getEl(), buf.join(""));
34185         }else{
34186             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34187         }
34188
34189         this.elNode = this.wrap.childNodes[0];
34190         this.ctNode = this.wrap.childNodes[1];
34191         var cs = this.elNode.childNodes;
34192         this.indentNode = cs[0];
34193         this.ecNode = cs[1];
34194         this.iconNode = cs[2];
34195         var index = 3;
34196         if(cb){
34197             this.checkbox = cs[3];
34198             index++;
34199         }
34200         this.anchor = cs[index];
34201         this.textNode = cs[index].firstChild;
34202     },
34203
34204     getAnchor : function(){
34205         return this.anchor;
34206     },
34207
34208     getTextEl : function(){
34209         return this.textNode;
34210     },
34211
34212     getIconEl : function(){
34213         return this.iconNode;
34214     },
34215
34216     isChecked : function(){
34217         return this.checkbox ? this.checkbox.checked : false;
34218     },
34219
34220     updateExpandIcon : function(){
34221         if(this.rendered){
34222             var n = this.node, c1, c2;
34223             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34224             var hasChild = n.hasChildNodes();
34225             if(hasChild){
34226                 if(n.expanded){
34227                     cls += "-minus";
34228                     c1 = "x-tree-node-collapsed";
34229                     c2 = "x-tree-node-expanded";
34230                 }else{
34231                     cls += "-plus";
34232                     c1 = "x-tree-node-expanded";
34233                     c2 = "x-tree-node-collapsed";
34234                 }
34235                 if(this.wasLeaf){
34236                     this.removeClass("x-tree-node-leaf");
34237                     this.wasLeaf = false;
34238                 }
34239                 if(this.c1 != c1 || this.c2 != c2){
34240                     Roo.fly(this.elNode).replaceClass(c1, c2);
34241                     this.c1 = c1; this.c2 = c2;
34242                 }
34243             }else{
34244                 // this changes non-leafs into leafs if they have no children.
34245                 // it's not very rational behaviour..
34246                 
34247                 if(!this.wasLeaf && this.node.leaf){
34248                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34249                     delete this.c1;
34250                     delete this.c2;
34251                     this.wasLeaf = true;
34252                 }
34253             }
34254             var ecc = "x-tree-ec-icon "+cls;
34255             if(this.ecc != ecc){
34256                 this.ecNode.className = ecc;
34257                 this.ecc = ecc;
34258             }
34259         }
34260     },
34261
34262     getChildIndent : function(){
34263         if(!this.childIndent){
34264             var buf = [];
34265             var p = this.node;
34266             while(p){
34267                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34268                     if(!p.isLast()) {
34269                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34270                     } else {
34271                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34272                     }
34273                 }
34274                 p = p.parentNode;
34275             }
34276             this.childIndent = buf.join("");
34277         }
34278         return this.childIndent;
34279     },
34280
34281     renderIndent : function(){
34282         if(this.rendered){
34283             var indent = "";
34284             var p = this.node.parentNode;
34285             if(p){
34286                 indent = p.ui.getChildIndent();
34287             }
34288             if(this.indentMarkup != indent){ // don't rerender if not required
34289                 this.indentNode.innerHTML = indent;
34290                 this.indentMarkup = indent;
34291             }
34292             this.updateExpandIcon();
34293         }
34294     }
34295 };
34296
34297 Roo.tree.RootTreeNodeUI = function(){
34298     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34299 };
34300 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34301     render : function(){
34302         if(!this.rendered){
34303             var targetNode = this.node.ownerTree.innerCt.dom;
34304             this.node.expanded = true;
34305             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34306             this.wrap = this.ctNode = targetNode.firstChild;
34307         }
34308     },
34309     collapse : function(){
34310     },
34311     expand : function(){
34312     }
34313 });/*
34314  * Based on:
34315  * Ext JS Library 1.1.1
34316  * Copyright(c) 2006-2007, Ext JS, LLC.
34317  *
34318  * Originally Released Under LGPL - original licence link has changed is not relivant.
34319  *
34320  * Fork - LGPL
34321  * <script type="text/javascript">
34322  */
34323 /**
34324  * @class Roo.tree.TreeLoader
34325  * @extends Roo.util.Observable
34326  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34327  * nodes from a specified URL. The response must be a javascript Array definition
34328  * who's elements are node definition objects. eg:
34329  * <pre><code>
34330 {  success : true,
34331    data :      [
34332    
34333     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34334     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34335     ]
34336 }
34337
34338
34339 </code></pre>
34340  * <br><br>
34341  * The old style respose with just an array is still supported, but not recommended.
34342  * <br><br>
34343  *
34344  * A server request is sent, and child nodes are loaded only when a node is expanded.
34345  * The loading node's id is passed to the server under the parameter name "node" to
34346  * enable the server to produce the correct child nodes.
34347  * <br><br>
34348  * To pass extra parameters, an event handler may be attached to the "beforeload"
34349  * event, and the parameters specified in the TreeLoader's baseParams property:
34350  * <pre><code>
34351     myTreeLoader.on("beforeload", function(treeLoader, node) {
34352         this.baseParams.category = node.attributes.category;
34353     }, this);
34354 </code></pre><
34355  * This would pass an HTTP parameter called "category" to the server containing
34356  * the value of the Node's "category" attribute.
34357  * @constructor
34358  * Creates a new Treeloader.
34359  * @param {Object} config A config object containing config properties.
34360  */
34361 Roo.tree.TreeLoader = function(config){
34362     this.baseParams = {};
34363     this.requestMethod = "POST";
34364     Roo.apply(this, config);
34365
34366     this.addEvents({
34367     
34368         /**
34369          * @event beforeload
34370          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34371          * @param {Object} This TreeLoader object.
34372          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34373          * @param {Object} callback The callback function specified in the {@link #load} call.
34374          */
34375         beforeload : true,
34376         /**
34377          * @event load
34378          * Fires when the node has been successfuly loaded.
34379          * @param {Object} This TreeLoader object.
34380          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34381          * @param {Object} response The response object containing the data from the server.
34382          */
34383         load : true,
34384         /**
34385          * @event loadexception
34386          * Fires if the network request failed.
34387          * @param {Object} This TreeLoader object.
34388          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34389          * @param {Object} response The response object containing the data from the server.
34390          */
34391         loadexception : true,
34392         /**
34393          * @event create
34394          * Fires before a node is created, enabling you to return custom Node types 
34395          * @param {Object} This TreeLoader object.
34396          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34397          */
34398         create : true
34399     });
34400
34401     Roo.tree.TreeLoader.superclass.constructor.call(this);
34402 };
34403
34404 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34405     /**
34406     * @cfg {String} dataUrl The URL from which to request a Json string which
34407     * specifies an array of node definition object representing the child nodes
34408     * to be loaded.
34409     */
34410     /**
34411     * @cfg {String} requestMethod either GET or POST
34412     * defaults to POST (due to BC)
34413     * to be loaded.
34414     */
34415     /**
34416     * @cfg {Object} baseParams (optional) An object containing properties which
34417     * specify HTTP parameters to be passed to each request for child nodes.
34418     */
34419     /**
34420     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34421     * created by this loader. If the attributes sent by the server have an attribute in this object,
34422     * they take priority.
34423     */
34424     /**
34425     * @cfg {Object} uiProviders (optional) An object containing properties which
34426     * 
34427     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34428     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34429     * <i>uiProvider</i> attribute of a returned child node is a string rather
34430     * than a reference to a TreeNodeUI implementation, this that string value
34431     * is used as a property name in the uiProviders object. You can define the provider named
34432     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34433     */
34434     uiProviders : {},
34435
34436     /**
34437     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34438     * child nodes before loading.
34439     */
34440     clearOnLoad : true,
34441
34442     /**
34443     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34444     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34445     * Grid query { data : [ .....] }
34446     */
34447     
34448     root : false,
34449      /**
34450     * @cfg {String} queryParam (optional) 
34451     * Name of the query as it will be passed on the querystring (defaults to 'node')
34452     * eg. the request will be ?node=[id]
34453     */
34454     
34455     
34456     queryParam: false,
34457     
34458     /**
34459      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34460      * This is called automatically when a node is expanded, but may be used to reload
34461      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34462      * @param {Roo.tree.TreeNode} node
34463      * @param {Function} callback
34464      */
34465     load : function(node, callback){
34466         if(this.clearOnLoad){
34467             while(node.firstChild){
34468                 node.removeChild(node.firstChild);
34469             }
34470         }
34471         if(node.attributes.children){ // preloaded json children
34472             var cs = node.attributes.children;
34473             for(var i = 0, len = cs.length; i < len; i++){
34474                 node.appendChild(this.createNode(cs[i]));
34475             }
34476             if(typeof callback == "function"){
34477                 callback();
34478             }
34479         }else if(this.dataUrl){
34480             this.requestData(node, callback);
34481         }
34482     },
34483
34484     getParams: function(node){
34485         var buf = [], bp = this.baseParams;
34486         for(var key in bp){
34487             if(typeof bp[key] != "function"){
34488                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34489             }
34490         }
34491         var n = this.queryParam === false ? 'node' : this.queryParam;
34492         buf.push(n + "=", encodeURIComponent(node.id));
34493         return buf.join("");
34494     },
34495
34496     requestData : function(node, callback){
34497         if(this.fireEvent("beforeload", this, node, callback) !== false){
34498             this.transId = Roo.Ajax.request({
34499                 method:this.requestMethod,
34500                 url: this.dataUrl||this.url,
34501                 success: this.handleResponse,
34502                 failure: this.handleFailure,
34503                 scope: this,
34504                 argument: {callback: callback, node: node},
34505                 params: this.getParams(node)
34506             });
34507         }else{
34508             // if the load is cancelled, make sure we notify
34509             // the node that we are done
34510             if(typeof callback == "function"){
34511                 callback();
34512             }
34513         }
34514     },
34515
34516     isLoading : function(){
34517         return this.transId ? true : false;
34518     },
34519
34520     abort : function(){
34521         if(this.isLoading()){
34522             Roo.Ajax.abort(this.transId);
34523         }
34524     },
34525
34526     // private
34527     createNode : function(attr)
34528     {
34529         // apply baseAttrs, nice idea Corey!
34530         if(this.baseAttrs){
34531             Roo.applyIf(attr, this.baseAttrs);
34532         }
34533         if(this.applyLoader !== false){
34534             attr.loader = this;
34535         }
34536         // uiProvider = depreciated..
34537         
34538         if(typeof(attr.uiProvider) == 'string'){
34539            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34540                 /**  eval:var:attr */ eval(attr.uiProvider);
34541         }
34542         if(typeof(this.uiProviders['default']) != 'undefined') {
34543             attr.uiProvider = this.uiProviders['default'];
34544         }
34545         
34546         this.fireEvent('create', this, attr);
34547         
34548         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34549         return(attr.leaf ?
34550                         new Roo.tree.TreeNode(attr) :
34551                         new Roo.tree.AsyncTreeNode(attr));
34552     },
34553
34554     processResponse : function(response, node, callback)
34555     {
34556         var json = response.responseText;
34557         try {
34558             
34559             var o = Roo.decode(json);
34560             
34561             if (this.root === false && typeof(o.success) != undefined) {
34562                 this.root = 'data'; // the default behaviour for list like data..
34563                 }
34564                 
34565             if (this.root !== false &&  !o.success) {
34566                 // it's a failure condition.
34567                 var a = response.argument;
34568                 this.fireEvent("loadexception", this, a.node, response);
34569                 Roo.log("Load failed - should have a handler really");
34570                 return;
34571             }
34572             
34573             
34574             
34575             if (this.root !== false) {
34576                  o = o[this.root];
34577             }
34578             
34579             for(var i = 0, len = o.length; i < len; i++){
34580                 var n = this.createNode(o[i]);
34581                 if(n){
34582                     node.appendChild(n);
34583                 }
34584             }
34585             if(typeof callback == "function"){
34586                 callback(this, node);
34587             }
34588         }catch(e){
34589             this.handleFailure(response);
34590         }
34591     },
34592
34593     handleResponse : function(response){
34594         this.transId = false;
34595         var a = response.argument;
34596         this.processResponse(response, a.node, a.callback);
34597         this.fireEvent("load", this, a.node, response);
34598     },
34599
34600     handleFailure : function(response)
34601     {
34602         // should handle failure better..
34603         this.transId = false;
34604         var a = response.argument;
34605         this.fireEvent("loadexception", this, a.node, response);
34606         if(typeof a.callback == "function"){
34607             a.callback(this, a.node);
34608         }
34609     }
34610 });/*
34611  * Based on:
34612  * Ext JS Library 1.1.1
34613  * Copyright(c) 2006-2007, Ext JS, LLC.
34614  *
34615  * Originally Released Under LGPL - original licence link has changed is not relivant.
34616  *
34617  * Fork - LGPL
34618  * <script type="text/javascript">
34619  */
34620
34621 /**
34622 * @class Roo.tree.TreeFilter
34623 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34624 * @param {TreePanel} tree
34625 * @param {Object} config (optional)
34626  */
34627 Roo.tree.TreeFilter = function(tree, config){
34628     this.tree = tree;
34629     this.filtered = {};
34630     Roo.apply(this, config);
34631 };
34632
34633 Roo.tree.TreeFilter.prototype = {
34634     clearBlank:false,
34635     reverse:false,
34636     autoClear:false,
34637     remove:false,
34638
34639      /**
34640      * Filter the data by a specific attribute.
34641      * @param {String/RegExp} value Either string that the attribute value
34642      * should start with or a RegExp to test against the attribute
34643      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34644      * @param {TreeNode} startNode (optional) The node to start the filter at.
34645      */
34646     filter : function(value, attr, startNode){
34647         attr = attr || "text";
34648         var f;
34649         if(typeof value == "string"){
34650             var vlen = value.length;
34651             // auto clear empty filter
34652             if(vlen == 0 && this.clearBlank){
34653                 this.clear();
34654                 return;
34655             }
34656             value = value.toLowerCase();
34657             f = function(n){
34658                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34659             };
34660         }else if(value.exec){ // regex?
34661             f = function(n){
34662                 return value.test(n.attributes[attr]);
34663             };
34664         }else{
34665             throw 'Illegal filter type, must be string or regex';
34666         }
34667         this.filterBy(f, null, startNode);
34668         },
34669
34670     /**
34671      * Filter by a function. The passed function will be called with each
34672      * node in the tree (or from the startNode). If the function returns true, the node is kept
34673      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34674      * @param {Function} fn The filter function
34675      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34676      */
34677     filterBy : function(fn, scope, startNode){
34678         startNode = startNode || this.tree.root;
34679         if(this.autoClear){
34680             this.clear();
34681         }
34682         var af = this.filtered, rv = this.reverse;
34683         var f = function(n){
34684             if(n == startNode){
34685                 return true;
34686             }
34687             if(af[n.id]){
34688                 return false;
34689             }
34690             var m = fn.call(scope || n, n);
34691             if(!m || rv){
34692                 af[n.id] = n;
34693                 n.ui.hide();
34694                 return false;
34695             }
34696             return true;
34697         };
34698         startNode.cascade(f);
34699         if(this.remove){
34700            for(var id in af){
34701                if(typeof id != "function"){
34702                    var n = af[id];
34703                    if(n && n.parentNode){
34704                        n.parentNode.removeChild(n);
34705                    }
34706                }
34707            }
34708         }
34709     },
34710
34711     /**
34712      * Clears the current filter. Note: with the "remove" option
34713      * set a filter cannot be cleared.
34714      */
34715     clear : function(){
34716         var t = this.tree;
34717         var af = this.filtered;
34718         for(var id in af){
34719             if(typeof id != "function"){
34720                 var n = af[id];
34721                 if(n){
34722                     n.ui.show();
34723                 }
34724             }
34725         }
34726         this.filtered = {};
34727     }
34728 };
34729 /*
34730  * Based on:
34731  * Ext JS Library 1.1.1
34732  * Copyright(c) 2006-2007, Ext JS, LLC.
34733  *
34734  * Originally Released Under LGPL - original licence link has changed is not relivant.
34735  *
34736  * Fork - LGPL
34737  * <script type="text/javascript">
34738  */
34739  
34740
34741 /**
34742  * @class Roo.tree.TreeSorter
34743  * Provides sorting of nodes in a TreePanel
34744  * 
34745  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34746  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34747  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34748  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34749  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34750  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34751  * @constructor
34752  * @param {TreePanel} tree
34753  * @param {Object} config
34754  */
34755 Roo.tree.TreeSorter = function(tree, config){
34756     Roo.apply(this, config);
34757     tree.on("beforechildrenrendered", this.doSort, this);
34758     tree.on("append", this.updateSort, this);
34759     tree.on("insert", this.updateSort, this);
34760     
34761     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34762     var p = this.property || "text";
34763     var sortType = this.sortType;
34764     var fs = this.folderSort;
34765     var cs = this.caseSensitive === true;
34766     var leafAttr = this.leafAttr || 'leaf';
34767
34768     this.sortFn = function(n1, n2){
34769         if(fs){
34770             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34771                 return 1;
34772             }
34773             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34774                 return -1;
34775             }
34776         }
34777         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34778         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34779         if(v1 < v2){
34780                         return dsc ? +1 : -1;
34781                 }else if(v1 > v2){
34782                         return dsc ? -1 : +1;
34783         }else{
34784                 return 0;
34785         }
34786     };
34787 };
34788
34789 Roo.tree.TreeSorter.prototype = {
34790     doSort : function(node){
34791         node.sort(this.sortFn);
34792     },
34793     
34794     compareNodes : function(n1, n2){
34795         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34796     },
34797     
34798     updateSort : function(tree, node){
34799         if(node.childrenRendered){
34800             this.doSort.defer(1, this, [node]);
34801         }
34802     }
34803 };/*
34804  * Based on:
34805  * Ext JS Library 1.1.1
34806  * Copyright(c) 2006-2007, Ext JS, LLC.
34807  *
34808  * Originally Released Under LGPL - original licence link has changed is not relivant.
34809  *
34810  * Fork - LGPL
34811  * <script type="text/javascript">
34812  */
34813
34814 if(Roo.dd.DropZone){
34815     
34816 Roo.tree.TreeDropZone = function(tree, config){
34817     this.allowParentInsert = false;
34818     this.allowContainerDrop = false;
34819     this.appendOnly = false;
34820     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34821     this.tree = tree;
34822     this.lastInsertClass = "x-tree-no-status";
34823     this.dragOverData = {};
34824 };
34825
34826 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34827     ddGroup : "TreeDD",
34828     scroll:  true,
34829     
34830     expandDelay : 1000,
34831     
34832     expandNode : function(node){
34833         if(node.hasChildNodes() && !node.isExpanded()){
34834             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34835         }
34836     },
34837     
34838     queueExpand : function(node){
34839         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34840     },
34841     
34842     cancelExpand : function(){
34843         if(this.expandProcId){
34844             clearTimeout(this.expandProcId);
34845             this.expandProcId = false;
34846         }
34847     },
34848     
34849     isValidDropPoint : function(n, pt, dd, e, data){
34850         if(!n || !data){ return false; }
34851         var targetNode = n.node;
34852         var dropNode = data.node;
34853         // default drop rules
34854         if(!(targetNode && targetNode.isTarget && pt)){
34855             return false;
34856         }
34857         if(pt == "append" && targetNode.allowChildren === false){
34858             return false;
34859         }
34860         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34861             return false;
34862         }
34863         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34864             return false;
34865         }
34866         // reuse the object
34867         var overEvent = this.dragOverData;
34868         overEvent.tree = this.tree;
34869         overEvent.target = targetNode;
34870         overEvent.data = data;
34871         overEvent.point = pt;
34872         overEvent.source = dd;
34873         overEvent.rawEvent = e;
34874         overEvent.dropNode = dropNode;
34875         overEvent.cancel = false;  
34876         var result = this.tree.fireEvent("nodedragover", overEvent);
34877         return overEvent.cancel === false && result !== false;
34878     },
34879     
34880     getDropPoint : function(e, n, dd)
34881     {
34882         var tn = n.node;
34883         if(tn.isRoot){
34884             return tn.allowChildren !== false ? "append" : false; // always append for root
34885         }
34886         var dragEl = n.ddel;
34887         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34888         var y = Roo.lib.Event.getPageY(e);
34889         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34890         
34891         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34892         var noAppend = tn.allowChildren === false;
34893         if(this.appendOnly || tn.parentNode.allowChildren === false){
34894             return noAppend ? false : "append";
34895         }
34896         var noBelow = false;
34897         if(!this.allowParentInsert){
34898             noBelow = tn.hasChildNodes() && tn.isExpanded();
34899         }
34900         var q = (b - t) / (noAppend ? 2 : 3);
34901         if(y >= t && y < (t + q)){
34902             return "above";
34903         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34904             return "below";
34905         }else{
34906             return "append";
34907         }
34908     },
34909     
34910     onNodeEnter : function(n, dd, e, data)
34911     {
34912         this.cancelExpand();
34913     },
34914     
34915     onNodeOver : function(n, dd, e, data)
34916     {
34917        
34918         var pt = this.getDropPoint(e, n, dd);
34919         var node = n.node;
34920         
34921         // auto node expand check
34922         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34923             this.queueExpand(node);
34924         }else if(pt != "append"){
34925             this.cancelExpand();
34926         }
34927         
34928         // set the insert point style on the target node
34929         var returnCls = this.dropNotAllowed;
34930         if(this.isValidDropPoint(n, pt, dd, e, data)){
34931            if(pt){
34932                var el = n.ddel;
34933                var cls;
34934                if(pt == "above"){
34935                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34936                    cls = "x-tree-drag-insert-above";
34937                }else if(pt == "below"){
34938                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34939                    cls = "x-tree-drag-insert-below";
34940                }else{
34941                    returnCls = "x-tree-drop-ok-append";
34942                    cls = "x-tree-drag-append";
34943                }
34944                if(this.lastInsertClass != cls){
34945                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34946                    this.lastInsertClass = cls;
34947                }
34948            }
34949        }
34950        return returnCls;
34951     },
34952     
34953     onNodeOut : function(n, dd, e, data){
34954         
34955         this.cancelExpand();
34956         this.removeDropIndicators(n);
34957     },
34958     
34959     onNodeDrop : function(n, dd, e, data){
34960         var point = this.getDropPoint(e, n, dd);
34961         var targetNode = n.node;
34962         targetNode.ui.startDrop();
34963         if(!this.isValidDropPoint(n, point, dd, e, data)){
34964             targetNode.ui.endDrop();
34965             return false;
34966         }
34967         // first try to find the drop node
34968         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34969         var dropEvent = {
34970             tree : this.tree,
34971             target: targetNode,
34972             data: data,
34973             point: point,
34974             source: dd,
34975             rawEvent: e,
34976             dropNode: dropNode,
34977             cancel: !dropNode   
34978         };
34979         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34980         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34981             targetNode.ui.endDrop();
34982             return false;
34983         }
34984         // allow target changing
34985         targetNode = dropEvent.target;
34986         if(point == "append" && !targetNode.isExpanded()){
34987             targetNode.expand(false, null, function(){
34988                 this.completeDrop(dropEvent);
34989             }.createDelegate(this));
34990         }else{
34991             this.completeDrop(dropEvent);
34992         }
34993         return true;
34994     },
34995     
34996     completeDrop : function(de){
34997         var ns = de.dropNode, p = de.point, t = de.target;
34998         if(!(ns instanceof Array)){
34999             ns = [ns];
35000         }
35001         var n;
35002         for(var i = 0, len = ns.length; i < len; i++){
35003             n = ns[i];
35004             if(p == "above"){
35005                 t.parentNode.insertBefore(n, t);
35006             }else if(p == "below"){
35007                 t.parentNode.insertBefore(n, t.nextSibling);
35008             }else{
35009                 t.appendChild(n);
35010             }
35011         }
35012         n.ui.focus();
35013         if(this.tree.hlDrop){
35014             n.ui.highlight();
35015         }
35016         t.ui.endDrop();
35017         this.tree.fireEvent("nodedrop", de);
35018     },
35019     
35020     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35021         if(this.tree.hlDrop){
35022             dropNode.ui.focus();
35023             dropNode.ui.highlight();
35024         }
35025         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35026     },
35027     
35028     getTree : function(){
35029         return this.tree;
35030     },
35031     
35032     removeDropIndicators : function(n){
35033         if(n && n.ddel){
35034             var el = n.ddel;
35035             Roo.fly(el).removeClass([
35036                     "x-tree-drag-insert-above",
35037                     "x-tree-drag-insert-below",
35038                     "x-tree-drag-append"]);
35039             this.lastInsertClass = "_noclass";
35040         }
35041     },
35042     
35043     beforeDragDrop : function(target, e, id){
35044         this.cancelExpand();
35045         return true;
35046     },
35047     
35048     afterRepair : function(data){
35049         if(data && Roo.enableFx){
35050             data.node.ui.highlight();
35051         }
35052         this.hideProxy();
35053     } 
35054     
35055 });
35056
35057 }
35058 /*
35059  * Based on:
35060  * Ext JS Library 1.1.1
35061  * Copyright(c) 2006-2007, Ext JS, LLC.
35062  *
35063  * Originally Released Under LGPL - original licence link has changed is not relivant.
35064  *
35065  * Fork - LGPL
35066  * <script type="text/javascript">
35067  */
35068  
35069
35070 if(Roo.dd.DragZone){
35071 Roo.tree.TreeDragZone = function(tree, config){
35072     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35073     this.tree = tree;
35074 };
35075
35076 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35077     ddGroup : "TreeDD",
35078    
35079     onBeforeDrag : function(data, e){
35080         var n = data.node;
35081         return n && n.draggable && !n.disabled;
35082     },
35083      
35084     
35085     onInitDrag : function(e){
35086         var data = this.dragData;
35087         this.tree.getSelectionModel().select(data.node);
35088         this.proxy.update("");
35089         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35090         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35091     },
35092     
35093     getRepairXY : function(e, data){
35094         return data.node.ui.getDDRepairXY();
35095     },
35096     
35097     onEndDrag : function(data, e){
35098         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35099         
35100         
35101     },
35102     
35103     onValidDrop : function(dd, e, id){
35104         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35105         this.hideProxy();
35106     },
35107     
35108     beforeInvalidDrop : function(e, id){
35109         // this scrolls the original position back into view
35110         var sm = this.tree.getSelectionModel();
35111         sm.clearSelections();
35112         sm.select(this.dragData.node);
35113     }
35114 });
35115 }/*
35116  * Based on:
35117  * Ext JS Library 1.1.1
35118  * Copyright(c) 2006-2007, Ext JS, LLC.
35119  *
35120  * Originally Released Under LGPL - original licence link has changed is not relivant.
35121  *
35122  * Fork - LGPL
35123  * <script type="text/javascript">
35124  */
35125 /**
35126  * @class Roo.tree.TreeEditor
35127  * @extends Roo.Editor
35128  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35129  * as the editor field.
35130  * @constructor
35131  * @param {Object} config (used to be the tree panel.)
35132  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35133  * 
35134  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35135  * @cfg {Roo.form.TextField|Object} field The field configuration
35136  *
35137  * 
35138  */
35139 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35140     var tree = config;
35141     var field;
35142     if (oldconfig) { // old style..
35143         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35144     } else {
35145         // new style..
35146         tree = config.tree;
35147         config.field = config.field  || {};
35148         config.field.xtype = 'TextField';
35149         field = Roo.factory(config.field, Roo.form);
35150     }
35151     config = config || {};
35152     
35153     
35154     this.addEvents({
35155         /**
35156          * @event beforenodeedit
35157          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35158          * false from the handler of this event.
35159          * @param {Editor} this
35160          * @param {Roo.tree.Node} node 
35161          */
35162         "beforenodeedit" : true
35163     });
35164     
35165     //Roo.log(config);
35166     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35167
35168     this.tree = tree;
35169
35170     tree.on('beforeclick', this.beforeNodeClick, this);
35171     tree.getTreeEl().on('mousedown', this.hide, this);
35172     this.on('complete', this.updateNode, this);
35173     this.on('beforestartedit', this.fitToTree, this);
35174     this.on('startedit', this.bindScroll, this, {delay:10});
35175     this.on('specialkey', this.onSpecialKey, this);
35176 };
35177
35178 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35179     /**
35180      * @cfg {String} alignment
35181      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35182      */
35183     alignment: "l-l",
35184     // inherit
35185     autoSize: false,
35186     /**
35187      * @cfg {Boolean} hideEl
35188      * True to hide the bound element while the editor is displayed (defaults to false)
35189      */
35190     hideEl : false,
35191     /**
35192      * @cfg {String} cls
35193      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35194      */
35195     cls: "x-small-editor x-tree-editor",
35196     /**
35197      * @cfg {Boolean} shim
35198      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35199      */
35200     shim:false,
35201     // inherit
35202     shadow:"frame",
35203     /**
35204      * @cfg {Number} maxWidth
35205      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35206      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35207      * scroll and client offsets into account prior to each edit.
35208      */
35209     maxWidth: 250,
35210
35211     editDelay : 350,
35212
35213     // private
35214     fitToTree : function(ed, el){
35215         var td = this.tree.getTreeEl().dom, nd = el.dom;
35216         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35217             td.scrollLeft = nd.offsetLeft;
35218         }
35219         var w = Math.min(
35220                 this.maxWidth,
35221                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35222         this.setSize(w, '');
35223         
35224         return this.fireEvent('beforenodeedit', this, this.editNode);
35225         
35226     },
35227
35228     // private
35229     triggerEdit : function(node){
35230         this.completeEdit();
35231         this.editNode = node;
35232         this.startEdit(node.ui.textNode, node.text);
35233     },
35234
35235     // private
35236     bindScroll : function(){
35237         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35238     },
35239
35240     // private
35241     beforeNodeClick : function(node, e){
35242         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35243         this.lastClick = new Date();
35244         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35245             e.stopEvent();
35246             this.triggerEdit(node);
35247             return false;
35248         }
35249         return true;
35250     },
35251
35252     // private
35253     updateNode : function(ed, value){
35254         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35255         this.editNode.setText(value);
35256     },
35257
35258     // private
35259     onHide : function(){
35260         Roo.tree.TreeEditor.superclass.onHide.call(this);
35261         if(this.editNode){
35262             this.editNode.ui.focus();
35263         }
35264     },
35265
35266     // private
35267     onSpecialKey : function(field, e){
35268         var k = e.getKey();
35269         if(k == e.ESC){
35270             e.stopEvent();
35271             this.cancelEdit();
35272         }else if(k == e.ENTER && !e.hasModifier()){
35273             e.stopEvent();
35274             this.completeEdit();
35275         }
35276     }
35277 });//<Script type="text/javascript">
35278 /*
35279  * Based on:
35280  * Ext JS Library 1.1.1
35281  * Copyright(c) 2006-2007, Ext JS, LLC.
35282  *
35283  * Originally Released Under LGPL - original licence link has changed is not relivant.
35284  *
35285  * Fork - LGPL
35286  * <script type="text/javascript">
35287  */
35288  
35289 /**
35290  * Not documented??? - probably should be...
35291  */
35292
35293 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35294     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35295     
35296     renderElements : function(n, a, targetNode, bulkRender){
35297         //consel.log("renderElements?");
35298         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35299
35300         var t = n.getOwnerTree();
35301         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35302         
35303         var cols = t.columns;
35304         var bw = t.borderWidth;
35305         var c = cols[0];
35306         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35307          var cb = typeof a.checked == "boolean";
35308         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35309         var colcls = 'x-t-' + tid + '-c0';
35310         var buf = [
35311             '<li class="x-tree-node">',
35312             
35313                 
35314                 '<div class="x-tree-node-el ', a.cls,'">',
35315                     // extran...
35316                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35317                 
35318                 
35319                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35320                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35321                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35322                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35323                            (a.iconCls ? ' '+a.iconCls : ''),
35324                            '" unselectable="on" />',
35325                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35326                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35327                              
35328                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35329                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35330                             '<span unselectable="on" qtip="' + tx + '">',
35331                              tx,
35332                              '</span></a>' ,
35333                     '</div>',
35334                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35335                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35336                  ];
35337         for(var i = 1, len = cols.length; i < len; i++){
35338             c = cols[i];
35339             colcls = 'x-t-' + tid + '-c' +i;
35340             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35341             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35342                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35343                       "</div>");
35344          }
35345          
35346          buf.push(
35347             '</a>',
35348             '<div class="x-clear"></div></div>',
35349             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35350             "</li>");
35351         
35352         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35353             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35354                                 n.nextSibling.ui.getEl(), buf.join(""));
35355         }else{
35356             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35357         }
35358         var el = this.wrap.firstChild;
35359         this.elRow = el;
35360         this.elNode = el.firstChild;
35361         this.ranchor = el.childNodes[1];
35362         this.ctNode = this.wrap.childNodes[1];
35363         var cs = el.firstChild.childNodes;
35364         this.indentNode = cs[0];
35365         this.ecNode = cs[1];
35366         this.iconNode = cs[2];
35367         var index = 3;
35368         if(cb){
35369             this.checkbox = cs[3];
35370             index++;
35371         }
35372         this.anchor = cs[index];
35373         
35374         this.textNode = cs[index].firstChild;
35375         
35376         //el.on("click", this.onClick, this);
35377         //el.on("dblclick", this.onDblClick, this);
35378         
35379         
35380        // console.log(this);
35381     },
35382     initEvents : function(){
35383         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35384         
35385             
35386         var a = this.ranchor;
35387
35388         var el = Roo.get(a);
35389
35390         if(Roo.isOpera){ // opera render bug ignores the CSS
35391             el.setStyle("text-decoration", "none");
35392         }
35393
35394         el.on("click", this.onClick, this);
35395         el.on("dblclick", this.onDblClick, this);
35396         el.on("contextmenu", this.onContextMenu, this);
35397         
35398     },
35399     
35400     /*onSelectedChange : function(state){
35401         if(state){
35402             this.focus();
35403             this.addClass("x-tree-selected");
35404         }else{
35405             //this.blur();
35406             this.removeClass("x-tree-selected");
35407         }
35408     },*/
35409     addClass : function(cls){
35410         if(this.elRow){
35411             Roo.fly(this.elRow).addClass(cls);
35412         }
35413         
35414     },
35415     
35416     
35417     removeClass : function(cls){
35418         if(this.elRow){
35419             Roo.fly(this.elRow).removeClass(cls);
35420         }
35421     }
35422
35423     
35424     
35425 });//<Script type="text/javascript">
35426
35427 /*
35428  * Based on:
35429  * Ext JS Library 1.1.1
35430  * Copyright(c) 2006-2007, Ext JS, LLC.
35431  *
35432  * Originally Released Under LGPL - original licence link has changed is not relivant.
35433  *
35434  * Fork - LGPL
35435  * <script type="text/javascript">
35436  */
35437  
35438
35439 /**
35440  * @class Roo.tree.ColumnTree
35441  * @extends Roo.data.TreePanel
35442  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35443  * @cfg {int} borderWidth  compined right/left border allowance
35444  * @constructor
35445  * @param {String/HTMLElement/Element} el The container element
35446  * @param {Object} config
35447  */
35448 Roo.tree.ColumnTree =  function(el, config)
35449 {
35450    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35451    this.addEvents({
35452         /**
35453         * @event resize
35454         * Fire this event on a container when it resizes
35455         * @param {int} w Width
35456         * @param {int} h Height
35457         */
35458        "resize" : true
35459     });
35460     this.on('resize', this.onResize, this);
35461 };
35462
35463 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35464     //lines:false,
35465     
35466     
35467     borderWidth: Roo.isBorderBox ? 0 : 2, 
35468     headEls : false,
35469     
35470     render : function(){
35471         // add the header.....
35472        
35473         Roo.tree.ColumnTree.superclass.render.apply(this);
35474         
35475         this.el.addClass('x-column-tree');
35476         
35477         this.headers = this.el.createChild(
35478             {cls:'x-tree-headers'},this.innerCt.dom);
35479    
35480         var cols = this.columns, c;
35481         var totalWidth = 0;
35482         this.headEls = [];
35483         var  len = cols.length;
35484         for(var i = 0; i < len; i++){
35485              c = cols[i];
35486              totalWidth += c.width;
35487             this.headEls.push(this.headers.createChild({
35488                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35489                  cn: {
35490                      cls:'x-tree-hd-text',
35491                      html: c.header
35492                  },
35493                  style:'width:'+(c.width-this.borderWidth)+'px;'
35494              }));
35495         }
35496         this.headers.createChild({cls:'x-clear'});
35497         // prevent floats from wrapping when clipped
35498         this.headers.setWidth(totalWidth);
35499         //this.innerCt.setWidth(totalWidth);
35500         this.innerCt.setStyle({ overflow: 'auto' });
35501         this.onResize(this.width, this.height);
35502              
35503         
35504     },
35505     onResize : function(w,h)
35506     {
35507         this.height = h;
35508         this.width = w;
35509         // resize cols..
35510         this.innerCt.setWidth(this.width);
35511         this.innerCt.setHeight(this.height-20);
35512         
35513         // headers...
35514         var cols = this.columns, c;
35515         var totalWidth = 0;
35516         var expEl = false;
35517         var len = cols.length;
35518         for(var i = 0; i < len; i++){
35519             c = cols[i];
35520             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35521                 // it's the expander..
35522                 expEl  = this.headEls[i];
35523                 continue;
35524             }
35525             totalWidth += c.width;
35526             
35527         }
35528         if (expEl) {
35529             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35530         }
35531         this.headers.setWidth(w-20);
35532
35533         
35534         
35535         
35536     }
35537 });
35538 /*
35539  * Based on:
35540  * Ext JS Library 1.1.1
35541  * Copyright(c) 2006-2007, Ext JS, LLC.
35542  *
35543  * Originally Released Under LGPL - original licence link has changed is not relivant.
35544  *
35545  * Fork - LGPL
35546  * <script type="text/javascript">
35547  */
35548  
35549 /**
35550  * @class Roo.menu.Menu
35551  * @extends Roo.util.Observable
35552  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35553  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35554  * @constructor
35555  * Creates a new Menu
35556  * @param {Object} config Configuration options
35557  */
35558 Roo.menu.Menu = function(config){
35559     Roo.apply(this, config);
35560     this.id = this.id || Roo.id();
35561     this.addEvents({
35562         /**
35563          * @event beforeshow
35564          * Fires before this menu is displayed
35565          * @param {Roo.menu.Menu} this
35566          */
35567         beforeshow : true,
35568         /**
35569          * @event beforehide
35570          * Fires before this menu is hidden
35571          * @param {Roo.menu.Menu} this
35572          */
35573         beforehide : true,
35574         /**
35575          * @event show
35576          * Fires after this menu is displayed
35577          * @param {Roo.menu.Menu} this
35578          */
35579         show : true,
35580         /**
35581          * @event hide
35582          * Fires after this menu is hidden
35583          * @param {Roo.menu.Menu} this
35584          */
35585         hide : true,
35586         /**
35587          * @event click
35588          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35589          * @param {Roo.menu.Menu} this
35590          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35591          * @param {Roo.EventObject} e
35592          */
35593         click : true,
35594         /**
35595          * @event mouseover
35596          * Fires when the mouse is hovering over this menu
35597          * @param {Roo.menu.Menu} this
35598          * @param {Roo.EventObject} e
35599          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35600          */
35601         mouseover : true,
35602         /**
35603          * @event mouseout
35604          * Fires when the mouse exits this menu
35605          * @param {Roo.menu.Menu} this
35606          * @param {Roo.EventObject} e
35607          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35608          */
35609         mouseout : true,
35610         /**
35611          * @event itemclick
35612          * Fires when a menu item contained in this menu is clicked
35613          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35614          * @param {Roo.EventObject} e
35615          */
35616         itemclick: true
35617     });
35618     if (this.registerMenu) {
35619         Roo.menu.MenuMgr.register(this);
35620     }
35621     
35622     var mis = this.items;
35623     this.items = new Roo.util.MixedCollection();
35624     if(mis){
35625         this.add.apply(this, mis);
35626     }
35627 };
35628
35629 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35630     /**
35631      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35632      */
35633     minWidth : 120,
35634     /**
35635      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35636      * for bottom-right shadow (defaults to "sides")
35637      */
35638     shadow : "sides",
35639     /**
35640      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35641      * this menu (defaults to "tl-tr?")
35642      */
35643     subMenuAlign : "tl-tr?",
35644     /**
35645      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35646      * relative to its element of origin (defaults to "tl-bl?")
35647      */
35648     defaultAlign : "tl-bl?",
35649     /**
35650      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35651      */
35652     allowOtherMenus : false,
35653     /**
35654      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35655      */
35656     registerMenu : true,
35657
35658     hidden:true,
35659
35660     // private
35661     render : function(){
35662         if(this.el){
35663             return;
35664         }
35665         var el = this.el = new Roo.Layer({
35666             cls: "x-menu",
35667             shadow:this.shadow,
35668             constrain: false,
35669             parentEl: this.parentEl || document.body,
35670             zindex:15000
35671         });
35672
35673         this.keyNav = new Roo.menu.MenuNav(this);
35674
35675         if(this.plain){
35676             el.addClass("x-menu-plain");
35677         }
35678         if(this.cls){
35679             el.addClass(this.cls);
35680         }
35681         // generic focus element
35682         this.focusEl = el.createChild({
35683             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35684         });
35685         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35686         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35687         
35688         ul.on("mouseover", this.onMouseOver, this);
35689         ul.on("mouseout", this.onMouseOut, this);
35690         this.items.each(function(item){
35691             if (item.hidden) {
35692                 return;
35693             }
35694             
35695             var li = document.createElement("li");
35696             li.className = "x-menu-list-item";
35697             ul.dom.appendChild(li);
35698             item.render(li, this);
35699         }, this);
35700         this.ul = ul;
35701         this.autoWidth();
35702     },
35703
35704     // private
35705     autoWidth : function(){
35706         var el = this.el, ul = this.ul;
35707         if(!el){
35708             return;
35709         }
35710         var w = this.width;
35711         if(w){
35712             el.setWidth(w);
35713         }else if(Roo.isIE){
35714             el.setWidth(this.minWidth);
35715             var t = el.dom.offsetWidth; // force recalc
35716             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35717         }
35718     },
35719
35720     // private
35721     delayAutoWidth : function(){
35722         if(this.rendered){
35723             if(!this.awTask){
35724                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35725             }
35726             this.awTask.delay(20);
35727         }
35728     },
35729
35730     // private
35731     findTargetItem : function(e){
35732         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35733         if(t && t.menuItemId){
35734             return this.items.get(t.menuItemId);
35735         }
35736     },
35737
35738     // private
35739     onClick : function(e){
35740         Roo.log("menu.onClick");
35741         var t = this.findTargetItem(e);
35742         if(!t){
35743             return;
35744         }
35745         Roo.log(e);
35746         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35747             if(t == this.activeItem && t.shouldDeactivate(e)){
35748                 this.activeItem.deactivate();
35749                 delete this.activeItem;
35750                 return;
35751             }
35752             if(t.canActivate){
35753                 this.setActiveItem(t, true);
35754             }
35755             return;
35756             
35757             
35758         }
35759         
35760         t.onClick(e);
35761         this.fireEvent("click", this, t, e);
35762     },
35763
35764     // private
35765     setActiveItem : function(item, autoExpand){
35766         if(item != this.activeItem){
35767             if(this.activeItem){
35768                 this.activeItem.deactivate();
35769             }
35770             this.activeItem = item;
35771             item.activate(autoExpand);
35772         }else if(autoExpand){
35773             item.expandMenu();
35774         }
35775     },
35776
35777     // private
35778     tryActivate : function(start, step){
35779         var items = this.items;
35780         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35781             var item = items.get(i);
35782             if(!item.disabled && item.canActivate){
35783                 this.setActiveItem(item, false);
35784                 return item;
35785             }
35786         }
35787         return false;
35788     },
35789
35790     // private
35791     onMouseOver : function(e){
35792         var t;
35793         if(t = this.findTargetItem(e)){
35794             if(t.canActivate && !t.disabled){
35795                 this.setActiveItem(t, true);
35796             }
35797         }
35798         this.fireEvent("mouseover", this, e, t);
35799     },
35800
35801     // private
35802     onMouseOut : function(e){
35803         var t;
35804         if(t = this.findTargetItem(e)){
35805             if(t == this.activeItem && t.shouldDeactivate(e)){
35806                 this.activeItem.deactivate();
35807                 delete this.activeItem;
35808             }
35809         }
35810         this.fireEvent("mouseout", this, e, t);
35811     },
35812
35813     /**
35814      * Read-only.  Returns true if the menu is currently displayed, else false.
35815      * @type Boolean
35816      */
35817     isVisible : function(){
35818         return this.el && !this.hidden;
35819     },
35820
35821     /**
35822      * Displays this menu relative to another element
35823      * @param {String/HTMLElement/Roo.Element} element The element to align to
35824      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35825      * the element (defaults to this.defaultAlign)
35826      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35827      */
35828     show : function(el, pos, parentMenu){
35829         this.parentMenu = parentMenu;
35830         if(!this.el){
35831             this.render();
35832         }
35833         this.fireEvent("beforeshow", this);
35834         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35835     },
35836
35837     /**
35838      * Displays this menu at a specific xy position
35839      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35840      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35841      */
35842     showAt : function(xy, parentMenu, /* private: */_e){
35843         this.parentMenu = parentMenu;
35844         if(!this.el){
35845             this.render();
35846         }
35847         if(_e !== false){
35848             this.fireEvent("beforeshow", this);
35849             xy = this.el.adjustForConstraints(xy);
35850         }
35851         this.el.setXY(xy);
35852         this.el.show();
35853         this.hidden = false;
35854         this.focus();
35855         this.fireEvent("show", this);
35856     },
35857
35858     focus : function(){
35859         if(!this.hidden){
35860             this.doFocus.defer(50, this);
35861         }
35862     },
35863
35864     doFocus : function(){
35865         if(!this.hidden){
35866             this.focusEl.focus();
35867         }
35868     },
35869
35870     /**
35871      * Hides this menu and optionally all parent menus
35872      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35873      */
35874     hide : function(deep){
35875         if(this.el && this.isVisible()){
35876             this.fireEvent("beforehide", this);
35877             if(this.activeItem){
35878                 this.activeItem.deactivate();
35879                 this.activeItem = null;
35880             }
35881             this.el.hide();
35882             this.hidden = true;
35883             this.fireEvent("hide", this);
35884         }
35885         if(deep === true && this.parentMenu){
35886             this.parentMenu.hide(true);
35887         }
35888     },
35889
35890     /**
35891      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35892      * Any of the following are valid:
35893      * <ul>
35894      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35895      * <li>An HTMLElement object which will be converted to a menu item</li>
35896      * <li>A menu item config object that will be created as a new menu item</li>
35897      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35898      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35899      * </ul>
35900      * Usage:
35901      * <pre><code>
35902 // Create the menu
35903 var menu = new Roo.menu.Menu();
35904
35905 // Create a menu item to add by reference
35906 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35907
35908 // Add a bunch of items at once using different methods.
35909 // Only the last item added will be returned.
35910 var item = menu.add(
35911     menuItem,                // add existing item by ref
35912     'Dynamic Item',          // new TextItem
35913     '-',                     // new separator
35914     { text: 'Config Item' }  // new item by config
35915 );
35916 </code></pre>
35917      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35918      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35919      */
35920     add : function(){
35921         var a = arguments, l = a.length, item;
35922         for(var i = 0; i < l; i++){
35923             var el = a[i];
35924             if ((typeof(el) == "object") && el.xtype && el.xns) {
35925                 el = Roo.factory(el, Roo.menu);
35926             }
35927             
35928             if(el.render){ // some kind of Item
35929                 item = this.addItem(el);
35930             }else if(typeof el == "string"){ // string
35931                 if(el == "separator" || el == "-"){
35932                     item = this.addSeparator();
35933                 }else{
35934                     item = this.addText(el);
35935                 }
35936             }else if(el.tagName || el.el){ // element
35937                 item = this.addElement(el);
35938             }else if(typeof el == "object"){ // must be menu item config?
35939                 item = this.addMenuItem(el);
35940             }
35941         }
35942         return item;
35943     },
35944
35945     /**
35946      * Returns this menu's underlying {@link Roo.Element} object
35947      * @return {Roo.Element} The element
35948      */
35949     getEl : function(){
35950         if(!this.el){
35951             this.render();
35952         }
35953         return this.el;
35954     },
35955
35956     /**
35957      * Adds a separator bar to the menu
35958      * @return {Roo.menu.Item} The menu item that was added
35959      */
35960     addSeparator : function(){
35961         return this.addItem(new Roo.menu.Separator());
35962     },
35963
35964     /**
35965      * Adds an {@link Roo.Element} object to the menu
35966      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35967      * @return {Roo.menu.Item} The menu item that was added
35968      */
35969     addElement : function(el){
35970         return this.addItem(new Roo.menu.BaseItem(el));
35971     },
35972
35973     /**
35974      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35975      * @param {Roo.menu.Item} item The menu item to add
35976      * @return {Roo.menu.Item} The menu item that was added
35977      */
35978     addItem : function(item){
35979         this.items.add(item);
35980         if(this.ul){
35981             var li = document.createElement("li");
35982             li.className = "x-menu-list-item";
35983             this.ul.dom.appendChild(li);
35984             item.render(li, this);
35985             this.delayAutoWidth();
35986         }
35987         return item;
35988     },
35989
35990     /**
35991      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35992      * @param {Object} config A MenuItem config object
35993      * @return {Roo.menu.Item} The menu item that was added
35994      */
35995     addMenuItem : function(config){
35996         if(!(config instanceof Roo.menu.Item)){
35997             if(typeof config.checked == "boolean"){ // must be check menu item config?
35998                 config = new Roo.menu.CheckItem(config);
35999             }else{
36000                 config = new Roo.menu.Item(config);
36001             }
36002         }
36003         return this.addItem(config);
36004     },
36005
36006     /**
36007      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36008      * @param {String} text The text to display in the menu item
36009      * @return {Roo.menu.Item} The menu item that was added
36010      */
36011     addText : function(text){
36012         return this.addItem(new Roo.menu.TextItem({ text : text }));
36013     },
36014
36015     /**
36016      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36017      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36018      * @param {Roo.menu.Item} item The menu item to add
36019      * @return {Roo.menu.Item} The menu item that was added
36020      */
36021     insert : function(index, item){
36022         this.items.insert(index, item);
36023         if(this.ul){
36024             var li = document.createElement("li");
36025             li.className = "x-menu-list-item";
36026             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36027             item.render(li, this);
36028             this.delayAutoWidth();
36029         }
36030         return item;
36031     },
36032
36033     /**
36034      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36035      * @param {Roo.menu.Item} item The menu item to remove
36036      */
36037     remove : function(item){
36038         this.items.removeKey(item.id);
36039         item.destroy();
36040     },
36041
36042     /**
36043      * Removes and destroys all items in the menu
36044      */
36045     removeAll : function(){
36046         var f;
36047         while(f = this.items.first()){
36048             this.remove(f);
36049         }
36050     }
36051 });
36052
36053 // MenuNav is a private utility class used internally by the Menu
36054 Roo.menu.MenuNav = function(menu){
36055     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36056     this.scope = this.menu = menu;
36057 };
36058
36059 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36060     doRelay : function(e, h){
36061         var k = e.getKey();
36062         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36063             this.menu.tryActivate(0, 1);
36064             return false;
36065         }
36066         return h.call(this.scope || this, e, this.menu);
36067     },
36068
36069     up : function(e, m){
36070         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36071             m.tryActivate(m.items.length-1, -1);
36072         }
36073     },
36074
36075     down : function(e, m){
36076         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36077             m.tryActivate(0, 1);
36078         }
36079     },
36080
36081     right : function(e, m){
36082         if(m.activeItem){
36083             m.activeItem.expandMenu(true);
36084         }
36085     },
36086
36087     left : function(e, m){
36088         m.hide();
36089         if(m.parentMenu && m.parentMenu.activeItem){
36090             m.parentMenu.activeItem.activate();
36091         }
36092     },
36093
36094     enter : function(e, m){
36095         if(m.activeItem){
36096             e.stopPropagation();
36097             m.activeItem.onClick(e);
36098             m.fireEvent("click", this, m.activeItem);
36099             return true;
36100         }
36101     }
36102 });/*
36103  * Based on:
36104  * Ext JS Library 1.1.1
36105  * Copyright(c) 2006-2007, Ext JS, LLC.
36106  *
36107  * Originally Released Under LGPL - original licence link has changed is not relivant.
36108  *
36109  * Fork - LGPL
36110  * <script type="text/javascript">
36111  */
36112  
36113 /**
36114  * @class Roo.menu.MenuMgr
36115  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36116  * @singleton
36117  */
36118 Roo.menu.MenuMgr = function(){
36119    var menus, active, groups = {}, attached = false, lastShow = new Date();
36120
36121    // private - called when first menu is created
36122    function init(){
36123        menus = {};
36124        active = new Roo.util.MixedCollection();
36125        Roo.get(document).addKeyListener(27, function(){
36126            if(active.length > 0){
36127                hideAll();
36128            }
36129        });
36130    }
36131
36132    // private
36133    function hideAll(){
36134        if(active && active.length > 0){
36135            var c = active.clone();
36136            c.each(function(m){
36137                m.hide();
36138            });
36139        }
36140    }
36141
36142    // private
36143    function onHide(m){
36144        active.remove(m);
36145        if(active.length < 1){
36146            Roo.get(document).un("mousedown", onMouseDown);
36147            attached = false;
36148        }
36149    }
36150
36151    // private
36152    function onShow(m){
36153        var last = active.last();
36154        lastShow = new Date();
36155        active.add(m);
36156        if(!attached){
36157            Roo.get(document).on("mousedown", onMouseDown);
36158            attached = true;
36159        }
36160        if(m.parentMenu){
36161           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36162           m.parentMenu.activeChild = m;
36163        }else if(last && last.isVisible()){
36164           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36165        }
36166    }
36167
36168    // private
36169    function onBeforeHide(m){
36170        if(m.activeChild){
36171            m.activeChild.hide();
36172        }
36173        if(m.autoHideTimer){
36174            clearTimeout(m.autoHideTimer);
36175            delete m.autoHideTimer;
36176        }
36177    }
36178
36179    // private
36180    function onBeforeShow(m){
36181        var pm = m.parentMenu;
36182        if(!pm && !m.allowOtherMenus){
36183            hideAll();
36184        }else if(pm && pm.activeChild && active != m){
36185            pm.activeChild.hide();
36186        }
36187    }
36188
36189    // private
36190    function onMouseDown(e){
36191        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36192            hideAll();
36193        }
36194    }
36195
36196    // private
36197    function onBeforeCheck(mi, state){
36198        if(state){
36199            var g = groups[mi.group];
36200            for(var i = 0, l = g.length; i < l; i++){
36201                if(g[i] != mi){
36202                    g[i].setChecked(false);
36203                }
36204            }
36205        }
36206    }
36207
36208    return {
36209
36210        /**
36211         * Hides all menus that are currently visible
36212         */
36213        hideAll : function(){
36214             hideAll();  
36215        },
36216
36217        // private
36218        register : function(menu){
36219            if(!menus){
36220                init();
36221            }
36222            menus[menu.id] = menu;
36223            menu.on("beforehide", onBeforeHide);
36224            menu.on("hide", onHide);
36225            menu.on("beforeshow", onBeforeShow);
36226            menu.on("show", onShow);
36227            var g = menu.group;
36228            if(g && menu.events["checkchange"]){
36229                if(!groups[g]){
36230                    groups[g] = [];
36231                }
36232                groups[g].push(menu);
36233                menu.on("checkchange", onCheck);
36234            }
36235        },
36236
36237         /**
36238          * Returns a {@link Roo.menu.Menu} object
36239          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36240          * be used to generate and return a new Menu instance.
36241          */
36242        get : function(menu){
36243            if(typeof menu == "string"){ // menu id
36244                return menus[menu];
36245            }else if(menu.events){  // menu instance
36246                return menu;
36247            }else if(typeof menu.length == 'number'){ // array of menu items?
36248                return new Roo.menu.Menu({items:menu});
36249            }else{ // otherwise, must be a config
36250                return new Roo.menu.Menu(menu);
36251            }
36252        },
36253
36254        // private
36255        unregister : function(menu){
36256            delete menus[menu.id];
36257            menu.un("beforehide", onBeforeHide);
36258            menu.un("hide", onHide);
36259            menu.un("beforeshow", onBeforeShow);
36260            menu.un("show", onShow);
36261            var g = menu.group;
36262            if(g && menu.events["checkchange"]){
36263                groups[g].remove(menu);
36264                menu.un("checkchange", onCheck);
36265            }
36266        },
36267
36268        // private
36269        registerCheckable : function(menuItem){
36270            var g = menuItem.group;
36271            if(g){
36272                if(!groups[g]){
36273                    groups[g] = [];
36274                }
36275                groups[g].push(menuItem);
36276                menuItem.on("beforecheckchange", onBeforeCheck);
36277            }
36278        },
36279
36280        // private
36281        unregisterCheckable : function(menuItem){
36282            var g = menuItem.group;
36283            if(g){
36284                groups[g].remove(menuItem);
36285                menuItem.un("beforecheckchange", onBeforeCheck);
36286            }
36287        }
36288    };
36289 }();/*
36290  * Based on:
36291  * Ext JS Library 1.1.1
36292  * Copyright(c) 2006-2007, Ext JS, LLC.
36293  *
36294  * Originally Released Under LGPL - original licence link has changed is not relivant.
36295  *
36296  * Fork - LGPL
36297  * <script type="text/javascript">
36298  */
36299  
36300
36301 /**
36302  * @class Roo.menu.BaseItem
36303  * @extends Roo.Component
36304  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36305  * management and base configuration options shared by all menu components.
36306  * @constructor
36307  * Creates a new BaseItem
36308  * @param {Object} config Configuration options
36309  */
36310 Roo.menu.BaseItem = function(config){
36311     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36312
36313     this.addEvents({
36314         /**
36315          * @event click
36316          * Fires when this item is clicked
36317          * @param {Roo.menu.BaseItem} this
36318          * @param {Roo.EventObject} e
36319          */
36320         click: true,
36321         /**
36322          * @event activate
36323          * Fires when this item is activated
36324          * @param {Roo.menu.BaseItem} this
36325          */
36326         activate : true,
36327         /**
36328          * @event deactivate
36329          * Fires when this item is deactivated
36330          * @param {Roo.menu.BaseItem} this
36331          */
36332         deactivate : true
36333     });
36334
36335     if(this.handler){
36336         this.on("click", this.handler, this.scope, true);
36337     }
36338 };
36339
36340 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36341     /**
36342      * @cfg {Function} handler
36343      * A function that will handle the click event of this menu item (defaults to undefined)
36344      */
36345     /**
36346      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36347      */
36348     canActivate : false,
36349     
36350      /**
36351      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36352      */
36353     hidden: false,
36354     
36355     /**
36356      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36357      */
36358     activeClass : "x-menu-item-active",
36359     /**
36360      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36361      */
36362     hideOnClick : true,
36363     /**
36364      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36365      */
36366     hideDelay : 100,
36367
36368     // private
36369     ctype: "Roo.menu.BaseItem",
36370
36371     // private
36372     actionMode : "container",
36373
36374     // private
36375     render : function(container, parentMenu){
36376         this.parentMenu = parentMenu;
36377         Roo.menu.BaseItem.superclass.render.call(this, container);
36378         this.container.menuItemId = this.id;
36379     },
36380
36381     // private
36382     onRender : function(container, position){
36383         this.el = Roo.get(this.el);
36384         container.dom.appendChild(this.el.dom);
36385     },
36386
36387     // private
36388     onClick : function(e){
36389         if(!this.disabled && this.fireEvent("click", this, e) !== false
36390                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36391             this.handleClick(e);
36392         }else{
36393             e.stopEvent();
36394         }
36395     },
36396
36397     // private
36398     activate : function(){
36399         if(this.disabled){
36400             return false;
36401         }
36402         var li = this.container;
36403         li.addClass(this.activeClass);
36404         this.region = li.getRegion().adjust(2, 2, -2, -2);
36405         this.fireEvent("activate", this);
36406         return true;
36407     },
36408
36409     // private
36410     deactivate : function(){
36411         this.container.removeClass(this.activeClass);
36412         this.fireEvent("deactivate", this);
36413     },
36414
36415     // private
36416     shouldDeactivate : function(e){
36417         return !this.region || !this.region.contains(e.getPoint());
36418     },
36419
36420     // private
36421     handleClick : function(e){
36422         if(this.hideOnClick){
36423             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36424         }
36425     },
36426
36427     // private
36428     expandMenu : function(autoActivate){
36429         // do nothing
36430     },
36431
36432     // private
36433     hideMenu : function(){
36434         // do nothing
36435     }
36436 });/*
36437  * Based on:
36438  * Ext JS Library 1.1.1
36439  * Copyright(c) 2006-2007, Ext JS, LLC.
36440  *
36441  * Originally Released Under LGPL - original licence link has changed is not relivant.
36442  *
36443  * Fork - LGPL
36444  * <script type="text/javascript">
36445  */
36446  
36447 /**
36448  * @class Roo.menu.Adapter
36449  * @extends Roo.menu.BaseItem
36450  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
36451  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36452  * @constructor
36453  * Creates a new Adapter
36454  * @param {Object} config Configuration options
36455  */
36456 Roo.menu.Adapter = function(component, config){
36457     Roo.menu.Adapter.superclass.constructor.call(this, config);
36458     this.component = component;
36459 };
36460 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36461     // private
36462     canActivate : true,
36463
36464     // private
36465     onRender : function(container, position){
36466         this.component.render(container);
36467         this.el = this.component.getEl();
36468     },
36469
36470     // private
36471     activate : function(){
36472         if(this.disabled){
36473             return false;
36474         }
36475         this.component.focus();
36476         this.fireEvent("activate", this);
36477         return true;
36478     },
36479
36480     // private
36481     deactivate : function(){
36482         this.fireEvent("deactivate", this);
36483     },
36484
36485     // private
36486     disable : function(){
36487         this.component.disable();
36488         Roo.menu.Adapter.superclass.disable.call(this);
36489     },
36490
36491     // private
36492     enable : function(){
36493         this.component.enable();
36494         Roo.menu.Adapter.superclass.enable.call(this);
36495     }
36496 });/*
36497  * Based on:
36498  * Ext JS Library 1.1.1
36499  * Copyright(c) 2006-2007, Ext JS, LLC.
36500  *
36501  * Originally Released Under LGPL - original licence link has changed is not relivant.
36502  *
36503  * Fork - LGPL
36504  * <script type="text/javascript">
36505  */
36506
36507 /**
36508  * @class Roo.menu.TextItem
36509  * @extends Roo.menu.BaseItem
36510  * Adds a static text string to a menu, usually used as either a heading or group separator.
36511  * Note: old style constructor with text is still supported.
36512  * 
36513  * @constructor
36514  * Creates a new TextItem
36515  * @param {Object} cfg Configuration
36516  */
36517 Roo.menu.TextItem = function(cfg){
36518     if (typeof(cfg) == 'string') {
36519         this.text = cfg;
36520     } else {
36521         Roo.apply(this,cfg);
36522     }
36523     
36524     Roo.menu.TextItem.superclass.constructor.call(this);
36525 };
36526
36527 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36528     /**
36529      * @cfg {Boolean} text Text to show on item.
36530      */
36531     text : '',
36532     
36533     /**
36534      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36535      */
36536     hideOnClick : false,
36537     /**
36538      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36539      */
36540     itemCls : "x-menu-text",
36541
36542     // private
36543     onRender : function(){
36544         var s = document.createElement("span");
36545         s.className = this.itemCls;
36546         s.innerHTML = this.text;
36547         this.el = s;
36548         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36549     }
36550 });/*
36551  * Based on:
36552  * Ext JS Library 1.1.1
36553  * Copyright(c) 2006-2007, Ext JS, LLC.
36554  *
36555  * Originally Released Under LGPL - original licence link has changed is not relivant.
36556  *
36557  * Fork - LGPL
36558  * <script type="text/javascript">
36559  */
36560
36561 /**
36562  * @class Roo.menu.Separator
36563  * @extends Roo.menu.BaseItem
36564  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36565  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36566  * @constructor
36567  * @param {Object} config Configuration options
36568  */
36569 Roo.menu.Separator = function(config){
36570     Roo.menu.Separator.superclass.constructor.call(this, config);
36571 };
36572
36573 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36574     /**
36575      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36576      */
36577     itemCls : "x-menu-sep",
36578     /**
36579      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36580      */
36581     hideOnClick : false,
36582
36583     // private
36584     onRender : function(li){
36585         var s = document.createElement("span");
36586         s.className = this.itemCls;
36587         s.innerHTML = "&#160;";
36588         this.el = s;
36589         li.addClass("x-menu-sep-li");
36590         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36591     }
36592 });/*
36593  * Based on:
36594  * Ext JS Library 1.1.1
36595  * Copyright(c) 2006-2007, Ext JS, LLC.
36596  *
36597  * Originally Released Under LGPL - original licence link has changed is not relivant.
36598  *
36599  * Fork - LGPL
36600  * <script type="text/javascript">
36601  */
36602 /**
36603  * @class Roo.menu.Item
36604  * @extends Roo.menu.BaseItem
36605  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36606  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36607  * activation and click handling.
36608  * @constructor
36609  * Creates a new Item
36610  * @param {Object} config Configuration options
36611  */
36612 Roo.menu.Item = function(config){
36613     Roo.menu.Item.superclass.constructor.call(this, config);
36614     if(this.menu){
36615         this.menu = Roo.menu.MenuMgr.get(this.menu);
36616     }
36617 };
36618 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36619     
36620     /**
36621      * @cfg {String} text
36622      * The text to show on the menu item.
36623      */
36624     text: '',
36625      /**
36626      * @cfg {String} HTML to render in menu
36627      * The text to show on the menu item (HTML version).
36628      */
36629     html: '',
36630     /**
36631      * @cfg {String} icon
36632      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36633      */
36634     icon: undefined,
36635     /**
36636      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36637      */
36638     itemCls : "x-menu-item",
36639     /**
36640      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36641      */
36642     canActivate : true,
36643     /**
36644      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36645      */
36646     showDelay: 200,
36647     // doc'd in BaseItem
36648     hideDelay: 200,
36649
36650     // private
36651     ctype: "Roo.menu.Item",
36652     
36653     // private
36654     onRender : function(container, position){
36655         var el = document.createElement("a");
36656         el.hideFocus = true;
36657         el.unselectable = "on";
36658         el.href = this.href || "#";
36659         if(this.hrefTarget){
36660             el.target = this.hrefTarget;
36661         }
36662         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36663         
36664         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36665         
36666         el.innerHTML = String.format(
36667                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36668                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36669         this.el = el;
36670         Roo.menu.Item.superclass.onRender.call(this, container, position);
36671     },
36672
36673     /**
36674      * Sets the text to display in this menu item
36675      * @param {String} text The text to display
36676      * @param {Boolean} isHTML true to indicate text is pure html.
36677      */
36678     setText : function(text, isHTML){
36679         if (isHTML) {
36680             this.html = text;
36681         } else {
36682             this.text = text;
36683             this.html = '';
36684         }
36685         if(this.rendered){
36686             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36687      
36688             this.el.update(String.format(
36689                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36690                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36691             this.parentMenu.autoWidth();
36692         }
36693     },
36694
36695     // private
36696     handleClick : function(e){
36697         if(!this.href){ // if no link defined, stop the event automatically
36698             e.stopEvent();
36699         }
36700         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36701     },
36702
36703     // private
36704     activate : function(autoExpand){
36705         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36706             this.focus();
36707             if(autoExpand){
36708                 this.expandMenu();
36709             }
36710         }
36711         return true;
36712     },
36713
36714     // private
36715     shouldDeactivate : function(e){
36716         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36717             if(this.menu && this.menu.isVisible()){
36718                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36719             }
36720             return true;
36721         }
36722         return false;
36723     },
36724
36725     // private
36726     deactivate : function(){
36727         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36728         this.hideMenu();
36729     },
36730
36731     // private
36732     expandMenu : function(autoActivate){
36733         if(!this.disabled && this.menu){
36734             clearTimeout(this.hideTimer);
36735             delete this.hideTimer;
36736             if(!this.menu.isVisible() && !this.showTimer){
36737                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36738             }else if (this.menu.isVisible() && autoActivate){
36739                 this.menu.tryActivate(0, 1);
36740             }
36741         }
36742     },
36743
36744     // private
36745     deferExpand : function(autoActivate){
36746         delete this.showTimer;
36747         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36748         if(autoActivate){
36749             this.menu.tryActivate(0, 1);
36750         }
36751     },
36752
36753     // private
36754     hideMenu : function(){
36755         clearTimeout(this.showTimer);
36756         delete this.showTimer;
36757         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36758             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36759         }
36760     },
36761
36762     // private
36763     deferHide : function(){
36764         delete this.hideTimer;
36765         this.menu.hide();
36766     }
36767 });/*
36768  * Based on:
36769  * Ext JS Library 1.1.1
36770  * Copyright(c) 2006-2007, Ext JS, LLC.
36771  *
36772  * Originally Released Under LGPL - original licence link has changed is not relivant.
36773  *
36774  * Fork - LGPL
36775  * <script type="text/javascript">
36776  */
36777  
36778 /**
36779  * @class Roo.menu.CheckItem
36780  * @extends Roo.menu.Item
36781  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36782  * @constructor
36783  * Creates a new CheckItem
36784  * @param {Object} config Configuration options
36785  */
36786 Roo.menu.CheckItem = function(config){
36787     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36788     this.addEvents({
36789         /**
36790          * @event beforecheckchange
36791          * Fires before the checked value is set, providing an opportunity to cancel if needed
36792          * @param {Roo.menu.CheckItem} this
36793          * @param {Boolean} checked The new checked value that will be set
36794          */
36795         "beforecheckchange" : true,
36796         /**
36797          * @event checkchange
36798          * Fires after the checked value has been set
36799          * @param {Roo.menu.CheckItem} this
36800          * @param {Boolean} checked The checked value that was set
36801          */
36802         "checkchange" : true
36803     });
36804     if(this.checkHandler){
36805         this.on('checkchange', this.checkHandler, this.scope);
36806     }
36807 };
36808 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36809     /**
36810      * @cfg {String} group
36811      * All check items with the same group name will automatically be grouped into a single-select
36812      * radio button group (defaults to '')
36813      */
36814     /**
36815      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36816      */
36817     itemCls : "x-menu-item x-menu-check-item",
36818     /**
36819      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36820      */
36821     groupClass : "x-menu-group-item",
36822
36823     /**
36824      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36825      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36826      * initialized with checked = true will be rendered as checked.
36827      */
36828     checked: false,
36829
36830     // private
36831     ctype: "Roo.menu.CheckItem",
36832
36833     // private
36834     onRender : function(c){
36835         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36836         if(this.group){
36837             this.el.addClass(this.groupClass);
36838         }
36839         Roo.menu.MenuMgr.registerCheckable(this);
36840         if(this.checked){
36841             this.checked = false;
36842             this.setChecked(true, true);
36843         }
36844     },
36845
36846     // private
36847     destroy : function(){
36848         if(this.rendered){
36849             Roo.menu.MenuMgr.unregisterCheckable(this);
36850         }
36851         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36852     },
36853
36854     /**
36855      * Set the checked state of this item
36856      * @param {Boolean} checked The new checked value
36857      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36858      */
36859     setChecked : function(state, suppressEvent){
36860         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36861             if(this.container){
36862                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36863             }
36864             this.checked = state;
36865             if(suppressEvent !== true){
36866                 this.fireEvent("checkchange", this, state);
36867             }
36868         }
36869     },
36870
36871     // private
36872     handleClick : function(e){
36873        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36874            this.setChecked(!this.checked);
36875        }
36876        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36877     }
36878 });/*
36879  * Based on:
36880  * Ext JS Library 1.1.1
36881  * Copyright(c) 2006-2007, Ext JS, LLC.
36882  *
36883  * Originally Released Under LGPL - original licence link has changed is not relivant.
36884  *
36885  * Fork - LGPL
36886  * <script type="text/javascript">
36887  */
36888  
36889 /**
36890  * @class Roo.menu.DateItem
36891  * @extends Roo.menu.Adapter
36892  * A menu item that wraps the {@link Roo.DatPicker} component.
36893  * @constructor
36894  * Creates a new DateItem
36895  * @param {Object} config Configuration options
36896  */
36897 Roo.menu.DateItem = function(config){
36898     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36899     /** The Roo.DatePicker object @type Roo.DatePicker */
36900     this.picker = this.component;
36901     this.addEvents({select: true});
36902     
36903     this.picker.on("render", function(picker){
36904         picker.getEl().swallowEvent("click");
36905         picker.container.addClass("x-menu-date-item");
36906     });
36907
36908     this.picker.on("select", this.onSelect, this);
36909 };
36910
36911 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36912     // private
36913     onSelect : function(picker, date){
36914         this.fireEvent("select", this, date, picker);
36915         Roo.menu.DateItem.superclass.handleClick.call(this);
36916     }
36917 });/*
36918  * Based on:
36919  * Ext JS Library 1.1.1
36920  * Copyright(c) 2006-2007, Ext JS, LLC.
36921  *
36922  * Originally Released Under LGPL - original licence link has changed is not relivant.
36923  *
36924  * Fork - LGPL
36925  * <script type="text/javascript">
36926  */
36927  
36928 /**
36929  * @class Roo.menu.ColorItem
36930  * @extends Roo.menu.Adapter
36931  * A menu item that wraps the {@link Roo.ColorPalette} component.
36932  * @constructor
36933  * Creates a new ColorItem
36934  * @param {Object} config Configuration options
36935  */
36936 Roo.menu.ColorItem = function(config){
36937     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36938     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36939     this.palette = this.component;
36940     this.relayEvents(this.palette, ["select"]);
36941     if(this.selectHandler){
36942         this.on('select', this.selectHandler, this.scope);
36943     }
36944 };
36945 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36946  * Based on:
36947  * Ext JS Library 1.1.1
36948  * Copyright(c) 2006-2007, Ext JS, LLC.
36949  *
36950  * Originally Released Under LGPL - original licence link has changed is not relivant.
36951  *
36952  * Fork - LGPL
36953  * <script type="text/javascript">
36954  */
36955  
36956
36957 /**
36958  * @class Roo.menu.DateMenu
36959  * @extends Roo.menu.Menu
36960  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36961  * @constructor
36962  * Creates a new DateMenu
36963  * @param {Object} config Configuration options
36964  */
36965 Roo.menu.DateMenu = function(config){
36966     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36967     this.plain = true;
36968     var di = new Roo.menu.DateItem(config);
36969     this.add(di);
36970     /**
36971      * The {@link Roo.DatePicker} instance for this DateMenu
36972      * @type DatePicker
36973      */
36974     this.picker = di.picker;
36975     /**
36976      * @event select
36977      * @param {DatePicker} picker
36978      * @param {Date} date
36979      */
36980     this.relayEvents(di, ["select"]);
36981     this.on('beforeshow', function(){
36982         if(this.picker){
36983             this.picker.hideMonthPicker(false);
36984         }
36985     }, this);
36986 };
36987 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36988     cls:'x-date-menu'
36989 });/*
36990  * Based on:
36991  * Ext JS Library 1.1.1
36992  * Copyright(c) 2006-2007, Ext JS, LLC.
36993  *
36994  * Originally Released Under LGPL - original licence link has changed is not relivant.
36995  *
36996  * Fork - LGPL
36997  * <script type="text/javascript">
36998  */
36999  
37000
37001 /**
37002  * @class Roo.menu.ColorMenu
37003  * @extends Roo.menu.Menu
37004  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37005  * @constructor
37006  * Creates a new ColorMenu
37007  * @param {Object} config Configuration options
37008  */
37009 Roo.menu.ColorMenu = function(config){
37010     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37011     this.plain = true;
37012     var ci = new Roo.menu.ColorItem(config);
37013     this.add(ci);
37014     /**
37015      * The {@link Roo.ColorPalette} instance for this ColorMenu
37016      * @type ColorPalette
37017      */
37018     this.palette = ci.palette;
37019     /**
37020      * @event select
37021      * @param {ColorPalette} palette
37022      * @param {String} color
37023      */
37024     this.relayEvents(ci, ["select"]);
37025 };
37026 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37027  * Based on:
37028  * Ext JS Library 1.1.1
37029  * Copyright(c) 2006-2007, Ext JS, LLC.
37030  *
37031  * Originally Released Under LGPL - original licence link has changed is not relivant.
37032  *
37033  * Fork - LGPL
37034  * <script type="text/javascript">
37035  */
37036  
37037 /**
37038  * @class Roo.form.Field
37039  * @extends Roo.BoxComponent
37040  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37041  * @constructor
37042  * Creates a new Field
37043  * @param {Object} config Configuration options
37044  */
37045 Roo.form.Field = function(config){
37046     Roo.form.Field.superclass.constructor.call(this, config);
37047 };
37048
37049 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37050     /**
37051      * @cfg {String} fieldLabel Label to use when rendering a form.
37052      */
37053        /**
37054      * @cfg {String} qtip Mouse over tip
37055      */
37056      
37057     /**
37058      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37059      */
37060     invalidClass : "x-form-invalid",
37061     /**
37062      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
37063      */
37064     invalidText : "The value in this field is invalid",
37065     /**
37066      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37067      */
37068     focusClass : "x-form-focus",
37069     /**
37070      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37071       automatic validation (defaults to "keyup").
37072      */
37073     validationEvent : "keyup",
37074     /**
37075      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37076      */
37077     validateOnBlur : true,
37078     /**
37079      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37080      */
37081     validationDelay : 250,
37082     /**
37083      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37084      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37085      */
37086     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37087     /**
37088      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37089      */
37090     fieldClass : "x-form-field",
37091     /**
37092      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37093      *<pre>
37094 Value         Description
37095 -----------   ----------------------------------------------------------------------
37096 qtip          Display a quick tip when the user hovers over the field
37097 title         Display a default browser title attribute popup
37098 under         Add a block div beneath the field containing the error text
37099 side          Add an error icon to the right of the field with a popup on hover
37100 [element id]  Add the error text directly to the innerHTML of the specified element
37101 </pre>
37102      */
37103     msgTarget : 'qtip',
37104     /**
37105      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37106      */
37107     msgFx : 'normal',
37108
37109     /**
37110      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
37111      */
37112     readOnly : false,
37113
37114     /**
37115      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37116      */
37117     disabled : false,
37118
37119     /**
37120      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37121      */
37122     inputType : undefined,
37123     
37124     /**
37125      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
37126          */
37127         tabIndex : undefined,
37128         
37129     // private
37130     isFormField : true,
37131
37132     // private
37133     hasFocus : false,
37134     /**
37135      * @property {Roo.Element} fieldEl
37136      * Element Containing the rendered Field (with label etc.)
37137      */
37138     /**
37139      * @cfg {Mixed} value A value to initialize this field with.
37140      */
37141     value : undefined,
37142
37143     /**
37144      * @cfg {String} name The field's HTML name attribute.
37145      */
37146     /**
37147      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37148      */
37149
37150         // private ??
37151         initComponent : function(){
37152         Roo.form.Field.superclass.initComponent.call(this);
37153         this.addEvents({
37154             /**
37155              * @event focus
37156              * Fires when this field receives input focus.
37157              * @param {Roo.form.Field} this
37158              */
37159             focus : true,
37160             /**
37161              * @event blur
37162              * Fires when this field loses input focus.
37163              * @param {Roo.form.Field} this
37164              */
37165             blur : true,
37166             /**
37167              * @event specialkey
37168              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37169              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37170              * @param {Roo.form.Field} this
37171              * @param {Roo.EventObject} e The event object
37172              */
37173             specialkey : true,
37174             /**
37175              * @event change
37176              * Fires just before the field blurs if the field value has changed.
37177              * @param {Roo.form.Field} this
37178              * @param {Mixed} newValue The new value
37179              * @param {Mixed} oldValue The original value
37180              */
37181             change : true,
37182             /**
37183              * @event invalid
37184              * Fires after the field has been marked as invalid.
37185              * @param {Roo.form.Field} this
37186              * @param {String} msg The validation message
37187              */
37188             invalid : true,
37189             /**
37190              * @event valid
37191              * Fires after the field has been validated with no errors.
37192              * @param {Roo.form.Field} this
37193              */
37194             valid : true,
37195              /**
37196              * @event keyup
37197              * Fires after the key up
37198              * @param {Roo.form.Field} this
37199              * @param {Roo.EventObject}  e The event Object
37200              */
37201             keyup : true
37202         });
37203     },
37204
37205     /**
37206      * Returns the name attribute of the field if available
37207      * @return {String} name The field name
37208      */
37209     getName: function(){
37210          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37211     },
37212
37213     // private
37214     onRender : function(ct, position){
37215         Roo.form.Field.superclass.onRender.call(this, ct, position);
37216         if(!this.el){
37217             var cfg = this.getAutoCreate();
37218             if(!cfg.name){
37219                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37220             }
37221             if (!cfg.name.length) {
37222                 delete cfg.name;
37223             }
37224             if(this.inputType){
37225                 cfg.type = this.inputType;
37226             }
37227             this.el = ct.createChild(cfg, position);
37228         }
37229         var type = this.el.dom.type;
37230         if(type){
37231             if(type == 'password'){
37232                 type = 'text';
37233             }
37234             this.el.addClass('x-form-'+type);
37235         }
37236         if(this.readOnly){
37237             this.el.dom.readOnly = true;
37238         }
37239         if(this.tabIndex !== undefined){
37240             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37241         }
37242
37243         this.el.addClass([this.fieldClass, this.cls]);
37244         this.initValue();
37245     },
37246
37247     /**
37248      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37249      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37250      * @return {Roo.form.Field} this
37251      */
37252     applyTo : function(target){
37253         this.allowDomMove = false;
37254         this.el = Roo.get(target);
37255         this.render(this.el.dom.parentNode);
37256         return this;
37257     },
37258
37259     // private
37260     initValue : function(){
37261         if(this.value !== undefined){
37262             this.setValue(this.value);
37263         }else if(this.el.dom.value.length > 0){
37264             this.setValue(this.el.dom.value);
37265         }
37266     },
37267
37268     /**
37269      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37270      */
37271     isDirty : function() {
37272         if(this.disabled) {
37273             return false;
37274         }
37275         return String(this.getValue()) !== String(this.originalValue);
37276     },
37277
37278     // private
37279     afterRender : function(){
37280         Roo.form.Field.superclass.afterRender.call(this);
37281         this.initEvents();
37282     },
37283
37284     // private
37285     fireKey : function(e){
37286         //Roo.log('field ' + e.getKey());
37287         if(e.isNavKeyPress()){
37288             this.fireEvent("specialkey", this, e);
37289         }
37290     },
37291
37292     /**
37293      * Resets the current field value to the originally loaded value and clears any validation messages
37294      */
37295     reset : function(){
37296         this.setValue(this.resetValue);
37297         this.clearInvalid();
37298     },
37299
37300     // private
37301     initEvents : function(){
37302         // safari killled keypress - so keydown is now used..
37303         this.el.on("keydown" , this.fireKey,  this);
37304         this.el.on("focus", this.onFocus,  this);
37305         this.el.on("blur", this.onBlur,  this);
37306         this.el.relayEvent('keyup', this);
37307
37308         // reference to original value for reset
37309         this.originalValue = this.getValue();
37310         this.resetValue =  this.getValue();
37311     },
37312
37313     // private
37314     onFocus : function(){
37315         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37316             this.el.addClass(this.focusClass);
37317         }
37318         if(!this.hasFocus){
37319             this.hasFocus = true;
37320             this.startValue = this.getValue();
37321             this.fireEvent("focus", this);
37322         }
37323     },
37324
37325     beforeBlur : Roo.emptyFn,
37326
37327     // private
37328     onBlur : function(){
37329         this.beforeBlur();
37330         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37331             this.el.removeClass(this.focusClass);
37332         }
37333         this.hasFocus = false;
37334         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37335             this.validate();
37336         }
37337         var v = this.getValue();
37338         if(String(v) !== String(this.startValue)){
37339             this.fireEvent('change', this, v, this.startValue);
37340         }
37341         this.fireEvent("blur", this);
37342     },
37343
37344     /**
37345      * Returns whether or not the field value is currently valid
37346      * @param {Boolean} preventMark True to disable marking the field invalid
37347      * @return {Boolean} True if the value is valid, else false
37348      */
37349     isValid : function(preventMark){
37350         if(this.disabled){
37351             return true;
37352         }
37353         var restore = this.preventMark;
37354         this.preventMark = preventMark === true;
37355         var v = this.validateValue(this.processValue(this.getRawValue()));
37356         this.preventMark = restore;
37357         return v;
37358     },
37359
37360     /**
37361      * Validates the field value
37362      * @return {Boolean} True if the value is valid, else false
37363      */
37364     validate : function(){
37365         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37366             this.clearInvalid();
37367             return true;
37368         }
37369         return false;
37370     },
37371
37372     processValue : function(value){
37373         return value;
37374     },
37375
37376     // private
37377     // Subclasses should provide the validation implementation by overriding this
37378     validateValue : function(value){
37379         return true;
37380     },
37381
37382     /**
37383      * Mark this field as invalid
37384      * @param {String} msg The validation message
37385      */
37386     markInvalid : function(msg){
37387         if(!this.rendered || this.preventMark){ // not rendered
37388             return;
37389         }
37390         
37391         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37392         
37393         obj.el.addClass(this.invalidClass);
37394         msg = msg || this.invalidText;
37395         switch(this.msgTarget){
37396             case 'qtip':
37397                 obj.el.dom.qtip = msg;
37398                 obj.el.dom.qclass = 'x-form-invalid-tip';
37399                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37400                     Roo.QuickTips.enable();
37401                 }
37402                 break;
37403             case 'title':
37404                 this.el.dom.title = msg;
37405                 break;
37406             case 'under':
37407                 if(!this.errorEl){
37408                     var elp = this.el.findParent('.x-form-element', 5, true);
37409                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37410                     this.errorEl.setWidth(elp.getWidth(true)-20);
37411                 }
37412                 this.errorEl.update(msg);
37413                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37414                 break;
37415             case 'side':
37416                 if(!this.errorIcon){
37417                     var elp = this.el.findParent('.x-form-element', 5, true);
37418                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37419                 }
37420                 this.alignErrorIcon();
37421                 this.errorIcon.dom.qtip = msg;
37422                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37423                 this.errorIcon.show();
37424                 this.on('resize', this.alignErrorIcon, this);
37425                 break;
37426             default:
37427                 var t = Roo.getDom(this.msgTarget);
37428                 t.innerHTML = msg;
37429                 t.style.display = this.msgDisplay;
37430                 break;
37431         }
37432         this.fireEvent('invalid', this, msg);
37433     },
37434
37435     // private
37436     alignErrorIcon : function(){
37437         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37438     },
37439
37440     /**
37441      * Clear any invalid styles/messages for this field
37442      */
37443     clearInvalid : function(){
37444         if(!this.rendered || this.preventMark){ // not rendered
37445             return;
37446         }
37447         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37448         
37449         obj.el.removeClass(this.invalidClass);
37450         switch(this.msgTarget){
37451             case 'qtip':
37452                 obj.el.dom.qtip = '';
37453                 break;
37454             case 'title':
37455                 this.el.dom.title = '';
37456                 break;
37457             case 'under':
37458                 if(this.errorEl){
37459                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37460                 }
37461                 break;
37462             case 'side':
37463                 if(this.errorIcon){
37464                     this.errorIcon.dom.qtip = '';
37465                     this.errorIcon.hide();
37466                     this.un('resize', this.alignErrorIcon, this);
37467                 }
37468                 break;
37469             default:
37470                 var t = Roo.getDom(this.msgTarget);
37471                 t.innerHTML = '';
37472                 t.style.display = 'none';
37473                 break;
37474         }
37475         this.fireEvent('valid', this);
37476     },
37477
37478     /**
37479      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37480      * @return {Mixed} value The field value
37481      */
37482     getRawValue : function(){
37483         var v = this.el.getValue();
37484         
37485         return v;
37486     },
37487
37488     /**
37489      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37490      * @return {Mixed} value The field value
37491      */
37492     getValue : function(){
37493         var v = this.el.getValue();
37494          
37495         return v;
37496     },
37497
37498     /**
37499      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37500      * @param {Mixed} value The value to set
37501      */
37502     setRawValue : function(v){
37503         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37504     },
37505
37506     /**
37507      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37508      * @param {Mixed} value The value to set
37509      */
37510     setValue : function(v){
37511         this.value = v;
37512         if(this.rendered){
37513             this.el.dom.value = (v === null || v === undefined ? '' : v);
37514              this.validate();
37515         }
37516     },
37517
37518     adjustSize : function(w, h){
37519         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37520         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37521         return s;
37522     },
37523
37524     adjustWidth : function(tag, w){
37525         tag = tag.toLowerCase();
37526         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37527             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37528                 if(tag == 'input'){
37529                     return w + 2;
37530                 }
37531                 if(tag == 'textarea'){
37532                     return w-2;
37533                 }
37534             }else if(Roo.isOpera){
37535                 if(tag == 'input'){
37536                     return w + 2;
37537                 }
37538                 if(tag == 'textarea'){
37539                     return w-2;
37540                 }
37541             }
37542         }
37543         return w;
37544     }
37545 });
37546
37547
37548 // anything other than normal should be considered experimental
37549 Roo.form.Field.msgFx = {
37550     normal : {
37551         show: function(msgEl, f){
37552             msgEl.setDisplayed('block');
37553         },
37554
37555         hide : function(msgEl, f){
37556             msgEl.setDisplayed(false).update('');
37557         }
37558     },
37559
37560     slide : {
37561         show: function(msgEl, f){
37562             msgEl.slideIn('t', {stopFx:true});
37563         },
37564
37565         hide : function(msgEl, f){
37566             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37567         }
37568     },
37569
37570     slideRight : {
37571         show: function(msgEl, f){
37572             msgEl.fixDisplay();
37573             msgEl.alignTo(f.el, 'tl-tr');
37574             msgEl.slideIn('l', {stopFx:true});
37575         },
37576
37577         hide : function(msgEl, f){
37578             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37579         }
37580     }
37581 };/*
37582  * Based on:
37583  * Ext JS Library 1.1.1
37584  * Copyright(c) 2006-2007, Ext JS, LLC.
37585  *
37586  * Originally Released Under LGPL - original licence link has changed is not relivant.
37587  *
37588  * Fork - LGPL
37589  * <script type="text/javascript">
37590  */
37591  
37592
37593 /**
37594  * @class Roo.form.TextField
37595  * @extends Roo.form.Field
37596  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37597  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37598  * @constructor
37599  * Creates a new TextField
37600  * @param {Object} config Configuration options
37601  */
37602 Roo.form.TextField = function(config){
37603     Roo.form.TextField.superclass.constructor.call(this, config);
37604     this.addEvents({
37605         /**
37606          * @event autosize
37607          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37608          * according to the default logic, but this event provides a hook for the developer to apply additional
37609          * logic at runtime to resize the field if needed.
37610              * @param {Roo.form.Field} this This text field
37611              * @param {Number} width The new field width
37612              */
37613         autosize : true
37614     });
37615 };
37616
37617 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37618     /**
37619      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37620      */
37621     grow : false,
37622     /**
37623      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37624      */
37625     growMin : 30,
37626     /**
37627      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37628      */
37629     growMax : 800,
37630     /**
37631      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37632      */
37633     vtype : null,
37634     /**
37635      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37636      */
37637     maskRe : null,
37638     /**
37639      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37640      */
37641     disableKeyFilter : false,
37642     /**
37643      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37644      */
37645     allowBlank : true,
37646     /**
37647      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37648      */
37649     minLength : 0,
37650     /**
37651      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37652      */
37653     maxLength : Number.MAX_VALUE,
37654     /**
37655      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37656      */
37657     minLengthText : "The minimum length for this field is {0}",
37658     /**
37659      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37660      */
37661     maxLengthText : "The maximum length for this field is {0}",
37662     /**
37663      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37664      */
37665     selectOnFocus : false,
37666     /**
37667      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37668      */
37669     blankText : "This field is required",
37670     /**
37671      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37672      * If available, this function will be called only after the basic validators all return true, and will be passed the
37673      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37674      */
37675     validator : null,
37676     /**
37677      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37678      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37679      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37680      */
37681     regex : null,
37682     /**
37683      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37684      */
37685     regexText : "",
37686     /**
37687      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37688      */
37689     emptyText : null,
37690    
37691
37692     // private
37693     initEvents : function()
37694     {
37695         if (this.emptyText) {
37696             this.el.attr('placeholder', this.emptyText);
37697         }
37698         
37699         Roo.form.TextField.superclass.initEvents.call(this);
37700         if(this.validationEvent == 'keyup'){
37701             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37702             this.el.on('keyup', this.filterValidation, this);
37703         }
37704         else if(this.validationEvent !== false){
37705             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37706         }
37707         
37708         if(this.selectOnFocus){
37709             this.on("focus", this.preFocus, this);
37710             
37711         }
37712         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37713             this.el.on("keypress", this.filterKeys, this);
37714         }
37715         if(this.grow){
37716             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37717             this.el.on("click", this.autoSize,  this);
37718         }
37719         if(this.el.is('input[type=password]') && Roo.isSafari){
37720             this.el.on('keydown', this.SafariOnKeyDown, this);
37721         }
37722     },
37723
37724     processValue : function(value){
37725         if(this.stripCharsRe){
37726             var newValue = value.replace(this.stripCharsRe, '');
37727             if(newValue !== value){
37728                 this.setRawValue(newValue);
37729                 return newValue;
37730             }
37731         }
37732         return value;
37733     },
37734
37735     filterValidation : function(e){
37736         if(!e.isNavKeyPress()){
37737             this.validationTask.delay(this.validationDelay);
37738         }
37739     },
37740
37741     // private
37742     onKeyUp : function(e){
37743         if(!e.isNavKeyPress()){
37744             this.autoSize();
37745         }
37746     },
37747
37748     /**
37749      * Resets the current field value to the originally-loaded value and clears any validation messages.
37750      *  
37751      */
37752     reset : function(){
37753         Roo.form.TextField.superclass.reset.call(this);
37754        
37755     },
37756
37757     
37758     // private
37759     preFocus : function(){
37760         
37761         if(this.selectOnFocus){
37762             this.el.dom.select();
37763         }
37764     },
37765
37766     
37767     // private
37768     filterKeys : function(e){
37769         var k = e.getKey();
37770         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37771             return;
37772         }
37773         var c = e.getCharCode(), cc = String.fromCharCode(c);
37774         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37775             return;
37776         }
37777         if(!this.maskRe.test(cc)){
37778             e.stopEvent();
37779         }
37780     },
37781
37782     setValue : function(v){
37783         
37784         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37785         
37786         this.autoSize();
37787     },
37788
37789     /**
37790      * Validates a value according to the field's validation rules and marks the field as invalid
37791      * if the validation fails
37792      * @param {Mixed} value The value to validate
37793      * @return {Boolean} True if the value is valid, else false
37794      */
37795     validateValue : function(value){
37796         if(value.length < 1)  { // if it's blank
37797              if(this.allowBlank){
37798                 this.clearInvalid();
37799                 return true;
37800              }else{
37801                 this.markInvalid(this.blankText);
37802                 return false;
37803              }
37804         }
37805         if(value.length < this.minLength){
37806             this.markInvalid(String.format(this.minLengthText, this.minLength));
37807             return false;
37808         }
37809         if(value.length > this.maxLength){
37810             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37811             return false;
37812         }
37813         if(this.vtype){
37814             var vt = Roo.form.VTypes;
37815             if(!vt[this.vtype](value, this)){
37816                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37817                 return false;
37818             }
37819         }
37820         if(typeof this.validator == "function"){
37821             var msg = this.validator(value);
37822             if(msg !== true){
37823                 this.markInvalid(msg);
37824                 return false;
37825             }
37826         }
37827         if(this.regex && !this.regex.test(value)){
37828             this.markInvalid(this.regexText);
37829             return false;
37830         }
37831         return true;
37832     },
37833
37834     /**
37835      * Selects text in this field
37836      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37837      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37838      */
37839     selectText : function(start, end){
37840         var v = this.getRawValue();
37841         if(v.length > 0){
37842             start = start === undefined ? 0 : start;
37843             end = end === undefined ? v.length : end;
37844             var d = this.el.dom;
37845             if(d.setSelectionRange){
37846                 d.setSelectionRange(start, end);
37847             }else if(d.createTextRange){
37848                 var range = d.createTextRange();
37849                 range.moveStart("character", start);
37850                 range.moveEnd("character", v.length-end);
37851                 range.select();
37852             }
37853         }
37854     },
37855
37856     /**
37857      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37858      * This only takes effect if grow = true, and fires the autosize event.
37859      */
37860     autoSize : function(){
37861         if(!this.grow || !this.rendered){
37862             return;
37863         }
37864         if(!this.metrics){
37865             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37866         }
37867         var el = this.el;
37868         var v = el.dom.value;
37869         var d = document.createElement('div');
37870         d.appendChild(document.createTextNode(v));
37871         v = d.innerHTML;
37872         d = null;
37873         v += "&#160;";
37874         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37875         this.el.setWidth(w);
37876         this.fireEvent("autosize", this, w);
37877     },
37878     
37879     // private
37880     SafariOnKeyDown : function(event)
37881     {
37882         // this is a workaround for a password hang bug on chrome/ webkit.
37883         
37884         var isSelectAll = false;
37885         
37886         if(this.el.dom.selectionEnd > 0){
37887             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37888         }
37889         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37890             event.preventDefault();
37891             this.setValue('');
37892             return;
37893         }
37894         
37895         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37896             
37897             event.preventDefault();
37898             // this is very hacky as keydown always get's upper case.
37899             
37900             var cc = String.fromCharCode(event.getCharCode());
37901             
37902             
37903             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37904             
37905         }
37906         
37907         
37908     }
37909 });/*
37910  * Based on:
37911  * Ext JS Library 1.1.1
37912  * Copyright(c) 2006-2007, Ext JS, LLC.
37913  *
37914  * Originally Released Under LGPL - original licence link has changed is not relivant.
37915  *
37916  * Fork - LGPL
37917  * <script type="text/javascript">
37918  */
37919  
37920 /**
37921  * @class Roo.form.Hidden
37922  * @extends Roo.form.TextField
37923  * Simple Hidden element used on forms 
37924  * 
37925  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37926  * 
37927  * @constructor
37928  * Creates a new Hidden form element.
37929  * @param {Object} config Configuration options
37930  */
37931
37932
37933
37934 // easy hidden field...
37935 Roo.form.Hidden = function(config){
37936     Roo.form.Hidden.superclass.constructor.call(this, config);
37937 };
37938   
37939 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37940     fieldLabel:      '',
37941     inputType:      'hidden',
37942     width:          50,
37943     allowBlank:     true,
37944     labelSeparator: '',
37945     hidden:         true,
37946     itemCls :       'x-form-item-display-none'
37947
37948
37949 });
37950
37951
37952 /*
37953  * Based on:
37954  * Ext JS Library 1.1.1
37955  * Copyright(c) 2006-2007, Ext JS, LLC.
37956  *
37957  * Originally Released Under LGPL - original licence link has changed is not relivant.
37958  *
37959  * Fork - LGPL
37960  * <script type="text/javascript">
37961  */
37962  
37963 /**
37964  * @class Roo.form.TriggerField
37965  * @extends Roo.form.TextField
37966  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37967  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37968  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37969  * for which you can provide a custom implementation.  For example:
37970  * <pre><code>
37971 var trigger = new Roo.form.TriggerField();
37972 trigger.onTriggerClick = myTriggerFn;
37973 trigger.applyTo('my-field');
37974 </code></pre>
37975  *
37976  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37977  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37978  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37979  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37980  * @constructor
37981  * Create a new TriggerField.
37982  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37983  * to the base TextField)
37984  */
37985 Roo.form.TriggerField = function(config){
37986     this.mimicing = false;
37987     Roo.form.TriggerField.superclass.constructor.call(this, config);
37988 };
37989
37990 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37991     /**
37992      * @cfg {String} triggerClass A CSS class to apply to the trigger
37993      */
37994     /**
37995      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37996      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37997      */
37998     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
37999     /**
38000      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38001      */
38002     hideTrigger:false,
38003
38004     /** @cfg {Boolean} grow @hide */
38005     /** @cfg {Number} growMin @hide */
38006     /** @cfg {Number} growMax @hide */
38007
38008     /**
38009      * @hide 
38010      * @method
38011      */
38012     autoSize: Roo.emptyFn,
38013     // private
38014     monitorTab : true,
38015     // private
38016     deferHeight : true,
38017
38018     
38019     actionMode : 'wrap',
38020     // private
38021     onResize : function(w, h){
38022         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38023         if(typeof w == 'number'){
38024             var x = w - this.trigger.getWidth();
38025             this.el.setWidth(this.adjustWidth('input', x));
38026             this.trigger.setStyle('left', x+'px');
38027         }
38028     },
38029
38030     // private
38031     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38032
38033     // private
38034     getResizeEl : function(){
38035         return this.wrap;
38036     },
38037
38038     // private
38039     getPositionEl : function(){
38040         return this.wrap;
38041     },
38042
38043     // private
38044     alignErrorIcon : function(){
38045         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38046     },
38047
38048     // private
38049     onRender : function(ct, position){
38050         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38051         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38052         this.trigger = this.wrap.createChild(this.triggerConfig ||
38053                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38054         if(this.hideTrigger){
38055             this.trigger.setDisplayed(false);
38056         }
38057         this.initTrigger();
38058         if(!this.width){
38059             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38060         }
38061     },
38062
38063     // private
38064     initTrigger : function(){
38065         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38066         this.trigger.addClassOnOver('x-form-trigger-over');
38067         this.trigger.addClassOnClick('x-form-trigger-click');
38068     },
38069
38070     // private
38071     onDestroy : function(){
38072         if(this.trigger){
38073             this.trigger.removeAllListeners();
38074             this.trigger.remove();
38075         }
38076         if(this.wrap){
38077             this.wrap.remove();
38078         }
38079         Roo.form.TriggerField.superclass.onDestroy.call(this);
38080     },
38081
38082     // private
38083     onFocus : function(){
38084         Roo.form.TriggerField.superclass.onFocus.call(this);
38085         if(!this.mimicing){
38086             this.wrap.addClass('x-trigger-wrap-focus');
38087             this.mimicing = true;
38088             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38089             if(this.monitorTab){
38090                 this.el.on("keydown", this.checkTab, this);
38091             }
38092         }
38093     },
38094
38095     // private
38096     checkTab : function(e){
38097         if(e.getKey() == e.TAB){
38098             this.triggerBlur();
38099         }
38100     },
38101
38102     // private
38103     onBlur : function(){
38104         // do nothing
38105     },
38106
38107     // private
38108     mimicBlur : function(e, t){
38109         if(!this.wrap.contains(t) && this.validateBlur()){
38110             this.triggerBlur();
38111         }
38112     },
38113
38114     // private
38115     triggerBlur : function(){
38116         this.mimicing = false;
38117         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38118         if(this.monitorTab){
38119             this.el.un("keydown", this.checkTab, this);
38120         }
38121         this.wrap.removeClass('x-trigger-wrap-focus');
38122         Roo.form.TriggerField.superclass.onBlur.call(this);
38123     },
38124
38125     // private
38126     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38127     validateBlur : function(e, t){
38128         return true;
38129     },
38130
38131     // private
38132     onDisable : function(){
38133         Roo.form.TriggerField.superclass.onDisable.call(this);
38134         if(this.wrap){
38135             this.wrap.addClass('x-item-disabled');
38136         }
38137     },
38138
38139     // private
38140     onEnable : function(){
38141         Roo.form.TriggerField.superclass.onEnable.call(this);
38142         if(this.wrap){
38143             this.wrap.removeClass('x-item-disabled');
38144         }
38145     },
38146
38147     // private
38148     onShow : function(){
38149         var ae = this.getActionEl();
38150         
38151         if(ae){
38152             ae.dom.style.display = '';
38153             ae.dom.style.visibility = 'visible';
38154         }
38155     },
38156
38157     // private
38158     
38159     onHide : function(){
38160         var ae = this.getActionEl();
38161         ae.dom.style.display = 'none';
38162     },
38163
38164     /**
38165      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38166      * by an implementing function.
38167      * @method
38168      * @param {EventObject} e
38169      */
38170     onTriggerClick : Roo.emptyFn
38171 });
38172
38173 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38174 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38175 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38176 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38177     initComponent : function(){
38178         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38179
38180         this.triggerConfig = {
38181             tag:'span', cls:'x-form-twin-triggers', cn:[
38182             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38183             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38184         ]};
38185     },
38186
38187     getTrigger : function(index){
38188         return this.triggers[index];
38189     },
38190
38191     initTrigger : function(){
38192         var ts = this.trigger.select('.x-form-trigger', true);
38193         this.wrap.setStyle('overflow', 'hidden');
38194         var triggerField = this;
38195         ts.each(function(t, all, index){
38196             t.hide = function(){
38197                 var w = triggerField.wrap.getWidth();
38198                 this.dom.style.display = 'none';
38199                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38200             };
38201             t.show = function(){
38202                 var w = triggerField.wrap.getWidth();
38203                 this.dom.style.display = '';
38204                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38205             };
38206             var triggerIndex = 'Trigger'+(index+1);
38207
38208             if(this['hide'+triggerIndex]){
38209                 t.dom.style.display = 'none';
38210             }
38211             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38212             t.addClassOnOver('x-form-trigger-over');
38213             t.addClassOnClick('x-form-trigger-click');
38214         }, this);
38215         this.triggers = ts.elements;
38216     },
38217
38218     onTrigger1Click : Roo.emptyFn,
38219     onTrigger2Click : Roo.emptyFn
38220 });/*
38221  * Based on:
38222  * Ext JS Library 1.1.1
38223  * Copyright(c) 2006-2007, Ext JS, LLC.
38224  *
38225  * Originally Released Under LGPL - original licence link has changed is not relivant.
38226  *
38227  * Fork - LGPL
38228  * <script type="text/javascript">
38229  */
38230  
38231 /**
38232  * @class Roo.form.TextArea
38233  * @extends Roo.form.TextField
38234  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38235  * support for auto-sizing.
38236  * @constructor
38237  * Creates a new TextArea
38238  * @param {Object} config Configuration options
38239  */
38240 Roo.form.TextArea = function(config){
38241     Roo.form.TextArea.superclass.constructor.call(this, config);
38242     // these are provided exchanges for backwards compat
38243     // minHeight/maxHeight were replaced by growMin/growMax to be
38244     // compatible with TextField growing config values
38245     if(this.minHeight !== undefined){
38246         this.growMin = this.minHeight;
38247     }
38248     if(this.maxHeight !== undefined){
38249         this.growMax = this.maxHeight;
38250     }
38251 };
38252
38253 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38254     /**
38255      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38256      */
38257     growMin : 60,
38258     /**
38259      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38260      */
38261     growMax: 1000,
38262     /**
38263      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38264      * in the field (equivalent to setting overflow: hidden, defaults to false)
38265      */
38266     preventScrollbars: false,
38267     /**
38268      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38269      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38270      */
38271
38272     // private
38273     onRender : function(ct, position){
38274         if(!this.el){
38275             this.defaultAutoCreate = {
38276                 tag: "textarea",
38277                 style:"width:300px;height:60px;",
38278                 autocomplete: "new-password"
38279             };
38280         }
38281         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38282         if(this.grow){
38283             this.textSizeEl = Roo.DomHelper.append(document.body, {
38284                 tag: "pre", cls: "x-form-grow-sizer"
38285             });
38286             if(this.preventScrollbars){
38287                 this.el.setStyle("overflow", "hidden");
38288             }
38289             this.el.setHeight(this.growMin);
38290         }
38291     },
38292
38293     onDestroy : function(){
38294         if(this.textSizeEl){
38295             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38296         }
38297         Roo.form.TextArea.superclass.onDestroy.call(this);
38298     },
38299
38300     // private
38301     onKeyUp : function(e){
38302         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38303             this.autoSize();
38304         }
38305     },
38306
38307     /**
38308      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38309      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38310      */
38311     autoSize : function(){
38312         if(!this.grow || !this.textSizeEl){
38313             return;
38314         }
38315         var el = this.el;
38316         var v = el.dom.value;
38317         var ts = this.textSizeEl;
38318
38319         ts.innerHTML = '';
38320         ts.appendChild(document.createTextNode(v));
38321         v = ts.innerHTML;
38322
38323         Roo.fly(ts).setWidth(this.el.getWidth());
38324         if(v.length < 1){
38325             v = "&#160;&#160;";
38326         }else{
38327             if(Roo.isIE){
38328                 v = v.replace(/\n/g, '<p>&#160;</p>');
38329             }
38330             v += "&#160;\n&#160;";
38331         }
38332         ts.innerHTML = v;
38333         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38334         if(h != this.lastHeight){
38335             this.lastHeight = h;
38336             this.el.setHeight(h);
38337             this.fireEvent("autosize", this, h);
38338         }
38339     }
38340 });/*
38341  * Based on:
38342  * Ext JS Library 1.1.1
38343  * Copyright(c) 2006-2007, Ext JS, LLC.
38344  *
38345  * Originally Released Under LGPL - original licence link has changed is not relivant.
38346  *
38347  * Fork - LGPL
38348  * <script type="text/javascript">
38349  */
38350  
38351
38352 /**
38353  * @class Roo.form.NumberField
38354  * @extends Roo.form.TextField
38355  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38356  * @constructor
38357  * Creates a new NumberField
38358  * @param {Object} config Configuration options
38359  */
38360 Roo.form.NumberField = function(config){
38361     Roo.form.NumberField.superclass.constructor.call(this, config);
38362 };
38363
38364 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38365     /**
38366      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38367      */
38368     fieldClass: "x-form-field x-form-num-field",
38369     /**
38370      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38371      */
38372     allowDecimals : true,
38373     /**
38374      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38375      */
38376     decimalSeparator : ".",
38377     /**
38378      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38379      */
38380     decimalPrecision : 2,
38381     /**
38382      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38383      */
38384     allowNegative : true,
38385     /**
38386      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38387      */
38388     minValue : Number.NEGATIVE_INFINITY,
38389     /**
38390      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38391      */
38392     maxValue : Number.MAX_VALUE,
38393     /**
38394      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38395      */
38396     minText : "The minimum value for this field is {0}",
38397     /**
38398      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38399      */
38400     maxText : "The maximum value for this field is {0}",
38401     /**
38402      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38403      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38404      */
38405     nanText : "{0} is not a valid number",
38406
38407     // private
38408     initEvents : function(){
38409         Roo.form.NumberField.superclass.initEvents.call(this);
38410         var allowed = "0123456789";
38411         if(this.allowDecimals){
38412             allowed += this.decimalSeparator;
38413         }
38414         if(this.allowNegative){
38415             allowed += "-";
38416         }
38417         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38418         var keyPress = function(e){
38419             var k = e.getKey();
38420             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38421                 return;
38422             }
38423             var c = e.getCharCode();
38424             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38425                 e.stopEvent();
38426             }
38427         };
38428         this.el.on("keypress", keyPress, this);
38429     },
38430
38431     // private
38432     validateValue : function(value){
38433         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38434             return false;
38435         }
38436         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38437              return true;
38438         }
38439         var num = this.parseValue(value);
38440         if(isNaN(num)){
38441             this.markInvalid(String.format(this.nanText, value));
38442             return false;
38443         }
38444         if(num < this.minValue){
38445             this.markInvalid(String.format(this.minText, this.minValue));
38446             return false;
38447         }
38448         if(num > this.maxValue){
38449             this.markInvalid(String.format(this.maxText, this.maxValue));
38450             return false;
38451         }
38452         return true;
38453     },
38454
38455     getValue : function(){
38456         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38457     },
38458
38459     // private
38460     parseValue : function(value){
38461         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38462         return isNaN(value) ? '' : value;
38463     },
38464
38465     // private
38466     fixPrecision : function(value){
38467         var nan = isNaN(value);
38468         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38469             return nan ? '' : value;
38470         }
38471         return parseFloat(value).toFixed(this.decimalPrecision);
38472     },
38473
38474     setValue : function(v){
38475         v = this.fixPrecision(v);
38476         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38477     },
38478
38479     // private
38480     decimalPrecisionFcn : function(v){
38481         return Math.floor(v);
38482     },
38483
38484     beforeBlur : function(){
38485         var v = this.parseValue(this.getRawValue());
38486         if(v){
38487             this.setValue(v);
38488         }
38489     }
38490 });/*
38491  * Based on:
38492  * Ext JS Library 1.1.1
38493  * Copyright(c) 2006-2007, Ext JS, LLC.
38494  *
38495  * Originally Released Under LGPL - original licence link has changed is not relivant.
38496  *
38497  * Fork - LGPL
38498  * <script type="text/javascript">
38499  */
38500  
38501 /**
38502  * @class Roo.form.DateField
38503  * @extends Roo.form.TriggerField
38504  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38505 * @constructor
38506 * Create a new DateField
38507 * @param {Object} config
38508  */
38509 Roo.form.DateField = function(config){
38510     Roo.form.DateField.superclass.constructor.call(this, config);
38511     
38512       this.addEvents({
38513          
38514         /**
38515          * @event select
38516          * Fires when a date is selected
38517              * @param {Roo.form.DateField} combo This combo box
38518              * @param {Date} date The date selected
38519              */
38520         'select' : true
38521          
38522     });
38523     
38524     
38525     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38526     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38527     this.ddMatch = null;
38528     if(this.disabledDates){
38529         var dd = this.disabledDates;
38530         var re = "(?:";
38531         for(var i = 0; i < dd.length; i++){
38532             re += dd[i];
38533             if(i != dd.length-1) re += "|";
38534         }
38535         this.ddMatch = new RegExp(re + ")");
38536     }
38537 };
38538
38539 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38540     /**
38541      * @cfg {String} format
38542      * The default date format string which can be overriden for localization support.  The format must be
38543      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38544      */
38545     format : "m/d/y",
38546     /**
38547      * @cfg {String} altFormats
38548      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38549      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38550      */
38551     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38552     /**
38553      * @cfg {Array} disabledDays
38554      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38555      */
38556     disabledDays : null,
38557     /**
38558      * @cfg {String} disabledDaysText
38559      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38560      */
38561     disabledDaysText : "Disabled",
38562     /**
38563      * @cfg {Array} disabledDates
38564      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38565      * expression so they are very powerful. Some examples:
38566      * <ul>
38567      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38568      * <li>["03/08", "09/16"] would disable those days for every year</li>
38569      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38570      * <li>["03/../2006"] would disable every day in March 2006</li>
38571      * <li>["^03"] would disable every day in every March</li>
38572      * </ul>
38573      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38574      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38575      */
38576     disabledDates : null,
38577     /**
38578      * @cfg {String} disabledDatesText
38579      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38580      */
38581     disabledDatesText : "Disabled",
38582     /**
38583      * @cfg {Date/String} minValue
38584      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38585      * valid format (defaults to null).
38586      */
38587     minValue : null,
38588     /**
38589      * @cfg {Date/String} maxValue
38590      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38591      * valid format (defaults to null).
38592      */
38593     maxValue : null,
38594     /**
38595      * @cfg {String} minText
38596      * The error text to display when the date in the cell is before minValue (defaults to
38597      * 'The date in this field must be after {minValue}').
38598      */
38599     minText : "The date in this field must be equal to or after {0}",
38600     /**
38601      * @cfg {String} maxText
38602      * The error text to display when the date in the cell is after maxValue (defaults to
38603      * 'The date in this field must be before {maxValue}').
38604      */
38605     maxText : "The date in this field must be equal to or before {0}",
38606     /**
38607      * @cfg {String} invalidText
38608      * The error text to display when the date in the field is invalid (defaults to
38609      * '{value} is not a valid date - it must be in the format {format}').
38610      */
38611     invalidText : "{0} is not a valid date - it must be in the format {1}",
38612     /**
38613      * @cfg {String} triggerClass
38614      * An additional CSS class used to style the trigger button.  The trigger will always get the
38615      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38616      * which displays a calendar icon).
38617      */
38618     triggerClass : 'x-form-date-trigger',
38619     
38620
38621     /**
38622      * @cfg {Boolean} useIso
38623      * if enabled, then the date field will use a hidden field to store the 
38624      * real value as iso formated date. default (false)
38625      */ 
38626     useIso : false,
38627     /**
38628      * @cfg {String/Object} autoCreate
38629      * A DomHelper element spec, or true for a default element spec (defaults to
38630      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38631      */ 
38632     // private
38633     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38634     
38635     // private
38636     hiddenField: false,
38637     
38638     onRender : function(ct, position)
38639     {
38640         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38641         if (this.useIso) {
38642             //this.el.dom.removeAttribute('name'); 
38643             Roo.log("Changing name?");
38644             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38645             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38646                     'before', true);
38647             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38648             // prevent input submission
38649             this.hiddenName = this.name;
38650         }
38651             
38652             
38653     },
38654     
38655     // private
38656     validateValue : function(value)
38657     {
38658         value = this.formatDate(value);
38659         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38660             Roo.log('super failed');
38661             return false;
38662         }
38663         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38664              return true;
38665         }
38666         var svalue = value;
38667         value = this.parseDate(value);
38668         if(!value){
38669             Roo.log('parse date failed' + svalue);
38670             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38671             return false;
38672         }
38673         var time = value.getTime();
38674         if(this.minValue && time < this.minValue.getTime()){
38675             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38676             return false;
38677         }
38678         if(this.maxValue && time > this.maxValue.getTime()){
38679             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38680             return false;
38681         }
38682         if(this.disabledDays){
38683             var day = value.getDay();
38684             for(var i = 0; i < this.disabledDays.length; i++) {
38685                 if(day === this.disabledDays[i]){
38686                     this.markInvalid(this.disabledDaysText);
38687                     return false;
38688                 }
38689             }
38690         }
38691         var fvalue = this.formatDate(value);
38692         if(this.ddMatch && this.ddMatch.test(fvalue)){
38693             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38694             return false;
38695         }
38696         return true;
38697     },
38698
38699     // private
38700     // Provides logic to override the default TriggerField.validateBlur which just returns true
38701     validateBlur : function(){
38702         return !this.menu || !this.menu.isVisible();
38703     },
38704     
38705     getName: function()
38706     {
38707         // returns hidden if it's set..
38708         if (!this.rendered) {return ''};
38709         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38710         
38711     },
38712
38713     /**
38714      * Returns the current date value of the date field.
38715      * @return {Date} The date value
38716      */
38717     getValue : function(){
38718         
38719         return  this.hiddenField ?
38720                 this.hiddenField.value :
38721                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38722     },
38723
38724     /**
38725      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38726      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38727      * (the default format used is "m/d/y").
38728      * <br />Usage:
38729      * <pre><code>
38730 //All of these calls set the same date value (May 4, 2006)
38731
38732 //Pass a date object:
38733 var dt = new Date('5/4/06');
38734 dateField.setValue(dt);
38735
38736 //Pass a date string (default format):
38737 dateField.setValue('5/4/06');
38738
38739 //Pass a date string (custom format):
38740 dateField.format = 'Y-m-d';
38741 dateField.setValue('2006-5-4');
38742 </code></pre>
38743      * @param {String/Date} date The date or valid date string
38744      */
38745     setValue : function(date){
38746         if (this.hiddenField) {
38747             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38748         }
38749         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38750         // make sure the value field is always stored as a date..
38751         this.value = this.parseDate(date);
38752         
38753         
38754     },
38755
38756     // private
38757     parseDate : function(value){
38758         if(!value || value instanceof Date){
38759             return value;
38760         }
38761         var v = Date.parseDate(value, this.format);
38762          if (!v && this.useIso) {
38763             v = Date.parseDate(value, 'Y-m-d');
38764         }
38765         if(!v && this.altFormats){
38766             if(!this.altFormatsArray){
38767                 this.altFormatsArray = this.altFormats.split("|");
38768             }
38769             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38770                 v = Date.parseDate(value, this.altFormatsArray[i]);
38771             }
38772         }
38773         return v;
38774     },
38775
38776     // private
38777     formatDate : function(date, fmt){
38778         return (!date || !(date instanceof Date)) ?
38779                date : date.dateFormat(fmt || this.format);
38780     },
38781
38782     // private
38783     menuListeners : {
38784         select: function(m, d){
38785             
38786             this.setValue(d);
38787             this.fireEvent('select', this, d);
38788         },
38789         show : function(){ // retain focus styling
38790             this.onFocus();
38791         },
38792         hide : function(){
38793             this.focus.defer(10, this);
38794             var ml = this.menuListeners;
38795             this.menu.un("select", ml.select,  this);
38796             this.menu.un("show", ml.show,  this);
38797             this.menu.un("hide", ml.hide,  this);
38798         }
38799     },
38800
38801     // private
38802     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38803     onTriggerClick : function(){
38804         if(this.disabled){
38805             return;
38806         }
38807         if(this.menu == null){
38808             this.menu = new Roo.menu.DateMenu();
38809         }
38810         Roo.apply(this.menu.picker,  {
38811             showClear: this.allowBlank,
38812             minDate : this.minValue,
38813             maxDate : this.maxValue,
38814             disabledDatesRE : this.ddMatch,
38815             disabledDatesText : this.disabledDatesText,
38816             disabledDays : this.disabledDays,
38817             disabledDaysText : this.disabledDaysText,
38818             format : this.useIso ? 'Y-m-d' : this.format,
38819             minText : String.format(this.minText, this.formatDate(this.minValue)),
38820             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38821         });
38822         this.menu.on(Roo.apply({}, this.menuListeners, {
38823             scope:this
38824         }));
38825         this.menu.picker.setValue(this.getValue() || new Date());
38826         this.menu.show(this.el, "tl-bl?");
38827     },
38828
38829     beforeBlur : function(){
38830         var v = this.parseDate(this.getRawValue());
38831         if(v){
38832             this.setValue(v);
38833         }
38834     },
38835
38836     /*@
38837      * overide
38838      * 
38839      */
38840     isDirty : function() {
38841         if(this.disabled) {
38842             return false;
38843         }
38844         
38845         if(typeof(this.startValue) === 'undefined'){
38846             return false;
38847         }
38848         
38849         return String(this.getValue()) !== String(this.startValue);
38850         
38851     }
38852 });/*
38853  * Based on:
38854  * Ext JS Library 1.1.1
38855  * Copyright(c) 2006-2007, Ext JS, LLC.
38856  *
38857  * Originally Released Under LGPL - original licence link has changed is not relivant.
38858  *
38859  * Fork - LGPL
38860  * <script type="text/javascript">
38861  */
38862  
38863 /**
38864  * @class Roo.form.MonthField
38865  * @extends Roo.form.TriggerField
38866  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38867 * @constructor
38868 * Create a new MonthField
38869 * @param {Object} config
38870  */
38871 Roo.form.MonthField = function(config){
38872     
38873     Roo.form.MonthField.superclass.constructor.call(this, config);
38874     
38875       this.addEvents({
38876          
38877         /**
38878          * @event select
38879          * Fires when a date is selected
38880              * @param {Roo.form.MonthFieeld} combo This combo box
38881              * @param {Date} date The date selected
38882              */
38883         'select' : true
38884          
38885     });
38886     
38887     
38888     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38889     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38890     this.ddMatch = null;
38891     if(this.disabledDates){
38892         var dd = this.disabledDates;
38893         var re = "(?:";
38894         for(var i = 0; i < dd.length; i++){
38895             re += dd[i];
38896             if(i != dd.length-1) re += "|";
38897         }
38898         this.ddMatch = new RegExp(re + ")");
38899     }
38900 };
38901
38902 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38903     /**
38904      * @cfg {String} format
38905      * The default date format string which can be overriden for localization support.  The format must be
38906      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38907      */
38908     format : "M Y",
38909     /**
38910      * @cfg {String} altFormats
38911      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38912      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38913      */
38914     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38915     /**
38916      * @cfg {Array} disabledDays
38917      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38918      */
38919     disabledDays : [0,1,2,3,4,5,6],
38920     /**
38921      * @cfg {String} disabledDaysText
38922      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38923      */
38924     disabledDaysText : "Disabled",
38925     /**
38926      * @cfg {Array} disabledDates
38927      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38928      * expression so they are very powerful. Some examples:
38929      * <ul>
38930      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38931      * <li>["03/08", "09/16"] would disable those days for every year</li>
38932      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38933      * <li>["03/../2006"] would disable every day in March 2006</li>
38934      * <li>["^03"] would disable every day in every March</li>
38935      * </ul>
38936      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38937      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38938      */
38939     disabledDates : null,
38940     /**
38941      * @cfg {String} disabledDatesText
38942      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38943      */
38944     disabledDatesText : "Disabled",
38945     /**
38946      * @cfg {Date/String} minValue
38947      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38948      * valid format (defaults to null).
38949      */
38950     minValue : null,
38951     /**
38952      * @cfg {Date/String} maxValue
38953      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38954      * valid format (defaults to null).
38955      */
38956     maxValue : null,
38957     /**
38958      * @cfg {String} minText
38959      * The error text to display when the date in the cell is before minValue (defaults to
38960      * 'The date in this field must be after {minValue}').
38961      */
38962     minText : "The date in this field must be equal to or after {0}",
38963     /**
38964      * @cfg {String} maxTextf
38965      * The error text to display when the date in the cell is after maxValue (defaults to
38966      * 'The date in this field must be before {maxValue}').
38967      */
38968     maxText : "The date in this field must be equal to or before {0}",
38969     /**
38970      * @cfg {String} invalidText
38971      * The error text to display when the date in the field is invalid (defaults to
38972      * '{value} is not a valid date - it must be in the format {format}').
38973      */
38974     invalidText : "{0} is not a valid date - it must be in the format {1}",
38975     /**
38976      * @cfg {String} triggerClass
38977      * An additional CSS class used to style the trigger button.  The trigger will always get the
38978      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38979      * which displays a calendar icon).
38980      */
38981     triggerClass : 'x-form-date-trigger',
38982     
38983
38984     /**
38985      * @cfg {Boolean} useIso
38986      * if enabled, then the date field will use a hidden field to store the 
38987      * real value as iso formated date. default (true)
38988      */ 
38989     useIso : true,
38990     /**
38991      * @cfg {String/Object} autoCreate
38992      * A DomHelper element spec, or true for a default element spec (defaults to
38993      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38994      */ 
38995     // private
38996     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
38997     
38998     // private
38999     hiddenField: false,
39000     
39001     hideMonthPicker : false,
39002     
39003     onRender : function(ct, position)
39004     {
39005         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39006         if (this.useIso) {
39007             this.el.dom.removeAttribute('name'); 
39008             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39009                     'before', true);
39010             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39011             // prevent input submission
39012             this.hiddenName = this.name;
39013         }
39014             
39015             
39016     },
39017     
39018     // private
39019     validateValue : function(value)
39020     {
39021         value = this.formatDate(value);
39022         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39023             return false;
39024         }
39025         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39026              return true;
39027         }
39028         var svalue = value;
39029         value = this.parseDate(value);
39030         if(!value){
39031             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39032             return false;
39033         }
39034         var time = value.getTime();
39035         if(this.minValue && time < this.minValue.getTime()){
39036             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39037             return false;
39038         }
39039         if(this.maxValue && time > this.maxValue.getTime()){
39040             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39041             return false;
39042         }
39043         /*if(this.disabledDays){
39044             var day = value.getDay();
39045             for(var i = 0; i < this.disabledDays.length; i++) {
39046                 if(day === this.disabledDays[i]){
39047                     this.markInvalid(this.disabledDaysText);
39048                     return false;
39049                 }
39050             }
39051         }
39052         */
39053         var fvalue = this.formatDate(value);
39054         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39055             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39056             return false;
39057         }
39058         */
39059         return true;
39060     },
39061
39062     // private
39063     // Provides logic to override the default TriggerField.validateBlur which just returns true
39064     validateBlur : function(){
39065         return !this.menu || !this.menu.isVisible();
39066     },
39067
39068     /**
39069      * Returns the current date value of the date field.
39070      * @return {Date} The date value
39071      */
39072     getValue : function(){
39073         
39074         
39075         
39076         return  this.hiddenField ?
39077                 this.hiddenField.value :
39078                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39079     },
39080
39081     /**
39082      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39083      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39084      * (the default format used is "m/d/y").
39085      * <br />Usage:
39086      * <pre><code>
39087 //All of these calls set the same date value (May 4, 2006)
39088
39089 //Pass a date object:
39090 var dt = new Date('5/4/06');
39091 monthField.setValue(dt);
39092
39093 //Pass a date string (default format):
39094 monthField.setValue('5/4/06');
39095
39096 //Pass a date string (custom format):
39097 monthField.format = 'Y-m-d';
39098 monthField.setValue('2006-5-4');
39099 </code></pre>
39100      * @param {String/Date} date The date or valid date string
39101      */
39102     setValue : function(date){
39103         Roo.log('month setValue' + date);
39104         // can only be first of month..
39105         
39106         var val = this.parseDate(date);
39107         
39108         if (this.hiddenField) {
39109             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39110         }
39111         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39112         this.value = this.parseDate(date);
39113     },
39114
39115     // private
39116     parseDate : function(value){
39117         if(!value || value instanceof Date){
39118             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39119             return value;
39120         }
39121         var v = Date.parseDate(value, this.format);
39122         if (!v && this.useIso) {
39123             v = Date.parseDate(value, 'Y-m-d');
39124         }
39125         if (v) {
39126             // 
39127             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39128         }
39129         
39130         
39131         if(!v && this.altFormats){
39132             if(!this.altFormatsArray){
39133                 this.altFormatsArray = this.altFormats.split("|");
39134             }
39135             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39136                 v = Date.parseDate(value, this.altFormatsArray[i]);
39137             }
39138         }
39139         return v;
39140     },
39141
39142     // private
39143     formatDate : function(date, fmt){
39144         return (!date || !(date instanceof Date)) ?
39145                date : date.dateFormat(fmt || this.format);
39146     },
39147
39148     // private
39149     menuListeners : {
39150         select: function(m, d){
39151             this.setValue(d);
39152             this.fireEvent('select', this, d);
39153         },
39154         show : function(){ // retain focus styling
39155             this.onFocus();
39156         },
39157         hide : function(){
39158             this.focus.defer(10, this);
39159             var ml = this.menuListeners;
39160             this.menu.un("select", ml.select,  this);
39161             this.menu.un("show", ml.show,  this);
39162             this.menu.un("hide", ml.hide,  this);
39163         }
39164     },
39165     // private
39166     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39167     onTriggerClick : function(){
39168         if(this.disabled){
39169             return;
39170         }
39171         if(this.menu == null){
39172             this.menu = new Roo.menu.DateMenu();
39173            
39174         }
39175         
39176         Roo.apply(this.menu.picker,  {
39177             
39178             showClear: this.allowBlank,
39179             minDate : this.minValue,
39180             maxDate : this.maxValue,
39181             disabledDatesRE : this.ddMatch,
39182             disabledDatesText : this.disabledDatesText,
39183             
39184             format : this.useIso ? 'Y-m-d' : this.format,
39185             minText : String.format(this.minText, this.formatDate(this.minValue)),
39186             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39187             
39188         });
39189          this.menu.on(Roo.apply({}, this.menuListeners, {
39190             scope:this
39191         }));
39192        
39193         
39194         var m = this.menu;
39195         var p = m.picker;
39196         
39197         // hide month picker get's called when we called by 'before hide';
39198         
39199         var ignorehide = true;
39200         p.hideMonthPicker  = function(disableAnim){
39201             if (ignorehide) {
39202                 return;
39203             }
39204              if(this.monthPicker){
39205                 Roo.log("hideMonthPicker called");
39206                 if(disableAnim === true){
39207                     this.monthPicker.hide();
39208                 }else{
39209                     this.monthPicker.slideOut('t', {duration:.2});
39210                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39211                     p.fireEvent("select", this, this.value);
39212                     m.hide();
39213                 }
39214             }
39215         }
39216         
39217         Roo.log('picker set value');
39218         Roo.log(this.getValue());
39219         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39220         m.show(this.el, 'tl-bl?');
39221         ignorehide  = false;
39222         // this will trigger hideMonthPicker..
39223         
39224         
39225         // hidden the day picker
39226         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39227         
39228         
39229         
39230       
39231         
39232         p.showMonthPicker.defer(100, p);
39233     
39234         
39235        
39236     },
39237
39238     beforeBlur : function(){
39239         var v = this.parseDate(this.getRawValue());
39240         if(v){
39241             this.setValue(v);
39242         }
39243     }
39244
39245     /** @cfg {Boolean} grow @hide */
39246     /** @cfg {Number} growMin @hide */
39247     /** @cfg {Number} growMax @hide */
39248     /**
39249      * @hide
39250      * @method autoSize
39251      */
39252 });/*
39253  * Based on:
39254  * Ext JS Library 1.1.1
39255  * Copyright(c) 2006-2007, Ext JS, LLC.
39256  *
39257  * Originally Released Under LGPL - original licence link has changed is not relivant.
39258  *
39259  * Fork - LGPL
39260  * <script type="text/javascript">
39261  */
39262  
39263
39264 /**
39265  * @class Roo.form.ComboBox
39266  * @extends Roo.form.TriggerField
39267  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39268  * @constructor
39269  * Create a new ComboBox.
39270  * @param {Object} config Configuration options
39271  */
39272 Roo.form.ComboBox = function(config){
39273     Roo.form.ComboBox.superclass.constructor.call(this, config);
39274     this.addEvents({
39275         /**
39276          * @event expand
39277          * Fires when the dropdown list is expanded
39278              * @param {Roo.form.ComboBox} combo This combo box
39279              */
39280         'expand' : true,
39281         /**
39282          * @event collapse
39283          * Fires when the dropdown list is collapsed
39284              * @param {Roo.form.ComboBox} combo This combo box
39285              */
39286         'collapse' : true,
39287         /**
39288          * @event beforeselect
39289          * Fires before a list item is selected. Return false to cancel the selection.
39290              * @param {Roo.form.ComboBox} combo This combo box
39291              * @param {Roo.data.Record} record The data record returned from the underlying store
39292              * @param {Number} index The index of the selected item in the dropdown list
39293              */
39294         'beforeselect' : true,
39295         /**
39296          * @event select
39297          * Fires when a list item is selected
39298              * @param {Roo.form.ComboBox} combo This combo box
39299              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39300              * @param {Number} index The index of the selected item in the dropdown list
39301              */
39302         'select' : true,
39303         /**
39304          * @event beforequery
39305          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39306          * The event object passed has these properties:
39307              * @param {Roo.form.ComboBox} combo This combo box
39308              * @param {String} query The query
39309              * @param {Boolean} forceAll true to force "all" query
39310              * @param {Boolean} cancel true to cancel the query
39311              * @param {Object} e The query event object
39312              */
39313         'beforequery': true,
39314          /**
39315          * @event add
39316          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39317              * @param {Roo.form.ComboBox} combo This combo box
39318              */
39319         'add' : true,
39320         /**
39321          * @event edit
39322          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39323              * @param {Roo.form.ComboBox} combo This combo box
39324              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39325              */
39326         'edit' : true
39327         
39328         
39329     });
39330     if(this.transform){
39331         this.allowDomMove = false;
39332         var s = Roo.getDom(this.transform);
39333         if(!this.hiddenName){
39334             this.hiddenName = s.name;
39335         }
39336         if(!this.store){
39337             this.mode = 'local';
39338             var d = [], opts = s.options;
39339             for(var i = 0, len = opts.length;i < len; i++){
39340                 var o = opts[i];
39341                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39342                 if(o.selected) {
39343                     this.value = value;
39344                 }
39345                 d.push([value, o.text]);
39346             }
39347             this.store = new Roo.data.SimpleStore({
39348                 'id': 0,
39349                 fields: ['value', 'text'],
39350                 data : d
39351             });
39352             this.valueField = 'value';
39353             this.displayField = 'text';
39354         }
39355         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39356         if(!this.lazyRender){
39357             this.target = true;
39358             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39359             s.parentNode.removeChild(s); // remove it
39360             this.render(this.el.parentNode);
39361         }else{
39362             s.parentNode.removeChild(s); // remove it
39363         }
39364
39365     }
39366     if (this.store) {
39367         this.store = Roo.factory(this.store, Roo.data);
39368     }
39369     
39370     this.selectedIndex = -1;
39371     if(this.mode == 'local'){
39372         if(config.queryDelay === undefined){
39373             this.queryDelay = 10;
39374         }
39375         if(config.minChars === undefined){
39376             this.minChars = 0;
39377         }
39378     }
39379 };
39380
39381 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39382     /**
39383      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39384      */
39385     /**
39386      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39387      * rendering into an Roo.Editor, defaults to false)
39388      */
39389     /**
39390      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39391      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39392      */
39393     /**
39394      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39395      */
39396     /**
39397      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39398      * the dropdown list (defaults to undefined, with no header element)
39399      */
39400
39401      /**
39402      * @cfg {String/Roo.Template} tpl The template to use to render the output
39403      */
39404      
39405     // private
39406     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39407     /**
39408      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39409      */
39410     listWidth: undefined,
39411     /**
39412      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39413      * mode = 'remote' or 'text' if mode = 'local')
39414      */
39415     displayField: undefined,
39416     /**
39417      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39418      * mode = 'remote' or 'value' if mode = 'local'). 
39419      * Note: use of a valueField requires the user make a selection
39420      * in order for a value to be mapped.
39421      */
39422     valueField: undefined,
39423     
39424     
39425     /**
39426      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39427      * field's data value (defaults to the underlying DOM element's name)
39428      */
39429     hiddenName: undefined,
39430     /**
39431      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39432      */
39433     listClass: '',
39434     /**
39435      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39436      */
39437     selectedClass: 'x-combo-selected',
39438     /**
39439      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39440      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39441      * which displays a downward arrow icon).
39442      */
39443     triggerClass : 'x-form-arrow-trigger',
39444     /**
39445      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39446      */
39447     shadow:'sides',
39448     /**
39449      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39450      * anchor positions (defaults to 'tl-bl')
39451      */
39452     listAlign: 'tl-bl?',
39453     /**
39454      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39455      */
39456     maxHeight: 300,
39457     /**
39458      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39459      * query specified by the allQuery config option (defaults to 'query')
39460      */
39461     triggerAction: 'query',
39462     /**
39463      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39464      * (defaults to 4, does not apply if editable = false)
39465      */
39466     minChars : 4,
39467     /**
39468      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39469      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39470      */
39471     typeAhead: false,
39472     /**
39473      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39474      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39475      */
39476     queryDelay: 500,
39477     /**
39478      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39479      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39480      */
39481     pageSize: 0,
39482     /**
39483      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39484      * when editable = true (defaults to false)
39485      */
39486     selectOnFocus:false,
39487     /**
39488      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39489      */
39490     queryParam: 'query',
39491     /**
39492      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39493      * when mode = 'remote' (defaults to 'Loading...')
39494      */
39495     loadingText: 'Loading...',
39496     /**
39497      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39498      */
39499     resizable: false,
39500     /**
39501      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39502      */
39503     handleHeight : 8,
39504     /**
39505      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39506      * traditional select (defaults to true)
39507      */
39508     editable: true,
39509     /**
39510      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39511      */
39512     allQuery: '',
39513     /**
39514      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39515      */
39516     mode: 'remote',
39517     /**
39518      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39519      * listWidth has a higher value)
39520      */
39521     minListWidth : 70,
39522     /**
39523      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39524      * allow the user to set arbitrary text into the field (defaults to false)
39525      */
39526     forceSelection:false,
39527     /**
39528      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39529      * if typeAhead = true (defaults to 250)
39530      */
39531     typeAheadDelay : 250,
39532     /**
39533      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39534      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39535      */
39536     valueNotFoundText : undefined,
39537     /**
39538      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39539      */
39540     blockFocus : false,
39541     
39542     /**
39543      * @cfg {Boolean} disableClear Disable showing of clear button.
39544      */
39545     disableClear : false,
39546     /**
39547      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39548      */
39549     alwaysQuery : false,
39550     
39551     //private
39552     addicon : false,
39553     editicon: false,
39554     
39555     // element that contains real text value.. (when hidden is used..)
39556      
39557     // private
39558     onRender : function(ct, position){
39559         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39560         if(this.hiddenName){
39561             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39562                     'before', true);
39563             this.hiddenField.value =
39564                 this.hiddenValue !== undefined ? this.hiddenValue :
39565                 this.value !== undefined ? this.value : '';
39566
39567             // prevent input submission
39568             this.el.dom.removeAttribute('name');
39569              
39570              
39571         }
39572         if(Roo.isGecko){
39573             this.el.dom.setAttribute('autocomplete', 'off');
39574         }
39575
39576         var cls = 'x-combo-list';
39577
39578         this.list = new Roo.Layer({
39579             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39580         });
39581
39582         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39583         this.list.setWidth(lw);
39584         this.list.swallowEvent('mousewheel');
39585         this.assetHeight = 0;
39586
39587         if(this.title){
39588             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39589             this.assetHeight += this.header.getHeight();
39590         }
39591
39592         this.innerList = this.list.createChild({cls:cls+'-inner'});
39593         this.innerList.on('mouseover', this.onViewOver, this);
39594         this.innerList.on('mousemove', this.onViewMove, this);
39595         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39596         
39597         if(this.allowBlank && !this.pageSize && !this.disableClear){
39598             this.footer = this.list.createChild({cls:cls+'-ft'});
39599             this.pageTb = new Roo.Toolbar(this.footer);
39600            
39601         }
39602         if(this.pageSize){
39603             this.footer = this.list.createChild({cls:cls+'-ft'});
39604             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39605                     {pageSize: this.pageSize});
39606             
39607         }
39608         
39609         if (this.pageTb && this.allowBlank && !this.disableClear) {
39610             var _this = this;
39611             this.pageTb.add(new Roo.Toolbar.Fill(), {
39612                 cls: 'x-btn-icon x-btn-clear',
39613                 text: '&#160;',
39614                 handler: function()
39615                 {
39616                     _this.collapse();
39617                     _this.clearValue();
39618                     _this.onSelect(false, -1);
39619                 }
39620             });
39621         }
39622         if (this.footer) {
39623             this.assetHeight += this.footer.getHeight();
39624         }
39625         
39626
39627         if(!this.tpl){
39628             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39629         }
39630
39631         this.view = new Roo.View(this.innerList, this.tpl, {
39632             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39633         });
39634
39635         this.view.on('click', this.onViewClick, this);
39636
39637         this.store.on('beforeload', this.onBeforeLoad, this);
39638         this.store.on('load', this.onLoad, this);
39639         this.store.on('loadexception', this.onLoadException, this);
39640
39641         if(this.resizable){
39642             this.resizer = new Roo.Resizable(this.list,  {
39643                pinned:true, handles:'se'
39644             });
39645             this.resizer.on('resize', function(r, w, h){
39646                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39647                 this.listWidth = w;
39648                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39649                 this.restrictHeight();
39650             }, this);
39651             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39652         }
39653         if(!this.editable){
39654             this.editable = true;
39655             this.setEditable(false);
39656         }  
39657         
39658         
39659         if (typeof(this.events.add.listeners) != 'undefined') {
39660             
39661             this.addicon = this.wrap.createChild(
39662                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39663        
39664             this.addicon.on('click', function(e) {
39665                 this.fireEvent('add', this);
39666             }, this);
39667         }
39668         if (typeof(this.events.edit.listeners) != 'undefined') {
39669             
39670             this.editicon = this.wrap.createChild(
39671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39672             if (this.addicon) {
39673                 this.editicon.setStyle('margin-left', '40px');
39674             }
39675             this.editicon.on('click', function(e) {
39676                 
39677                 // we fire even  if inothing is selected..
39678                 this.fireEvent('edit', this, this.lastData );
39679                 
39680             }, this);
39681         }
39682         
39683         
39684         
39685     },
39686
39687     // private
39688     initEvents : function(){
39689         Roo.form.ComboBox.superclass.initEvents.call(this);
39690
39691         this.keyNav = new Roo.KeyNav(this.el, {
39692             "up" : function(e){
39693                 this.inKeyMode = true;
39694                 this.selectPrev();
39695             },
39696
39697             "down" : function(e){
39698                 if(!this.isExpanded()){
39699                     this.onTriggerClick();
39700                 }else{
39701                     this.inKeyMode = true;
39702                     this.selectNext();
39703                 }
39704             },
39705
39706             "enter" : function(e){
39707                 this.onViewClick();
39708                 //return true;
39709             },
39710
39711             "esc" : function(e){
39712                 this.collapse();
39713             },
39714
39715             "tab" : function(e){
39716                 this.onViewClick(false);
39717                 this.fireEvent("specialkey", this, e);
39718                 return true;
39719             },
39720
39721             scope : this,
39722
39723             doRelay : function(foo, bar, hname){
39724                 if(hname == 'down' || this.scope.isExpanded()){
39725                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39726                 }
39727                 return true;
39728             },
39729
39730             forceKeyDown: true
39731         });
39732         this.queryDelay = Math.max(this.queryDelay || 10,
39733                 this.mode == 'local' ? 10 : 250);
39734         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39735         if(this.typeAhead){
39736             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39737         }
39738         if(this.editable !== false){
39739             this.el.on("keyup", this.onKeyUp, this);
39740         }
39741         if(this.forceSelection){
39742             this.on('blur', this.doForce, this);
39743         }
39744     },
39745
39746     onDestroy : function(){
39747         if(this.view){
39748             this.view.setStore(null);
39749             this.view.el.removeAllListeners();
39750             this.view.el.remove();
39751             this.view.purgeListeners();
39752         }
39753         if(this.list){
39754             this.list.destroy();
39755         }
39756         if(this.store){
39757             this.store.un('beforeload', this.onBeforeLoad, this);
39758             this.store.un('load', this.onLoad, this);
39759             this.store.un('loadexception', this.onLoadException, this);
39760         }
39761         Roo.form.ComboBox.superclass.onDestroy.call(this);
39762     },
39763
39764     // private
39765     fireKey : function(e){
39766         if(e.isNavKeyPress() && !this.list.isVisible()){
39767             this.fireEvent("specialkey", this, e);
39768         }
39769     },
39770
39771     // private
39772     onResize: function(w, h){
39773         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39774         
39775         if(typeof w != 'number'){
39776             // we do not handle it!?!?
39777             return;
39778         }
39779         var tw = this.trigger.getWidth();
39780         tw += this.addicon ? this.addicon.getWidth() : 0;
39781         tw += this.editicon ? this.editicon.getWidth() : 0;
39782         var x = w - tw;
39783         this.el.setWidth( this.adjustWidth('input', x));
39784             
39785         this.trigger.setStyle('left', x+'px');
39786         
39787         if(this.list && this.listWidth === undefined){
39788             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39789             this.list.setWidth(lw);
39790             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39791         }
39792         
39793     
39794         
39795     },
39796
39797     /**
39798      * Allow or prevent the user from directly editing the field text.  If false is passed,
39799      * the user will only be able to select from the items defined in the dropdown list.  This method
39800      * is the runtime equivalent of setting the 'editable' config option at config time.
39801      * @param {Boolean} value True to allow the user to directly edit the field text
39802      */
39803     setEditable : function(value){
39804         if(value == this.editable){
39805             return;
39806         }
39807         this.editable = value;
39808         if(!value){
39809             this.el.dom.setAttribute('readOnly', true);
39810             this.el.on('mousedown', this.onTriggerClick,  this);
39811             this.el.addClass('x-combo-noedit');
39812         }else{
39813             this.el.dom.setAttribute('readOnly', false);
39814             this.el.un('mousedown', this.onTriggerClick,  this);
39815             this.el.removeClass('x-combo-noedit');
39816         }
39817     },
39818
39819     // private
39820     onBeforeLoad : function(){
39821         if(!this.hasFocus){
39822             return;
39823         }
39824         this.innerList.update(this.loadingText ?
39825                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39826         this.restrictHeight();
39827         this.selectedIndex = -1;
39828     },
39829
39830     // private
39831     onLoad : function(){
39832         if(!this.hasFocus){
39833             return;
39834         }
39835         if(this.store.getCount() > 0){
39836             this.expand();
39837             this.restrictHeight();
39838             if(this.lastQuery == this.allQuery){
39839                 if(this.editable){
39840                     this.el.dom.select();
39841                 }
39842                 if(!this.selectByValue(this.value, true)){
39843                     this.select(0, true);
39844                 }
39845             }else{
39846                 this.selectNext();
39847                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39848                     this.taTask.delay(this.typeAheadDelay);
39849                 }
39850             }
39851         }else{
39852             this.onEmptyResults();
39853         }
39854         //this.el.focus();
39855     },
39856     // private
39857     onLoadException : function()
39858     {
39859         this.collapse();
39860         Roo.log(this.store.reader.jsonData);
39861         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39862             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39863         }
39864         
39865         
39866     },
39867     // private
39868     onTypeAhead : function(){
39869         if(this.store.getCount() > 0){
39870             var r = this.store.getAt(0);
39871             var newValue = r.data[this.displayField];
39872             var len = newValue.length;
39873             var selStart = this.getRawValue().length;
39874             if(selStart != len){
39875                 this.setRawValue(newValue);
39876                 this.selectText(selStart, newValue.length);
39877             }
39878         }
39879     },
39880
39881     // private
39882     onSelect : function(record, index){
39883         if(this.fireEvent('beforeselect', this, record, index) !== false){
39884             this.setFromData(index > -1 ? record.data : false);
39885             this.collapse();
39886             this.fireEvent('select', this, record, index);
39887         }
39888     },
39889
39890     /**
39891      * Returns the currently selected field value or empty string if no value is set.
39892      * @return {String} value The selected value
39893      */
39894     getValue : function(){
39895         if(this.valueField){
39896             return typeof this.value != 'undefined' ? this.value : '';
39897         }
39898         return Roo.form.ComboBox.superclass.getValue.call(this);
39899     },
39900
39901     /**
39902      * Clears any text/value currently set in the field
39903      */
39904     clearValue : function(){
39905         if(this.hiddenField){
39906             this.hiddenField.value = '';
39907         }
39908         this.value = '';
39909         this.setRawValue('');
39910         this.lastSelectionText = '';
39911         
39912     },
39913
39914     /**
39915      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39916      * will be displayed in the field.  If the value does not match the data value of an existing item,
39917      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39918      * Otherwise the field will be blank (although the value will still be set).
39919      * @param {String} value The value to match
39920      */
39921     setValue : function(v){
39922         var text = v;
39923         if(this.valueField){
39924             var r = this.findRecord(this.valueField, v);
39925             if(r){
39926                 text = r.data[this.displayField];
39927             }else if(this.valueNotFoundText !== undefined){
39928                 text = this.valueNotFoundText;
39929             }
39930         }
39931         this.lastSelectionText = text;
39932         if(this.hiddenField){
39933             this.hiddenField.value = v;
39934         }
39935         Roo.form.ComboBox.superclass.setValue.call(this, text);
39936         this.value = v;
39937     },
39938     /**
39939      * @property {Object} the last set data for the element
39940      */
39941     
39942     lastData : false,
39943     /**
39944      * Sets the value of the field based on a object which is related to the record format for the store.
39945      * @param {Object} value the value to set as. or false on reset?
39946      */
39947     setFromData : function(o){
39948         var dv = ''; // display value
39949         var vv = ''; // value value..
39950         this.lastData = o;
39951         if (this.displayField) {
39952             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39953         } else {
39954             // this is an error condition!!!
39955             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39956         }
39957         
39958         if(this.valueField){
39959             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39960         }
39961         if(this.hiddenField){
39962             this.hiddenField.value = vv;
39963             
39964             this.lastSelectionText = dv;
39965             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39966             this.value = vv;
39967             return;
39968         }
39969         // no hidden field.. - we store the value in 'value', but still display
39970         // display field!!!!
39971         this.lastSelectionText = dv;
39972         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39973         this.value = vv;
39974         
39975         
39976     },
39977     // private
39978     reset : function(){
39979         // overridden so that last data is reset..
39980         this.setValue(this.resetValue);
39981         this.clearInvalid();
39982         this.lastData = false;
39983         if (this.view) {
39984             this.view.clearSelections();
39985         }
39986     },
39987     // private
39988     findRecord : function(prop, value){
39989         var record;
39990         if(this.store.getCount() > 0){
39991             this.store.each(function(r){
39992                 if(r.data[prop] == value){
39993                     record = r;
39994                     return false;
39995                 }
39996                 return true;
39997             });
39998         }
39999         return record;
40000     },
40001     
40002     getName: function()
40003     {
40004         // returns hidden if it's set..
40005         if (!this.rendered) {return ''};
40006         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40007         
40008     },
40009     // private
40010     onViewMove : function(e, t){
40011         this.inKeyMode = false;
40012     },
40013
40014     // private
40015     onViewOver : function(e, t){
40016         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40017             return;
40018         }
40019         var item = this.view.findItemFromChild(t);
40020         if(item){
40021             var index = this.view.indexOf(item);
40022             this.select(index, false);
40023         }
40024     },
40025
40026     // private
40027     onViewClick : function(doFocus)
40028     {
40029         var index = this.view.getSelectedIndexes()[0];
40030         var r = this.store.getAt(index);
40031         if(r){
40032             this.onSelect(r, index);
40033         }
40034         if(doFocus !== false && !this.blockFocus){
40035             this.el.focus();
40036         }
40037     },
40038
40039     // private
40040     restrictHeight : function(){
40041         this.innerList.dom.style.height = '';
40042         var inner = this.innerList.dom;
40043         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40044         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40045         this.list.beginUpdate();
40046         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40047         this.list.alignTo(this.el, this.listAlign);
40048         this.list.endUpdate();
40049     },
40050
40051     // private
40052     onEmptyResults : function(){
40053         this.collapse();
40054     },
40055
40056     /**
40057      * Returns true if the dropdown list is expanded, else false.
40058      */
40059     isExpanded : function(){
40060         return this.list.isVisible();
40061     },
40062
40063     /**
40064      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40065      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40066      * @param {String} value The data value of the item to select
40067      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40068      * selected item if it is not currently in view (defaults to true)
40069      * @return {Boolean} True if the value matched an item in the list, else false
40070      */
40071     selectByValue : function(v, scrollIntoView){
40072         if(v !== undefined && v !== null){
40073             var r = this.findRecord(this.valueField || this.displayField, v);
40074             if(r){
40075                 this.select(this.store.indexOf(r), scrollIntoView);
40076                 return true;
40077             }
40078         }
40079         return false;
40080     },
40081
40082     /**
40083      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40084      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40085      * @param {Number} index The zero-based index of the list item to select
40086      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40087      * selected item if it is not currently in view (defaults to true)
40088      */
40089     select : function(index, scrollIntoView){
40090         this.selectedIndex = index;
40091         this.view.select(index);
40092         if(scrollIntoView !== false){
40093             var el = this.view.getNode(index);
40094             if(el){
40095                 this.innerList.scrollChildIntoView(el, false);
40096             }
40097         }
40098     },
40099
40100     // private
40101     selectNext : function(){
40102         var ct = this.store.getCount();
40103         if(ct > 0){
40104             if(this.selectedIndex == -1){
40105                 this.select(0);
40106             }else if(this.selectedIndex < ct-1){
40107                 this.select(this.selectedIndex+1);
40108             }
40109         }
40110     },
40111
40112     // private
40113     selectPrev : function(){
40114         var ct = this.store.getCount();
40115         if(ct > 0){
40116             if(this.selectedIndex == -1){
40117                 this.select(0);
40118             }else if(this.selectedIndex != 0){
40119                 this.select(this.selectedIndex-1);
40120             }
40121         }
40122     },
40123
40124     // private
40125     onKeyUp : function(e){
40126         if(this.editable !== false && !e.isSpecialKey()){
40127             this.lastKey = e.getKey();
40128             this.dqTask.delay(this.queryDelay);
40129         }
40130     },
40131
40132     // private
40133     validateBlur : function(){
40134         return !this.list || !this.list.isVisible();   
40135     },
40136
40137     // private
40138     initQuery : function(){
40139         this.doQuery(this.getRawValue());
40140     },
40141
40142     // private
40143     doForce : function(){
40144         if(this.el.dom.value.length > 0){
40145             this.el.dom.value =
40146                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40147              
40148         }
40149     },
40150
40151     /**
40152      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40153      * query allowing the query action to be canceled if needed.
40154      * @param {String} query The SQL query to execute
40155      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40156      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40157      * saved in the current store (defaults to false)
40158      */
40159     doQuery : function(q, forceAll){
40160         if(q === undefined || q === null){
40161             q = '';
40162         }
40163         var qe = {
40164             query: q,
40165             forceAll: forceAll,
40166             combo: this,
40167             cancel:false
40168         };
40169         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40170             return false;
40171         }
40172         q = qe.query;
40173         forceAll = qe.forceAll;
40174         if(forceAll === true || (q.length >= this.minChars)){
40175             if(this.lastQuery != q || this.alwaysQuery){
40176                 this.lastQuery = q;
40177                 if(this.mode == 'local'){
40178                     this.selectedIndex = -1;
40179                     if(forceAll){
40180                         this.store.clearFilter();
40181                     }else{
40182                         this.store.filter(this.displayField, q);
40183                     }
40184                     this.onLoad();
40185                 }else{
40186                     this.store.baseParams[this.queryParam] = q;
40187                     this.store.load({
40188                         params: this.getParams(q)
40189                     });
40190                     this.expand();
40191                 }
40192             }else{
40193                 this.selectedIndex = -1;
40194                 this.onLoad();   
40195             }
40196         }
40197     },
40198
40199     // private
40200     getParams : function(q){
40201         var p = {};
40202         //p[this.queryParam] = q;
40203         if(this.pageSize){
40204             p.start = 0;
40205             p.limit = this.pageSize;
40206         }
40207         return p;
40208     },
40209
40210     /**
40211      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40212      */
40213     collapse : function(){
40214         if(!this.isExpanded()){
40215             return;
40216         }
40217         this.list.hide();
40218         Roo.get(document).un('mousedown', this.collapseIf, this);
40219         Roo.get(document).un('mousewheel', this.collapseIf, this);
40220         if (!this.editable) {
40221             Roo.get(document).un('keydown', this.listKeyPress, this);
40222         }
40223         this.fireEvent('collapse', this);
40224     },
40225
40226     // private
40227     collapseIf : function(e){
40228         if(!e.within(this.wrap) && !e.within(this.list)){
40229             this.collapse();
40230         }
40231     },
40232
40233     /**
40234      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40235      */
40236     expand : function(){
40237         if(this.isExpanded() || !this.hasFocus){
40238             return;
40239         }
40240         this.list.alignTo(this.el, this.listAlign);
40241         this.list.show();
40242         Roo.get(document).on('mousedown', this.collapseIf, this);
40243         Roo.get(document).on('mousewheel', this.collapseIf, this);
40244         if (!this.editable) {
40245             Roo.get(document).on('keydown', this.listKeyPress, this);
40246         }
40247         
40248         this.fireEvent('expand', this);
40249     },
40250
40251     // private
40252     // Implements the default empty TriggerField.onTriggerClick function
40253     onTriggerClick : function(){
40254         if(this.disabled){
40255             return;
40256         }
40257         if(this.isExpanded()){
40258             this.collapse();
40259             if (!this.blockFocus) {
40260                 this.el.focus();
40261             }
40262             
40263         }else {
40264             this.hasFocus = true;
40265             if(this.triggerAction == 'all') {
40266                 this.doQuery(this.allQuery, true);
40267             } else {
40268                 this.doQuery(this.getRawValue());
40269             }
40270             if (!this.blockFocus) {
40271                 this.el.focus();
40272             }
40273         }
40274     },
40275     listKeyPress : function(e)
40276     {
40277         //Roo.log('listkeypress');
40278         // scroll to first matching element based on key pres..
40279         if (e.isSpecialKey()) {
40280             return false;
40281         }
40282         var k = String.fromCharCode(e.getKey()).toUpperCase();
40283         //Roo.log(k);
40284         var match  = false;
40285         var csel = this.view.getSelectedNodes();
40286         var cselitem = false;
40287         if (csel.length) {
40288             var ix = this.view.indexOf(csel[0]);
40289             cselitem  = this.store.getAt(ix);
40290             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40291                 cselitem = false;
40292             }
40293             
40294         }
40295         
40296         this.store.each(function(v) { 
40297             if (cselitem) {
40298                 // start at existing selection.
40299                 if (cselitem.id == v.id) {
40300                     cselitem = false;
40301                 }
40302                 return;
40303             }
40304                 
40305             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40306                 match = this.store.indexOf(v);
40307                 return false;
40308             }
40309         }, this);
40310         
40311         if (match === false) {
40312             return true; // no more action?
40313         }
40314         // scroll to?
40315         this.view.select(match);
40316         var sn = Roo.get(this.view.getSelectedNodes()[0])
40317         sn.scrollIntoView(sn.dom.parentNode, false);
40318     }
40319
40320     /** 
40321     * @cfg {Boolean} grow 
40322     * @hide 
40323     */
40324     /** 
40325     * @cfg {Number} growMin 
40326     * @hide 
40327     */
40328     /** 
40329     * @cfg {Number} growMax 
40330     * @hide 
40331     */
40332     /**
40333      * @hide
40334      * @method autoSize
40335      */
40336 });/*
40337  * Copyright(c) 2010-2012, Roo J Solutions Limited
40338  *
40339  * Licence LGPL
40340  *
40341  */
40342
40343 /**
40344  * @class Roo.form.ComboBoxArray
40345  * @extends Roo.form.TextField
40346  * A facebook style adder... for lists of email / people / countries  etc...
40347  * pick multiple items from a combo box, and shows each one.
40348  *
40349  *  Fred [x]  Brian [x]  [Pick another |v]
40350  *
40351  *
40352  *  For this to work: it needs various extra information
40353  *    - normal combo problay has
40354  *      name, hiddenName
40355  *    + displayField, valueField
40356  *
40357  *    For our purpose...
40358  *
40359  *
40360  *   If we change from 'extends' to wrapping...
40361  *   
40362  *  
40363  *
40364  
40365  
40366  * @constructor
40367  * Create a new ComboBoxArray.
40368  * @param {Object} config Configuration options
40369  */
40370  
40371
40372 Roo.form.ComboBoxArray = function(config)
40373 {
40374     this.addEvents({
40375         /**
40376          * @event beforeremove
40377          * Fires before remove the value from the list
40378              * @param {Roo.form.ComboBoxArray} _self This combo box array
40379              * @param {Roo.form.ComboBoxArray.Item} item removed item
40380              */
40381         'beforeremove' : true,
40382         /**
40383          * @event remove
40384          * Fires when remove the value from the list
40385              * @param {Roo.form.ComboBoxArray} _self This combo box array
40386              * @param {Roo.form.ComboBoxArray.Item} item removed item
40387              */
40388         'remove' : true
40389         
40390         
40391     });
40392     
40393     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40394     
40395     this.items = new Roo.util.MixedCollection(false);
40396     
40397     // construct the child combo...
40398     
40399     
40400     
40401     
40402    
40403     
40404 }
40405
40406  
40407 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40408
40409     /**
40410      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40411      */
40412     
40413     lastData : false,
40414     
40415     // behavies liek a hiddne field
40416     inputType:      'hidden',
40417     /**
40418      * @cfg {Number} width The width of the box that displays the selected element
40419      */ 
40420     width:          300,
40421
40422     
40423     
40424     /**
40425      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40426      */
40427     name : false,
40428     /**
40429      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40430      */
40431     hiddenName : false,
40432     
40433     
40434     // private the array of items that are displayed..
40435     items  : false,
40436     // private - the hidden field el.
40437     hiddenEl : false,
40438     // private - the filed el..
40439     el : false,
40440     
40441     //validateValue : function() { return true; }, // all values are ok!
40442     //onAddClick: function() { },
40443     
40444     onRender : function(ct, position) 
40445     {
40446         
40447         // create the standard hidden element
40448         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40449         
40450         
40451         // give fake names to child combo;
40452         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40453         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40454         
40455         this.combo = Roo.factory(this.combo, Roo.form);
40456         this.combo.onRender(ct, position);
40457         if (typeof(this.combo.width) != 'undefined') {
40458             this.combo.onResize(this.combo.width,0);
40459         }
40460         
40461         this.combo.initEvents();
40462         
40463         // assigned so form know we need to do this..
40464         this.store          = this.combo.store;
40465         this.valueField     = this.combo.valueField;
40466         this.displayField   = this.combo.displayField ;
40467         
40468         
40469         this.combo.wrap.addClass('x-cbarray-grp');
40470         
40471         var cbwrap = this.combo.wrap.createChild(
40472             {tag: 'div', cls: 'x-cbarray-cb'},
40473             this.combo.el.dom
40474         );
40475         
40476              
40477         this.hiddenEl = this.combo.wrap.createChild({
40478             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40479         });
40480         this.el = this.combo.wrap.createChild({
40481             tag: 'input',  type:'hidden' , name: this.name, value : ''
40482         });
40483          //   this.el.dom.removeAttribute("name");
40484         
40485         
40486         this.outerWrap = this.combo.wrap;
40487         this.wrap = cbwrap;
40488         
40489         this.outerWrap.setWidth(this.width);
40490         this.outerWrap.dom.removeChild(this.el.dom);
40491         
40492         this.wrap.dom.appendChild(this.el.dom);
40493         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40494         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40495         
40496         this.combo.trigger.setStyle('position','relative');
40497         this.combo.trigger.setStyle('left', '0px');
40498         this.combo.trigger.setStyle('top', '2px');
40499         
40500         this.combo.el.setStyle('vertical-align', 'text-bottom');
40501         
40502         //this.trigger.setStyle('vertical-align', 'top');
40503         
40504         // this should use the code from combo really... on('add' ....)
40505         if (this.adder) {
40506             
40507         
40508             this.adder = this.outerWrap.createChild(
40509                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40510             var _t = this;
40511             this.adder.on('click', function(e) {
40512                 _t.fireEvent('adderclick', this, e);
40513             }, _t);
40514         }
40515         //var _t = this;
40516         //this.adder.on('click', this.onAddClick, _t);
40517         
40518         
40519         this.combo.on('select', function(cb, rec, ix) {
40520             this.addItem(rec.data);
40521             
40522             cb.setValue('');
40523             cb.el.dom.value = '';
40524             //cb.lastData = rec.data;
40525             // add to list
40526             
40527         }, this);
40528         
40529         
40530     },
40531     
40532     
40533     getName: function()
40534     {
40535         // returns hidden if it's set..
40536         if (!this.rendered) {return ''};
40537         return  this.hiddenName ? this.hiddenName : this.name;
40538         
40539     },
40540     
40541     
40542     onResize: function(w, h){
40543         
40544         return;
40545         // not sure if this is needed..
40546         //this.combo.onResize(w,h);
40547         
40548         if(typeof w != 'number'){
40549             // we do not handle it!?!?
40550             return;
40551         }
40552         var tw = this.combo.trigger.getWidth();
40553         tw += this.addicon ? this.addicon.getWidth() : 0;
40554         tw += this.editicon ? this.editicon.getWidth() : 0;
40555         var x = w - tw;
40556         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40557             
40558         this.combo.trigger.setStyle('left', '0px');
40559         
40560         if(this.list && this.listWidth === undefined){
40561             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40562             this.list.setWidth(lw);
40563             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40564         }
40565         
40566     
40567         
40568     },
40569     
40570     addItem: function(rec)
40571     {
40572         var valueField = this.combo.valueField;
40573         var displayField = this.combo.displayField;
40574         if (this.items.indexOfKey(rec[valueField]) > -1) {
40575             //console.log("GOT " + rec.data.id);
40576             return;
40577         }
40578         
40579         var x = new Roo.form.ComboBoxArray.Item({
40580             //id : rec[this.idField],
40581             data : rec,
40582             displayField : displayField ,
40583             tipField : displayField ,
40584             cb : this
40585         });
40586         // use the 
40587         this.items.add(rec[valueField],x);
40588         // add it before the element..
40589         this.updateHiddenEl();
40590         x.render(this.outerWrap, this.wrap.dom);
40591         // add the image handler..
40592     },
40593     
40594     updateHiddenEl : function()
40595     {
40596         this.validate();
40597         if (!this.hiddenEl) {
40598             return;
40599         }
40600         var ar = [];
40601         var idField = this.combo.valueField;
40602         
40603         this.items.each(function(f) {
40604             ar.push(f.data[idField]);
40605            
40606         });
40607         this.hiddenEl.dom.value = ar.join(',');
40608         this.validate();
40609     },
40610     
40611     reset : function()
40612     {
40613         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40614         this.items.each(function(f) {
40615            f.remove(); 
40616         });
40617         this.el.dom.value = '';
40618         if (this.hiddenEl) {
40619             this.hiddenEl.dom.value = '';
40620         }
40621         
40622     },
40623     getValue: function()
40624     {
40625         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40626     },
40627     setValue: function(v) // not a valid action - must use addItems..
40628     {
40629          
40630         this.reset();
40631         
40632         
40633         
40634         if (this.store.isLocal && (typeof(v) == 'string')) {
40635             // then we can use the store to find the values..
40636             // comma seperated at present.. this needs to allow JSON based encoding..
40637             this.hiddenEl.value  = v;
40638             var v_ar = [];
40639             Roo.each(v.split(','), function(k) {
40640                 Roo.log("CHECK " + this.valueField + ',' + k);
40641                 var li = this.store.query(this.valueField, k);
40642                 if (!li.length) {
40643                     return;
40644                 }
40645                 var add = {};
40646                 add[this.valueField] = k;
40647                 add[this.displayField] = li.item(0).data[this.displayField];
40648                 
40649                 this.addItem(add);
40650             }, this) 
40651              
40652         }
40653         if (typeof(v) == 'object' ) {
40654             // then let's assume it's an array of objects..
40655             Roo.each(v, function(l) {
40656                 this.addItem(l);
40657             }, this);
40658              
40659         }
40660         
40661         
40662     },
40663     setFromData: function(v)
40664     {
40665         // this recieves an object, if setValues is called.
40666         this.reset();
40667         this.el.dom.value = v[this.displayField];
40668         this.hiddenEl.dom.value = v[this.valueField];
40669         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40670             return;
40671         }
40672         var kv = v[this.valueField];
40673         var dv = v[this.displayField];
40674         kv = typeof(kv) != 'string' ? '' : kv;
40675         dv = typeof(dv) != 'string' ? '' : dv;
40676         
40677         
40678         var keys = kv.split(',');
40679         var display = dv.split(',');
40680         for (var i = 0 ; i < keys.length; i++) {
40681             
40682             add = {};
40683             add[this.valueField] = keys[i];
40684             add[this.displayField] = display[i];
40685             this.addItem(add);
40686         }
40687       
40688         
40689     },
40690     
40691     /**
40692      * Validates the combox array value
40693      * @return {Boolean} True if the value is valid, else false
40694      */
40695     validate : function(){
40696         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40697             this.clearInvalid();
40698             return true;
40699         }
40700         return false;
40701     },
40702     
40703     validateValue : function(value){
40704         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40705         
40706     },
40707     
40708     /*@
40709      * overide
40710      * 
40711      */
40712     isDirty : function() {
40713         if(this.disabled) {
40714             return false;
40715         }
40716         
40717         try {
40718             var d = Roo.decode(String(this.originalValue));
40719         } catch (e) {
40720             return String(this.getValue()) !== String(this.originalValue);
40721         }
40722         
40723         var originalValue = [];
40724         
40725         for (var i = 0; i < d.length; i++){
40726             originalValue.push(d[i][this.valueField]);
40727         }
40728         
40729         return String(this.getValue()) !== String(originalValue.join(','));
40730         
40731     }
40732     
40733 });
40734
40735
40736
40737 /**
40738  * @class Roo.form.ComboBoxArray.Item
40739  * @extends Roo.BoxComponent
40740  * A selected item in the list
40741  *  Fred [x]  Brian [x]  [Pick another |v]
40742  * 
40743  * @constructor
40744  * Create a new item.
40745  * @param {Object} config Configuration options
40746  */
40747  
40748 Roo.form.ComboBoxArray.Item = function(config) {
40749     config.id = Roo.id();
40750     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40751 }
40752
40753 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40754     data : {},
40755     cb: false,
40756     displayField : false,
40757     tipField : false,
40758     
40759     
40760     defaultAutoCreate : {
40761         tag: 'div',
40762         cls: 'x-cbarray-item',
40763         cn : [ 
40764             { tag: 'div' },
40765             {
40766                 tag: 'img',
40767                 width:16,
40768                 height : 16,
40769                 src : Roo.BLANK_IMAGE_URL ,
40770                 align: 'center'
40771             }
40772         ]
40773         
40774     },
40775     
40776  
40777     onRender : function(ct, position)
40778     {
40779         Roo.form.Field.superclass.onRender.call(this, ct, position);
40780         
40781         if(!this.el){
40782             var cfg = this.getAutoCreate();
40783             this.el = ct.createChild(cfg, position);
40784         }
40785         
40786         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40787         
40788         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40789             this.cb.renderer(this.data) :
40790             String.format('{0}',this.data[this.displayField]);
40791         
40792             
40793         this.el.child('div').dom.setAttribute('qtip',
40794                         String.format('{0}',this.data[this.tipField])
40795         );
40796         
40797         this.el.child('img').on('click', this.remove, this);
40798         
40799     },
40800    
40801     remove : function()
40802     {
40803         if(this.cb.disabled){
40804             return;
40805         }
40806         
40807         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40808             this.cb.items.remove(this);
40809             this.el.child('img').un('click', this.remove, this);
40810             this.el.remove();
40811             this.cb.updateHiddenEl();
40812
40813             this.cb.fireEvent('remove', this.cb, this);
40814         }
40815         
40816     }
40817 });/*
40818  * Based on:
40819  * Ext JS Library 1.1.1
40820  * Copyright(c) 2006-2007, Ext JS, LLC.
40821  *
40822  * Originally Released Under LGPL - original licence link has changed is not relivant.
40823  *
40824  * Fork - LGPL
40825  * <script type="text/javascript">
40826  */
40827 /**
40828  * @class Roo.form.Checkbox
40829  * @extends Roo.form.Field
40830  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40831  * @constructor
40832  * Creates a new Checkbox
40833  * @param {Object} config Configuration options
40834  */
40835 Roo.form.Checkbox = function(config){
40836     Roo.form.Checkbox.superclass.constructor.call(this, config);
40837     this.addEvents({
40838         /**
40839          * @event check
40840          * Fires when the checkbox is checked or unchecked.
40841              * @param {Roo.form.Checkbox} this This checkbox
40842              * @param {Boolean} checked The new checked value
40843              */
40844         check : true
40845     });
40846 };
40847
40848 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40849     /**
40850      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40851      */
40852     focusClass : undefined,
40853     /**
40854      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40855      */
40856     fieldClass: "x-form-field",
40857     /**
40858      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40859      */
40860     checked: false,
40861     /**
40862      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40863      * {tag: "input", type: "checkbox", autocomplete: "off"})
40864      */
40865     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40866     /**
40867      * @cfg {String} boxLabel The text that appears beside the checkbox
40868      */
40869     boxLabel : "",
40870     /**
40871      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40872      */  
40873     inputValue : '1',
40874     /**
40875      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40876      */
40877      valueOff: '0', // value when not checked..
40878
40879     actionMode : 'viewEl', 
40880     //
40881     // private
40882     itemCls : 'x-menu-check-item x-form-item',
40883     groupClass : 'x-menu-group-item',
40884     inputType : 'hidden',
40885     
40886     
40887     inSetChecked: false, // check that we are not calling self...
40888     
40889     inputElement: false, // real input element?
40890     basedOn: false, // ????
40891     
40892     isFormField: true, // not sure where this is needed!!!!
40893
40894     onResize : function(){
40895         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40896         if(!this.boxLabel){
40897             this.el.alignTo(this.wrap, 'c-c');
40898         }
40899     },
40900
40901     initEvents : function(){
40902         Roo.form.Checkbox.superclass.initEvents.call(this);
40903         this.el.on("click", this.onClick,  this);
40904         this.el.on("change", this.onClick,  this);
40905     },
40906
40907
40908     getResizeEl : function(){
40909         return this.wrap;
40910     },
40911
40912     getPositionEl : function(){
40913         return this.wrap;
40914     },
40915
40916     // private
40917     onRender : function(ct, position){
40918         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40919         /*
40920         if(this.inputValue !== undefined){
40921             this.el.dom.value = this.inputValue;
40922         }
40923         */
40924         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40925         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40926         var viewEl = this.wrap.createChild({ 
40927             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40928         this.viewEl = viewEl;   
40929         this.wrap.on('click', this.onClick,  this); 
40930         
40931         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40932         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40933         
40934         
40935         
40936         if(this.boxLabel){
40937             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40938         //    viewEl.on('click', this.onClick,  this); 
40939         }
40940         //if(this.checked){
40941             this.setChecked(this.checked);
40942         //}else{
40943             //this.checked = this.el.dom;
40944         //}
40945
40946     },
40947
40948     // private
40949     initValue : Roo.emptyFn,
40950
40951     /**
40952      * Returns the checked state of the checkbox.
40953      * @return {Boolean} True if checked, else false
40954      */
40955     getValue : function(){
40956         if(this.el){
40957             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40958         }
40959         return this.valueOff;
40960         
40961     },
40962
40963         // private
40964     onClick : function(){ 
40965         if (this.disabled) {
40966             return;
40967         }
40968         this.setChecked(!this.checked);
40969
40970         //if(this.el.dom.checked != this.checked){
40971         //    this.setValue(this.el.dom.checked);
40972        // }
40973     },
40974
40975     /**
40976      * Sets the checked state of the checkbox.
40977      * On is always based on a string comparison between inputValue and the param.
40978      * @param {Boolean/String} value - the value to set 
40979      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40980      */
40981     setValue : function(v,suppressEvent){
40982         
40983         
40984         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40985         //if(this.el && this.el.dom){
40986         //    this.el.dom.checked = this.checked;
40987         //    this.el.dom.defaultChecked = this.checked;
40988         //}
40989         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40990         //this.fireEvent("check", this, this.checked);
40991     },
40992     // private..
40993     setChecked : function(state,suppressEvent)
40994     {
40995         if (this.inSetChecked) {
40996             this.checked = state;
40997             return;
40998         }
40999         
41000     
41001         if(this.wrap){
41002             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41003         }
41004         this.checked = state;
41005         if(suppressEvent !== true){
41006             this.fireEvent('check', this, state);
41007         }
41008         this.inSetChecked = true;
41009         this.el.dom.value = state ? this.inputValue : this.valueOff;
41010         this.inSetChecked = false;
41011         
41012     },
41013     // handle setting of hidden value by some other method!!?!?
41014     setFromHidden: function()
41015     {
41016         if(!this.el){
41017             return;
41018         }
41019         //console.log("SET FROM HIDDEN");
41020         //alert('setFrom hidden');
41021         this.setValue(this.el.dom.value);
41022     },
41023     
41024     onDestroy : function()
41025     {
41026         if(this.viewEl){
41027             Roo.get(this.viewEl).remove();
41028         }
41029          
41030         Roo.form.Checkbox.superclass.onDestroy.call(this);
41031     }
41032
41033 });/*
41034  * Based on:
41035  * Ext JS Library 1.1.1
41036  * Copyright(c) 2006-2007, Ext JS, LLC.
41037  *
41038  * Originally Released Under LGPL - original licence link has changed is not relivant.
41039  *
41040  * Fork - LGPL
41041  * <script type="text/javascript">
41042  */
41043  
41044 /**
41045  * @class Roo.form.Radio
41046  * @extends Roo.form.Checkbox
41047  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41048  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41049  * @constructor
41050  * Creates a new Radio
41051  * @param {Object} config Configuration options
41052  */
41053 Roo.form.Radio = function(){
41054     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41055 };
41056 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41057     inputType: 'radio',
41058
41059     /**
41060      * If this radio is part of a group, it will return the selected value
41061      * @return {String}
41062      */
41063     getGroupValue : function(){
41064         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41065     },
41066     
41067     
41068     onRender : function(ct, position){
41069         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41070         
41071         if(this.inputValue !== undefined){
41072             this.el.dom.value = this.inputValue;
41073         }
41074          
41075         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41076         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41077         //var viewEl = this.wrap.createChild({ 
41078         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41079         //this.viewEl = viewEl;   
41080         //this.wrap.on('click', this.onClick,  this); 
41081         
41082         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41083         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41084         
41085         
41086         
41087         if(this.boxLabel){
41088             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41089         //    viewEl.on('click', this.onClick,  this); 
41090         }
41091          if(this.checked){
41092             this.el.dom.checked =   'checked' ;
41093         }
41094          
41095     } 
41096     
41097     
41098 });//<script type="text/javascript">
41099
41100 /*
41101  * Based  Ext JS Library 1.1.1
41102  * Copyright(c) 2006-2007, Ext JS, LLC.
41103  * LGPL
41104  *
41105  */
41106  
41107 /**
41108  * @class Roo.HtmlEditorCore
41109  * @extends Roo.Component
41110  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41111  *
41112  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41113  */
41114
41115 Roo.HtmlEditorCore = function(config){
41116     
41117     
41118     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41119     
41120     
41121     this.addEvents({
41122         /**
41123          * @event initialize
41124          * Fires when the editor is fully initialized (including the iframe)
41125          * @param {Roo.HtmlEditorCore} this
41126          */
41127         initialize: true,
41128         /**
41129          * @event activate
41130          * Fires when the editor is first receives the focus. Any insertion must wait
41131          * until after this event.
41132          * @param {Roo.HtmlEditorCore} this
41133          */
41134         activate: true,
41135          /**
41136          * @event beforesync
41137          * Fires before the textarea is updated with content from the editor iframe. Return false
41138          * to cancel the sync.
41139          * @param {Roo.HtmlEditorCore} this
41140          * @param {String} html
41141          */
41142         beforesync: true,
41143          /**
41144          * @event beforepush
41145          * Fires before the iframe editor is updated with content from the textarea. Return false
41146          * to cancel the push.
41147          * @param {Roo.HtmlEditorCore} this
41148          * @param {String} html
41149          */
41150         beforepush: true,
41151          /**
41152          * @event sync
41153          * Fires when the textarea is updated with content from the editor iframe.
41154          * @param {Roo.HtmlEditorCore} this
41155          * @param {String} html
41156          */
41157         sync: true,
41158          /**
41159          * @event push
41160          * Fires when the iframe editor is updated with content from the textarea.
41161          * @param {Roo.HtmlEditorCore} this
41162          * @param {String} html
41163          */
41164         push: true,
41165         
41166         /**
41167          * @event editorevent
41168          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41169          * @param {Roo.HtmlEditorCore} this
41170          */
41171         editorevent: true
41172         
41173     });
41174     
41175     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41176     
41177     // defaults : white / black...
41178     this.applyBlacklists();
41179     
41180     
41181     
41182 };
41183
41184
41185 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41186
41187
41188      /**
41189      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41190      */
41191     
41192     owner : false,
41193     
41194      /**
41195      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41196      *                        Roo.resizable.
41197      */
41198     resizable : false,
41199      /**
41200      * @cfg {Number} height (in pixels)
41201      */   
41202     height: 300,
41203    /**
41204      * @cfg {Number} width (in pixels)
41205      */   
41206     width: 500,
41207     
41208     /**
41209      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41210      * 
41211      */
41212     stylesheets: false,
41213     
41214     // id of frame..
41215     frameId: false,
41216     
41217     // private properties
41218     validationEvent : false,
41219     deferHeight: true,
41220     initialized : false,
41221     activated : false,
41222     sourceEditMode : false,
41223     onFocus : Roo.emptyFn,
41224     iframePad:3,
41225     hideMode:'offsets',
41226     
41227     clearUp: true,
41228     
41229     // blacklist + whitelisted elements..
41230     black: false,
41231     white: false,
41232      
41233     
41234
41235     /**
41236      * Protected method that will not generally be called directly. It
41237      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41238      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41239      */
41240     getDocMarkup : function(){
41241         // body styles..
41242         var st = '';
41243         
41244         // inherit styels from page...?? 
41245         if (this.stylesheets === false) {
41246             
41247             Roo.get(document.head).select('style').each(function(node) {
41248                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41249             });
41250             
41251             Roo.get(document.head).select('link').each(function(node) { 
41252                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41253             });
41254             
41255         } else if (!this.stylesheets.length) {
41256                 // simple..
41257                 st = '<style type="text/css">' +
41258                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41259                    '</style>';
41260         } else { 
41261             
41262         }
41263         
41264         st +=  '<style type="text/css">' +
41265             'IMG { cursor: pointer } ' +
41266         '</style>';
41267
41268         
41269         return '<html><head>' + st  +
41270             //<style type="text/css">' +
41271             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41272             //'</style>' +
41273             ' </head><body class="roo-htmleditor-body"></body></html>';
41274     },
41275
41276     // private
41277     onRender : function(ct, position)
41278     {
41279         var _t = this;
41280         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41281         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41282         
41283         
41284         this.el.dom.style.border = '0 none';
41285         this.el.dom.setAttribute('tabIndex', -1);
41286         this.el.addClass('x-hidden hide');
41287         
41288         
41289         
41290         if(Roo.isIE){ // fix IE 1px bogus margin
41291             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41292         }
41293        
41294         
41295         this.frameId = Roo.id();
41296         
41297          
41298         
41299         var iframe = this.owner.wrap.createChild({
41300             tag: 'iframe',
41301             cls: 'form-control', // bootstrap..
41302             id: this.frameId,
41303             name: this.frameId,
41304             frameBorder : 'no',
41305             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41306         }, this.el
41307         );
41308         
41309         
41310         this.iframe = iframe.dom;
41311
41312          this.assignDocWin();
41313         
41314         this.doc.designMode = 'on';
41315        
41316         this.doc.open();
41317         this.doc.write(this.getDocMarkup());
41318         this.doc.close();
41319
41320         
41321         var task = { // must defer to wait for browser to be ready
41322             run : function(){
41323                 //console.log("run task?" + this.doc.readyState);
41324                 this.assignDocWin();
41325                 if(this.doc.body || this.doc.readyState == 'complete'){
41326                     try {
41327                         this.doc.designMode="on";
41328                     } catch (e) {
41329                         return;
41330                     }
41331                     Roo.TaskMgr.stop(task);
41332                     this.initEditor.defer(10, this);
41333                 }
41334             },
41335             interval : 10,
41336             duration: 10000,
41337             scope: this
41338         };
41339         Roo.TaskMgr.start(task);
41340
41341     },
41342
41343     // private
41344     onResize : function(w, h)
41345     {
41346          Roo.log('resize: ' +w + ',' + h );
41347         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41348         if(!this.iframe){
41349             return;
41350         }
41351         if(typeof w == 'number'){
41352             
41353             this.iframe.style.width = w + 'px';
41354         }
41355         if(typeof h == 'number'){
41356             
41357             this.iframe.style.height = h + 'px';
41358             if(this.doc){
41359                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41360             }
41361         }
41362         
41363     },
41364
41365     /**
41366      * Toggles the editor between standard and source edit mode.
41367      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41368      */
41369     toggleSourceEdit : function(sourceEditMode){
41370         
41371         this.sourceEditMode = sourceEditMode === true;
41372         
41373         if(this.sourceEditMode){
41374  
41375             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41376             
41377         }else{
41378             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41379             //this.iframe.className = '';
41380             this.deferFocus();
41381         }
41382         //this.setSize(this.owner.wrap.getSize());
41383         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41384     },
41385
41386     
41387   
41388
41389     /**
41390      * Protected method that will not generally be called directly. If you need/want
41391      * custom HTML cleanup, this is the method you should override.
41392      * @param {String} html The HTML to be cleaned
41393      * return {String} The cleaned HTML
41394      */
41395     cleanHtml : function(html){
41396         html = String(html);
41397         if(html.length > 5){
41398             if(Roo.isSafari){ // strip safari nonsense
41399                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41400             }
41401         }
41402         if(html == '&nbsp;'){
41403             html = '';
41404         }
41405         return html;
41406     },
41407
41408     /**
41409      * HTML Editor -> Textarea
41410      * Protected method that will not generally be called directly. Syncs the contents
41411      * of the editor iframe with the textarea.
41412      */
41413     syncValue : function(){
41414         if(this.initialized){
41415             var bd = (this.doc.body || this.doc.documentElement);
41416             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41417             var html = bd.innerHTML;
41418             if(Roo.isSafari){
41419                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41420                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41421                 if(m && m[1]){
41422                     html = '<div style="'+m[0]+'">' + html + '</div>';
41423                 }
41424             }
41425             html = this.cleanHtml(html);
41426             // fix up the special chars.. normaly like back quotes in word...
41427             // however we do not want to do this with chinese..
41428             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41429                 var cc = b.charCodeAt();
41430                 if (
41431                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41432                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41433                     (cc >= 0xf900 && cc < 0xfb00 )
41434                 ) {
41435                         return b;
41436                 }
41437                 return "&#"+cc+";" 
41438             });
41439             if(this.owner.fireEvent('beforesync', this, html) !== false){
41440                 this.el.dom.value = html;
41441                 this.owner.fireEvent('sync', this, html);
41442             }
41443         }
41444     },
41445
41446     /**
41447      * Protected method that will not generally be called directly. Pushes the value of the textarea
41448      * into the iframe editor.
41449      */
41450     pushValue : function(){
41451         if(this.initialized){
41452             var v = this.el.dom.value.trim();
41453             
41454 //            if(v.length < 1){
41455 //                v = '&#160;';
41456 //            }
41457             
41458             if(this.owner.fireEvent('beforepush', this, v) !== false){
41459                 var d = (this.doc.body || this.doc.documentElement);
41460                 d.innerHTML = v;
41461                 this.cleanUpPaste();
41462                 this.el.dom.value = d.innerHTML;
41463                 this.owner.fireEvent('push', this, v);
41464             }
41465         }
41466     },
41467
41468     // private
41469     deferFocus : function(){
41470         this.focus.defer(10, this);
41471     },
41472
41473     // doc'ed in Field
41474     focus : function(){
41475         if(this.win && !this.sourceEditMode){
41476             this.win.focus();
41477         }else{
41478             this.el.focus();
41479         }
41480     },
41481     
41482     assignDocWin: function()
41483     {
41484         var iframe = this.iframe;
41485         
41486          if(Roo.isIE){
41487             this.doc = iframe.contentWindow.document;
41488             this.win = iframe.contentWindow;
41489         } else {
41490 //            if (!Roo.get(this.frameId)) {
41491 //                return;
41492 //            }
41493 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41494 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41495             
41496             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41497                 return;
41498             }
41499             
41500             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41501             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41502         }
41503     },
41504     
41505     // private
41506     initEditor : function(){
41507         //console.log("INIT EDITOR");
41508         this.assignDocWin();
41509         
41510         
41511         
41512         this.doc.designMode="on";
41513         this.doc.open();
41514         this.doc.write(this.getDocMarkup());
41515         this.doc.close();
41516         
41517         var dbody = (this.doc.body || this.doc.documentElement);
41518         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41519         // this copies styles from the containing element into thsi one..
41520         // not sure why we need all of this..
41521         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41522         
41523         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41524         //ss['background-attachment'] = 'fixed'; // w3c
41525         dbody.bgProperties = 'fixed'; // ie
41526         //Roo.DomHelper.applyStyles(dbody, ss);
41527         Roo.EventManager.on(this.doc, {
41528             //'mousedown': this.onEditorEvent,
41529             'mouseup': this.onEditorEvent,
41530             'dblclick': this.onEditorEvent,
41531             'click': this.onEditorEvent,
41532             'keyup': this.onEditorEvent,
41533             buffer:100,
41534             scope: this
41535         });
41536         if(Roo.isGecko){
41537             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41538         }
41539         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41540             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41541         }
41542         this.initialized = true;
41543
41544         this.owner.fireEvent('initialize', this);
41545         this.pushValue();
41546     },
41547
41548     // private
41549     onDestroy : function(){
41550         
41551         
41552         
41553         if(this.rendered){
41554             
41555             //for (var i =0; i < this.toolbars.length;i++) {
41556             //    // fixme - ask toolbars for heights?
41557             //    this.toolbars[i].onDestroy();
41558            // }
41559             
41560             //this.wrap.dom.innerHTML = '';
41561             //this.wrap.remove();
41562         }
41563     },
41564
41565     // private
41566     onFirstFocus : function(){
41567         
41568         this.assignDocWin();
41569         
41570         
41571         this.activated = true;
41572          
41573     
41574         if(Roo.isGecko){ // prevent silly gecko errors
41575             this.win.focus();
41576             var s = this.win.getSelection();
41577             if(!s.focusNode || s.focusNode.nodeType != 3){
41578                 var r = s.getRangeAt(0);
41579                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41580                 r.collapse(true);
41581                 this.deferFocus();
41582             }
41583             try{
41584                 this.execCmd('useCSS', true);
41585                 this.execCmd('styleWithCSS', false);
41586             }catch(e){}
41587         }
41588         this.owner.fireEvent('activate', this);
41589     },
41590
41591     // private
41592     adjustFont: function(btn){
41593         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41594         //if(Roo.isSafari){ // safari
41595         //    adjust *= 2;
41596        // }
41597         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41598         if(Roo.isSafari){ // safari
41599             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41600             v =  (v < 10) ? 10 : v;
41601             v =  (v > 48) ? 48 : v;
41602             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41603             
41604         }
41605         
41606         
41607         v = Math.max(1, v+adjust);
41608         
41609         this.execCmd('FontSize', v  );
41610     },
41611
41612     onEditorEvent : function(e)
41613     {
41614         this.owner.fireEvent('editorevent', this, e);
41615       //  this.updateToolbar();
41616         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41617     },
41618
41619     insertTag : function(tg)
41620     {
41621         // could be a bit smarter... -> wrap the current selected tRoo..
41622         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41623             
41624             range = this.createRange(this.getSelection());
41625             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41626             wrappingNode.appendChild(range.extractContents());
41627             range.insertNode(wrappingNode);
41628
41629             return;
41630             
41631             
41632             
41633         }
41634         this.execCmd("formatblock",   tg);
41635         
41636     },
41637     
41638     insertText : function(txt)
41639     {
41640         
41641         
41642         var range = this.createRange();
41643         range.deleteContents();
41644                //alert(Sender.getAttribute('label'));
41645                
41646         range.insertNode(this.doc.createTextNode(txt));
41647     } ,
41648     
41649      
41650
41651     /**
41652      * Executes a Midas editor command on the editor document and performs necessary focus and
41653      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41654      * @param {String} cmd The Midas command
41655      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41656      */
41657     relayCmd : function(cmd, value){
41658         this.win.focus();
41659         this.execCmd(cmd, value);
41660         this.owner.fireEvent('editorevent', this);
41661         //this.updateToolbar();
41662         this.owner.deferFocus();
41663     },
41664
41665     /**
41666      * Executes a Midas editor command directly on the editor document.
41667      * For visual commands, you should use {@link #relayCmd} instead.
41668      * <b>This should only be called after the editor is initialized.</b>
41669      * @param {String} cmd The Midas command
41670      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41671      */
41672     execCmd : function(cmd, value){
41673         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41674         this.syncValue();
41675     },
41676  
41677  
41678    
41679     /**
41680      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41681      * to insert tRoo.
41682      * @param {String} text | dom node.. 
41683      */
41684     insertAtCursor : function(text)
41685     {
41686         
41687         
41688         
41689         if(!this.activated){
41690             return;
41691         }
41692         /*
41693         if(Roo.isIE){
41694             this.win.focus();
41695             var r = this.doc.selection.createRange();
41696             if(r){
41697                 r.collapse(true);
41698                 r.pasteHTML(text);
41699                 this.syncValue();
41700                 this.deferFocus();
41701             
41702             }
41703             return;
41704         }
41705         */
41706         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41707             this.win.focus();
41708             
41709             
41710             // from jquery ui (MIT licenced)
41711             var range, node;
41712             var win = this.win;
41713             
41714             if (win.getSelection && win.getSelection().getRangeAt) {
41715                 range = win.getSelection().getRangeAt(0);
41716                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41717                 range.insertNode(node);
41718             } else if (win.document.selection && win.document.selection.createRange) {
41719                 // no firefox support
41720                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41721                 win.document.selection.createRange().pasteHTML(txt);
41722             } else {
41723                 // no firefox support
41724                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41725                 this.execCmd('InsertHTML', txt);
41726             } 
41727             
41728             this.syncValue();
41729             
41730             this.deferFocus();
41731         }
41732     },
41733  // private
41734     mozKeyPress : function(e){
41735         if(e.ctrlKey){
41736             var c = e.getCharCode(), cmd;
41737           
41738             if(c > 0){
41739                 c = String.fromCharCode(c).toLowerCase();
41740                 switch(c){
41741                     case 'b':
41742                         cmd = 'bold';
41743                         break;
41744                     case 'i':
41745                         cmd = 'italic';
41746                         break;
41747                     
41748                     case 'u':
41749                         cmd = 'underline';
41750                         break;
41751                     
41752                     case 'v':
41753                         this.cleanUpPaste.defer(100, this);
41754                         return;
41755                         
41756                 }
41757                 if(cmd){
41758                     this.win.focus();
41759                     this.execCmd(cmd);
41760                     this.deferFocus();
41761                     e.preventDefault();
41762                 }
41763                 
41764             }
41765         }
41766     },
41767
41768     // private
41769     fixKeys : function(){ // load time branching for fastest keydown performance
41770         if(Roo.isIE){
41771             return function(e){
41772                 var k = e.getKey(), r;
41773                 if(k == e.TAB){
41774                     e.stopEvent();
41775                     r = this.doc.selection.createRange();
41776                     if(r){
41777                         r.collapse(true);
41778                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41779                         this.deferFocus();
41780                     }
41781                     return;
41782                 }
41783                 
41784                 if(k == e.ENTER){
41785                     r = this.doc.selection.createRange();
41786                     if(r){
41787                         var target = r.parentElement();
41788                         if(!target || target.tagName.toLowerCase() != 'li'){
41789                             e.stopEvent();
41790                             r.pasteHTML('<br />');
41791                             r.collapse(false);
41792                             r.select();
41793                         }
41794                     }
41795                 }
41796                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41797                     this.cleanUpPaste.defer(100, this);
41798                     return;
41799                 }
41800                 
41801                 
41802             };
41803         }else if(Roo.isOpera){
41804             return function(e){
41805                 var k = e.getKey();
41806                 if(k == e.TAB){
41807                     e.stopEvent();
41808                     this.win.focus();
41809                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41810                     this.deferFocus();
41811                 }
41812                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41813                     this.cleanUpPaste.defer(100, this);
41814                     return;
41815                 }
41816                 
41817             };
41818         }else if(Roo.isSafari){
41819             return function(e){
41820                 var k = e.getKey();
41821                 
41822                 if(k == e.TAB){
41823                     e.stopEvent();
41824                     this.execCmd('InsertText','\t');
41825                     this.deferFocus();
41826                     return;
41827                 }
41828                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41829                     this.cleanUpPaste.defer(100, this);
41830                     return;
41831                 }
41832                 
41833              };
41834         }
41835     }(),
41836     
41837     getAllAncestors: function()
41838     {
41839         var p = this.getSelectedNode();
41840         var a = [];
41841         if (!p) {
41842             a.push(p); // push blank onto stack..
41843             p = this.getParentElement();
41844         }
41845         
41846         
41847         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41848             a.push(p);
41849             p = p.parentNode;
41850         }
41851         a.push(this.doc.body);
41852         return a;
41853     },
41854     lastSel : false,
41855     lastSelNode : false,
41856     
41857     
41858     getSelection : function() 
41859     {
41860         this.assignDocWin();
41861         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41862     },
41863     
41864     getSelectedNode: function() 
41865     {
41866         // this may only work on Gecko!!!
41867         
41868         // should we cache this!!!!
41869         
41870         
41871         
41872          
41873         var range = this.createRange(this.getSelection()).cloneRange();
41874         
41875         if (Roo.isIE) {
41876             var parent = range.parentElement();
41877             while (true) {
41878                 var testRange = range.duplicate();
41879                 testRange.moveToElementText(parent);
41880                 if (testRange.inRange(range)) {
41881                     break;
41882                 }
41883                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41884                     break;
41885                 }
41886                 parent = parent.parentElement;
41887             }
41888             return parent;
41889         }
41890         
41891         // is ancestor a text element.
41892         var ac =  range.commonAncestorContainer;
41893         if (ac.nodeType == 3) {
41894             ac = ac.parentNode;
41895         }
41896         
41897         var ar = ac.childNodes;
41898          
41899         var nodes = [];
41900         var other_nodes = [];
41901         var has_other_nodes = false;
41902         for (var i=0;i<ar.length;i++) {
41903             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41904                 continue;
41905             }
41906             // fullly contained node.
41907             
41908             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41909                 nodes.push(ar[i]);
41910                 continue;
41911             }
41912             
41913             // probably selected..
41914             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41915                 other_nodes.push(ar[i]);
41916                 continue;
41917             }
41918             // outer..
41919             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41920                 continue;
41921             }
41922             
41923             
41924             has_other_nodes = true;
41925         }
41926         if (!nodes.length && other_nodes.length) {
41927             nodes= other_nodes;
41928         }
41929         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41930             return false;
41931         }
41932         
41933         return nodes[0];
41934     },
41935     createRange: function(sel)
41936     {
41937         // this has strange effects when using with 
41938         // top toolbar - not sure if it's a great idea.
41939         //this.editor.contentWindow.focus();
41940         if (typeof sel != "undefined") {
41941             try {
41942                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41943             } catch(e) {
41944                 return this.doc.createRange();
41945             }
41946         } else {
41947             return this.doc.createRange();
41948         }
41949     },
41950     getParentElement: function()
41951     {
41952         
41953         this.assignDocWin();
41954         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41955         
41956         var range = this.createRange(sel);
41957          
41958         try {
41959             var p = range.commonAncestorContainer;
41960             while (p.nodeType == 3) { // text node
41961                 p = p.parentNode;
41962             }
41963             return p;
41964         } catch (e) {
41965             return null;
41966         }
41967     
41968     },
41969     /***
41970      *
41971      * Range intersection.. the hard stuff...
41972      *  '-1' = before
41973      *  '0' = hits..
41974      *  '1' = after.
41975      *         [ -- selected range --- ]
41976      *   [fail]                        [fail]
41977      *
41978      *    basically..
41979      *      if end is before start or  hits it. fail.
41980      *      if start is after end or hits it fail.
41981      *
41982      *   if either hits (but other is outside. - then it's not 
41983      *   
41984      *    
41985      **/
41986     
41987     
41988     // @see http://www.thismuchiknow.co.uk/?p=64.
41989     rangeIntersectsNode : function(range, node)
41990     {
41991         var nodeRange = node.ownerDocument.createRange();
41992         try {
41993             nodeRange.selectNode(node);
41994         } catch (e) {
41995             nodeRange.selectNodeContents(node);
41996         }
41997     
41998         var rangeStartRange = range.cloneRange();
41999         rangeStartRange.collapse(true);
42000     
42001         var rangeEndRange = range.cloneRange();
42002         rangeEndRange.collapse(false);
42003     
42004         var nodeStartRange = nodeRange.cloneRange();
42005         nodeStartRange.collapse(true);
42006     
42007         var nodeEndRange = nodeRange.cloneRange();
42008         nodeEndRange.collapse(false);
42009     
42010         return rangeStartRange.compareBoundaryPoints(
42011                  Range.START_TO_START, nodeEndRange) == -1 &&
42012                rangeEndRange.compareBoundaryPoints(
42013                  Range.START_TO_START, nodeStartRange) == 1;
42014         
42015          
42016     },
42017     rangeCompareNode : function(range, node)
42018     {
42019         var nodeRange = node.ownerDocument.createRange();
42020         try {
42021             nodeRange.selectNode(node);
42022         } catch (e) {
42023             nodeRange.selectNodeContents(node);
42024         }
42025         
42026         
42027         range.collapse(true);
42028     
42029         nodeRange.collapse(true);
42030      
42031         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42032         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42033          
42034         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42035         
42036         var nodeIsBefore   =  ss == 1;
42037         var nodeIsAfter    = ee == -1;
42038         
42039         if (nodeIsBefore && nodeIsAfter)
42040             return 0; // outer
42041         if (!nodeIsBefore && nodeIsAfter)
42042             return 1; //right trailed.
42043         
42044         if (nodeIsBefore && !nodeIsAfter)
42045             return 2;  // left trailed.
42046         // fully contined.
42047         return 3;
42048     },
42049
42050     // private? - in a new class?
42051     cleanUpPaste :  function()
42052     {
42053         // cleans up the whole document..
42054         Roo.log('cleanuppaste');
42055         
42056         this.cleanUpChildren(this.doc.body);
42057         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42058         if (clean != this.doc.body.innerHTML) {
42059             this.doc.body.innerHTML = clean;
42060         }
42061         
42062     },
42063     
42064     cleanWordChars : function(input) {// change the chars to hex code
42065         var he = Roo.HtmlEditorCore;
42066         
42067         var output = input;
42068         Roo.each(he.swapCodes, function(sw) { 
42069             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42070             
42071             output = output.replace(swapper, sw[1]);
42072         });
42073         
42074         return output;
42075     },
42076     
42077     
42078     cleanUpChildren : function (n)
42079     {
42080         if (!n.childNodes.length) {
42081             return;
42082         }
42083         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42084            this.cleanUpChild(n.childNodes[i]);
42085         }
42086     },
42087     
42088     
42089         
42090     
42091     cleanUpChild : function (node)
42092     {
42093         var ed = this;
42094         //console.log(node);
42095         if (node.nodeName == "#text") {
42096             // clean up silly Windows -- stuff?
42097             return; 
42098         }
42099         if (node.nodeName == "#comment") {
42100             node.parentNode.removeChild(node);
42101             // clean up silly Windows -- stuff?
42102             return; 
42103         }
42104         var lcname = node.tagName.toLowerCase();
42105         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42106         // whitelist of tags..
42107         
42108         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42109             // remove node.
42110             node.parentNode.removeChild(node);
42111             return;
42112             
42113         }
42114         
42115         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42116         
42117         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42118         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42119         
42120         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42121         //    remove_keep_children = true;
42122         //}
42123         
42124         if (remove_keep_children) {
42125             this.cleanUpChildren(node);
42126             // inserts everything just before this node...
42127             while (node.childNodes.length) {
42128                 var cn = node.childNodes[0];
42129                 node.removeChild(cn);
42130                 node.parentNode.insertBefore(cn, node);
42131             }
42132             node.parentNode.removeChild(node);
42133             return;
42134         }
42135         
42136         if (!node.attributes || !node.attributes.length) {
42137             this.cleanUpChildren(node);
42138             return;
42139         }
42140         
42141         function cleanAttr(n,v)
42142         {
42143             
42144             if (v.match(/^\./) || v.match(/^\//)) {
42145                 return;
42146             }
42147             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42148                 return;
42149             }
42150             if (v.match(/^#/)) {
42151                 return;
42152             }
42153 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42154             node.removeAttribute(n);
42155             
42156         }
42157         
42158         var cwhite = this.cwhite;
42159         var cblack = this.cblack;
42160             
42161         function cleanStyle(n,v)
42162         {
42163             if (v.match(/expression/)) { //XSS?? should we even bother..
42164                 node.removeAttribute(n);
42165                 return;
42166             }
42167             
42168             var parts = v.split(/;/);
42169             var clean = [];
42170             
42171             Roo.each(parts, function(p) {
42172                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42173                 if (!p.length) {
42174                     return true;
42175                 }
42176                 var l = p.split(':').shift().replace(/\s+/g,'');
42177                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42178                 
42179                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42180 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42181                     //node.removeAttribute(n);
42182                     return true;
42183                 }
42184                 //Roo.log()
42185                 // only allow 'c whitelisted system attributes'
42186                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42187 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42188                     //node.removeAttribute(n);
42189                     return true;
42190                 }
42191                 
42192                 
42193                  
42194                 
42195                 clean.push(p);
42196                 return true;
42197             });
42198             if (clean.length) { 
42199                 node.setAttribute(n, clean.join(';'));
42200             } else {
42201                 node.removeAttribute(n);
42202             }
42203             
42204         }
42205         
42206         
42207         for (var i = node.attributes.length-1; i > -1 ; i--) {
42208             var a = node.attributes[i];
42209             //console.log(a);
42210             
42211             if (a.name.toLowerCase().substr(0,2)=='on')  {
42212                 node.removeAttribute(a.name);
42213                 continue;
42214             }
42215             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42216                 node.removeAttribute(a.name);
42217                 continue;
42218             }
42219             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42220                 cleanAttr(a.name,a.value); // fixme..
42221                 continue;
42222             }
42223             if (a.name == 'style') {
42224                 cleanStyle(a.name,a.value);
42225                 continue;
42226             }
42227             /// clean up MS crap..
42228             // tecnically this should be a list of valid class'es..
42229             
42230             
42231             if (a.name == 'class') {
42232                 if (a.value.match(/^Mso/)) {
42233                     node.className = '';
42234                 }
42235                 
42236                 if (a.value.match(/body/)) {
42237                     node.className = '';
42238                 }
42239                 continue;
42240             }
42241             
42242             // style cleanup!?
42243             // class cleanup?
42244             
42245         }
42246         
42247         
42248         this.cleanUpChildren(node);
42249         
42250         
42251     },
42252     
42253     /**
42254      * Clean up MS wordisms...
42255      */
42256     cleanWord : function(node)
42257     {
42258         
42259         
42260         if (!node) {
42261             this.cleanWord(this.doc.body);
42262             return;
42263         }
42264         if (node.nodeName == "#text") {
42265             // clean up silly Windows -- stuff?
42266             return; 
42267         }
42268         if (node.nodeName == "#comment") {
42269             node.parentNode.removeChild(node);
42270             // clean up silly Windows -- stuff?
42271             return; 
42272         }
42273         
42274         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42275             node.parentNode.removeChild(node);
42276             return;
42277         }
42278         
42279         // remove - but keep children..
42280         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42281             while (node.childNodes.length) {
42282                 var cn = node.childNodes[0];
42283                 node.removeChild(cn);
42284                 node.parentNode.insertBefore(cn, node);
42285             }
42286             node.parentNode.removeChild(node);
42287             this.iterateChildren(node, this.cleanWord);
42288             return;
42289         }
42290         // clean styles
42291         if (node.className.length) {
42292             
42293             var cn = node.className.split(/\W+/);
42294             var cna = [];
42295             Roo.each(cn, function(cls) {
42296                 if (cls.match(/Mso[a-zA-Z]+/)) {
42297                     return;
42298                 }
42299                 cna.push(cls);
42300             });
42301             node.className = cna.length ? cna.join(' ') : '';
42302             if (!cna.length) {
42303                 node.removeAttribute("class");
42304             }
42305         }
42306         
42307         if (node.hasAttribute("lang")) {
42308             node.removeAttribute("lang");
42309         }
42310         
42311         if (node.hasAttribute("style")) {
42312             
42313             var styles = node.getAttribute("style").split(";");
42314             var nstyle = [];
42315             Roo.each(styles, function(s) {
42316                 if (!s.match(/:/)) {
42317                     return;
42318                 }
42319                 var kv = s.split(":");
42320                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42321                     return;
42322                 }
42323                 // what ever is left... we allow.
42324                 nstyle.push(s);
42325             });
42326             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42327             if (!nstyle.length) {
42328                 node.removeAttribute('style');
42329             }
42330         }
42331         this.iterateChildren(node, this.cleanWord);
42332         
42333         
42334         
42335     },
42336     /**
42337      * iterateChildren of a Node, calling fn each time, using this as the scole..
42338      * @param {DomNode} node node to iterate children of.
42339      * @param {Function} fn method of this class to call on each item.
42340      */
42341     iterateChildren : function(node, fn)
42342     {
42343         if (!node.childNodes.length) {
42344                 return;
42345         }
42346         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42347            fn.call(this, node.childNodes[i])
42348         }
42349     },
42350     
42351     
42352     /**
42353      * cleanTableWidths.
42354      *
42355      * Quite often pasting from word etc.. results in tables with column and widths.
42356      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42357      *
42358      */
42359     cleanTableWidths : function(node)
42360     {
42361          
42362          
42363         if (!node) {
42364             this.cleanTableWidths(this.doc.body);
42365             return;
42366         }
42367         
42368         // ignore list...
42369         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42370             return; 
42371         }
42372         Roo.log(node.tagName);
42373         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42374             this.iterateChildren(node, this.cleanTableWidths);
42375             return;
42376         }
42377         if (node.hasAttribute('width')) {
42378             node.removeAttribute('width');
42379         }
42380         
42381          
42382         if (node.hasAttribute("style")) {
42383             // pretty basic...
42384             
42385             var styles = node.getAttribute("style").split(";");
42386             var nstyle = [];
42387             Roo.each(styles, function(s) {
42388                 if (!s.match(/:/)) {
42389                     return;
42390                 }
42391                 var kv = s.split(":");
42392                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42393                     return;
42394                 }
42395                 // what ever is left... we allow.
42396                 nstyle.push(s);
42397             });
42398             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42399             if (!nstyle.length) {
42400                 node.removeAttribute('style');
42401             }
42402         }
42403         
42404         this.iterateChildren(node, this.cleanTableWidths);
42405         
42406         
42407     },
42408     
42409     
42410     
42411     
42412     domToHTML : function(currentElement, depth, nopadtext) {
42413         
42414         depth = depth || 0;
42415         nopadtext = nopadtext || false;
42416     
42417         if (!currentElement) {
42418             return this.domToHTML(this.doc.body);
42419         }
42420         
42421         //Roo.log(currentElement);
42422         var j;
42423         var allText = false;
42424         var nodeName = currentElement.nodeName;
42425         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42426         
42427         if  (nodeName == '#text') {
42428             
42429             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42430         }
42431         
42432         
42433         var ret = '';
42434         if (nodeName != 'BODY') {
42435              
42436             var i = 0;
42437             // Prints the node tagName, such as <A>, <IMG>, etc
42438             if (tagName) {
42439                 var attr = [];
42440                 for(i = 0; i < currentElement.attributes.length;i++) {
42441                     // quoting?
42442                     var aname = currentElement.attributes.item(i).name;
42443                     if (!currentElement.attributes.item(i).value.length) {
42444                         continue;
42445                     }
42446                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42447                 }
42448                 
42449                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42450             } 
42451             else {
42452                 
42453                 // eack
42454             }
42455         } else {
42456             tagName = false;
42457         }
42458         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42459             return ret;
42460         }
42461         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42462             nopadtext = true;
42463         }
42464         
42465         
42466         // Traverse the tree
42467         i = 0;
42468         var currentElementChild = currentElement.childNodes.item(i);
42469         var allText = true;
42470         var innerHTML  = '';
42471         lastnode = '';
42472         while (currentElementChild) {
42473             // Formatting code (indent the tree so it looks nice on the screen)
42474             var nopad = nopadtext;
42475             if (lastnode == 'SPAN') {
42476                 nopad  = true;
42477             }
42478             // text
42479             if  (currentElementChild.nodeName == '#text') {
42480                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42481                 toadd = nopadtext ? toadd : toadd.trim();
42482                 if (!nopad && toadd.length > 80) {
42483                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42484                 }
42485                 innerHTML  += toadd;
42486                 
42487                 i++;
42488                 currentElementChild = currentElement.childNodes.item(i);
42489                 lastNode = '';
42490                 continue;
42491             }
42492             allText = false;
42493             
42494             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42495                 
42496             // Recursively traverse the tree structure of the child node
42497             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42498             lastnode = currentElementChild.nodeName;
42499             i++;
42500             currentElementChild=currentElement.childNodes.item(i);
42501         }
42502         
42503         ret += innerHTML;
42504         
42505         if (!allText) {
42506                 // The remaining code is mostly for formatting the tree
42507             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42508         }
42509         
42510         
42511         if (tagName) {
42512             ret+= "</"+tagName+">";
42513         }
42514         return ret;
42515         
42516     },
42517         
42518     applyBlacklists : function()
42519     {
42520         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42521         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42522         
42523         this.white = [];
42524         this.black = [];
42525         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42526             if (b.indexOf(tag) > -1) {
42527                 return;
42528             }
42529             this.white.push(tag);
42530             
42531         }, this);
42532         
42533         Roo.each(w, function(tag) {
42534             if (b.indexOf(tag) > -1) {
42535                 return;
42536             }
42537             if (this.white.indexOf(tag) > -1) {
42538                 return;
42539             }
42540             this.white.push(tag);
42541             
42542         }, this);
42543         
42544         
42545         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42546             if (w.indexOf(tag) > -1) {
42547                 return;
42548             }
42549             this.black.push(tag);
42550             
42551         }, this);
42552         
42553         Roo.each(b, function(tag) {
42554             if (w.indexOf(tag) > -1) {
42555                 return;
42556             }
42557             if (this.black.indexOf(tag) > -1) {
42558                 return;
42559             }
42560             this.black.push(tag);
42561             
42562         }, this);
42563         
42564         
42565         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42566         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42567         
42568         this.cwhite = [];
42569         this.cblack = [];
42570         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42571             if (b.indexOf(tag) > -1) {
42572                 return;
42573             }
42574             this.cwhite.push(tag);
42575             
42576         }, this);
42577         
42578         Roo.each(w, function(tag) {
42579             if (b.indexOf(tag) > -1) {
42580                 return;
42581             }
42582             if (this.cwhite.indexOf(tag) > -1) {
42583                 return;
42584             }
42585             this.cwhite.push(tag);
42586             
42587         }, this);
42588         
42589         
42590         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42591             if (w.indexOf(tag) > -1) {
42592                 return;
42593             }
42594             this.cblack.push(tag);
42595             
42596         }, this);
42597         
42598         Roo.each(b, function(tag) {
42599             if (w.indexOf(tag) > -1) {
42600                 return;
42601             }
42602             if (this.cblack.indexOf(tag) > -1) {
42603                 return;
42604             }
42605             this.cblack.push(tag);
42606             
42607         }, this);
42608     },
42609     
42610     setStylesheets : function(stylesheets)
42611     {
42612         if(typeof(stylesheets) == 'string'){
42613             Roo.get(this.iframe.contentDocument.head).createChild({
42614                 tag : 'link',
42615                 rel : 'stylesheet',
42616                 type : 'text/css',
42617                 href : stylesheets
42618             });
42619             
42620             return;
42621         }
42622         var _this = this;
42623      
42624         Roo.each(stylesheets, function(s) {
42625             if(!s.length){
42626                 return;
42627             }
42628             
42629             Roo.get(_this.iframe.contentDocument.head).createChild({
42630                 tag : 'link',
42631                 rel : 'stylesheet',
42632                 type : 'text/css',
42633                 href : s
42634             });
42635         });
42636
42637         
42638     },
42639     
42640     removeStylesheets : function()
42641     {
42642         var _this = this;
42643         
42644         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42645             s.remove();
42646         });
42647     }
42648     
42649     // hide stuff that is not compatible
42650     /**
42651      * @event blur
42652      * @hide
42653      */
42654     /**
42655      * @event change
42656      * @hide
42657      */
42658     /**
42659      * @event focus
42660      * @hide
42661      */
42662     /**
42663      * @event specialkey
42664      * @hide
42665      */
42666     /**
42667      * @cfg {String} fieldClass @hide
42668      */
42669     /**
42670      * @cfg {String} focusClass @hide
42671      */
42672     /**
42673      * @cfg {String} autoCreate @hide
42674      */
42675     /**
42676      * @cfg {String} inputType @hide
42677      */
42678     /**
42679      * @cfg {String} invalidClass @hide
42680      */
42681     /**
42682      * @cfg {String} invalidText @hide
42683      */
42684     /**
42685      * @cfg {String} msgFx @hide
42686      */
42687     /**
42688      * @cfg {String} validateOnBlur @hide
42689      */
42690 });
42691
42692 Roo.HtmlEditorCore.white = [
42693         'area', 'br', 'img', 'input', 'hr', 'wbr',
42694         
42695        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42696        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42697        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42698        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42699        'table',   'ul',         'xmp', 
42700        
42701        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42702       'thead',   'tr', 
42703      
42704       'dir', 'menu', 'ol', 'ul', 'dl',
42705        
42706       'embed',  'object'
42707 ];
42708
42709
42710 Roo.HtmlEditorCore.black = [
42711     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42712         'applet', // 
42713         'base',   'basefont', 'bgsound', 'blink',  'body', 
42714         'frame',  'frameset', 'head',    'html',   'ilayer', 
42715         'iframe', 'layer',  'link',     'meta',    'object',   
42716         'script', 'style' ,'title',  'xml' // clean later..
42717 ];
42718 Roo.HtmlEditorCore.clean = [
42719     'script', 'style', 'title', 'xml'
42720 ];
42721 Roo.HtmlEditorCore.remove = [
42722     'font'
42723 ];
42724 // attributes..
42725
42726 Roo.HtmlEditorCore.ablack = [
42727     'on'
42728 ];
42729     
42730 Roo.HtmlEditorCore.aclean = [ 
42731     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42732 ];
42733
42734 // protocols..
42735 Roo.HtmlEditorCore.pwhite= [
42736         'http',  'https',  'mailto'
42737 ];
42738
42739 // white listed style attributes.
42740 Roo.HtmlEditorCore.cwhite= [
42741       //  'text-align', /// default is to allow most things..
42742       
42743          
42744 //        'font-size'//??
42745 ];
42746
42747 // black listed style attributes.
42748 Roo.HtmlEditorCore.cblack= [
42749       //  'font-size' -- this can be set by the project 
42750 ];
42751
42752
42753 Roo.HtmlEditorCore.swapCodes   =[ 
42754     [    8211, "--" ], 
42755     [    8212, "--" ], 
42756     [    8216,  "'" ],  
42757     [    8217, "'" ],  
42758     [    8220, '"' ],  
42759     [    8221, '"' ],  
42760     [    8226, "*" ],  
42761     [    8230, "..." ]
42762 ]; 
42763
42764     //<script type="text/javascript">
42765
42766 /*
42767  * Ext JS Library 1.1.1
42768  * Copyright(c) 2006-2007, Ext JS, LLC.
42769  * Licence LGPL
42770  * 
42771  */
42772  
42773  
42774 Roo.form.HtmlEditor = function(config){
42775     
42776     
42777     
42778     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42779     
42780     if (!this.toolbars) {
42781         this.toolbars = [];
42782     }
42783     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42784     
42785     
42786 };
42787
42788 /**
42789  * @class Roo.form.HtmlEditor
42790  * @extends Roo.form.Field
42791  * Provides a lightweight HTML Editor component.
42792  *
42793  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42794  * 
42795  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42796  * supported by this editor.</b><br/><br/>
42797  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42798  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42799  */
42800 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42801     /**
42802      * @cfg {Boolean} clearUp
42803      */
42804     clearUp : true,
42805       /**
42806      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42807      */
42808     toolbars : false,
42809    
42810      /**
42811      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42812      *                        Roo.resizable.
42813      */
42814     resizable : false,
42815      /**
42816      * @cfg {Number} height (in pixels)
42817      */   
42818     height: 300,
42819    /**
42820      * @cfg {Number} width (in pixels)
42821      */   
42822     width: 500,
42823     
42824     /**
42825      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42826      * 
42827      */
42828     stylesheets: false,
42829     
42830     
42831      /**
42832      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42833      * 
42834      */
42835     cblack: false,
42836     /**
42837      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42838      * 
42839      */
42840     cwhite: false,
42841     
42842      /**
42843      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42844      * 
42845      */
42846     black: false,
42847     /**
42848      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42849      * 
42850      */
42851     white: false,
42852     
42853     // id of frame..
42854     frameId: false,
42855     
42856     // private properties
42857     validationEvent : false,
42858     deferHeight: true,
42859     initialized : false,
42860     activated : false,
42861     
42862     onFocus : Roo.emptyFn,
42863     iframePad:3,
42864     hideMode:'offsets',
42865     
42866     actionMode : 'container', // defaults to hiding it...
42867     
42868     defaultAutoCreate : { // modified by initCompnoent..
42869         tag: "textarea",
42870         style:"width:500px;height:300px;",
42871         autocomplete: "new-password"
42872     },
42873
42874     // private
42875     initComponent : function(){
42876         this.addEvents({
42877             /**
42878              * @event initialize
42879              * Fires when the editor is fully initialized (including the iframe)
42880              * @param {HtmlEditor} this
42881              */
42882             initialize: true,
42883             /**
42884              * @event activate
42885              * Fires when the editor is first receives the focus. Any insertion must wait
42886              * until after this event.
42887              * @param {HtmlEditor} this
42888              */
42889             activate: true,
42890              /**
42891              * @event beforesync
42892              * Fires before the textarea is updated with content from the editor iframe. Return false
42893              * to cancel the sync.
42894              * @param {HtmlEditor} this
42895              * @param {String} html
42896              */
42897             beforesync: true,
42898              /**
42899              * @event beforepush
42900              * Fires before the iframe editor is updated with content from the textarea. Return false
42901              * to cancel the push.
42902              * @param {HtmlEditor} this
42903              * @param {String} html
42904              */
42905             beforepush: true,
42906              /**
42907              * @event sync
42908              * Fires when the textarea is updated with content from the editor iframe.
42909              * @param {HtmlEditor} this
42910              * @param {String} html
42911              */
42912             sync: true,
42913              /**
42914              * @event push
42915              * Fires when the iframe editor is updated with content from the textarea.
42916              * @param {HtmlEditor} this
42917              * @param {String} html
42918              */
42919             push: true,
42920              /**
42921              * @event editmodechange
42922              * Fires when the editor switches edit modes
42923              * @param {HtmlEditor} this
42924              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42925              */
42926             editmodechange: true,
42927             /**
42928              * @event editorevent
42929              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42930              * @param {HtmlEditor} this
42931              */
42932             editorevent: true,
42933             /**
42934              * @event firstfocus
42935              * Fires when on first focus - needed by toolbars..
42936              * @param {HtmlEditor} this
42937              */
42938             firstfocus: true,
42939             /**
42940              * @event autosave
42941              * Auto save the htmlEditor value as a file into Events
42942              * @param {HtmlEditor} this
42943              */
42944             autosave: true,
42945             /**
42946              * @event savedpreview
42947              * preview the saved version of htmlEditor
42948              * @param {HtmlEditor} this
42949              */
42950             savedpreview: true,
42951             
42952             /**
42953             * @event stylesheetsclick
42954             * Fires when press the Sytlesheets button
42955             * @param {Roo.HtmlEditorCore} this
42956             */
42957             stylesheetsclick: true
42958         });
42959         this.defaultAutoCreate =  {
42960             tag: "textarea",
42961             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42962             autocomplete: "new-password"
42963         };
42964     },
42965
42966     /**
42967      * Protected method that will not generally be called directly. It
42968      * is called when the editor creates its toolbar. Override this method if you need to
42969      * add custom toolbar buttons.
42970      * @param {HtmlEditor} editor
42971      */
42972     createToolbar : function(editor){
42973         Roo.log("create toolbars");
42974         if (!editor.toolbars || !editor.toolbars.length) {
42975             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42976         }
42977         
42978         for (var i =0 ; i < editor.toolbars.length;i++) {
42979             editor.toolbars[i] = Roo.factory(
42980                     typeof(editor.toolbars[i]) == 'string' ?
42981                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42982                 Roo.form.HtmlEditor);
42983             editor.toolbars[i].init(editor);
42984         }
42985          
42986         
42987     },
42988
42989      
42990     // private
42991     onRender : function(ct, position)
42992     {
42993         var _t = this;
42994         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42995         
42996         this.wrap = this.el.wrap({
42997             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42998         });
42999         
43000         this.editorcore.onRender(ct, position);
43001          
43002         if (this.resizable) {
43003             this.resizeEl = new Roo.Resizable(this.wrap, {
43004                 pinned : true,
43005                 wrap: true,
43006                 dynamic : true,
43007                 minHeight : this.height,
43008                 height: this.height,
43009                 handles : this.resizable,
43010                 width: this.width,
43011                 listeners : {
43012                     resize : function(r, w, h) {
43013                         _t.onResize(w,h); // -something
43014                     }
43015                 }
43016             });
43017             
43018         }
43019         this.createToolbar(this);
43020        
43021         
43022         if(!this.width){
43023             this.setSize(this.wrap.getSize());
43024         }
43025         if (this.resizeEl) {
43026             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43027             // should trigger onReize..
43028         }
43029         
43030         this.keyNav = new Roo.KeyNav(this.el, {
43031             
43032             "tab" : function(e){
43033                 e.preventDefault();
43034                 
43035                 var value = this.getValue();
43036                 
43037                 var start = this.el.dom.selectionStart;
43038                 var end = this.el.dom.selectionEnd;
43039                 
43040                 if(!e.shiftKey){
43041                     
43042                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43043                     this.el.dom.setSelectionRange(end + 1, end + 1);
43044                     return;
43045                 }
43046                 
43047                 var f = value.substring(0, start).split("\t");
43048                 
43049                 if(f.pop().length != 0){
43050                     return;
43051                 }
43052                 
43053                 this.setValue(f.join("\t") + value.substring(end));
43054                 this.el.dom.setSelectionRange(start - 1, start - 1);
43055                 
43056             },
43057             
43058             "home" : function(e){
43059                 e.preventDefault();
43060                 
43061                 var curr = this.el.dom.selectionStart;
43062                 var lines = this.getValue().split("\n");
43063                 
43064                 if(!lines.length){
43065                     return;
43066                 }
43067                 
43068                 if(e.ctrlKey){
43069                     this.el.dom.setSelectionRange(0, 0);
43070                     return;
43071                 }
43072                 
43073                 var pos = 0;
43074                 
43075                 for (var i = 0; i < lines.length;i++) {
43076                     pos += lines[i].length;
43077                     
43078                     if(i != 0){
43079                         pos += 1;
43080                     }
43081                     
43082                     if(pos < curr){
43083                         continue;
43084                     }
43085                     
43086                     pos -= lines[i].length;
43087                     
43088                     break;
43089                 }
43090                 
43091                 if(!e.shiftKey){
43092                     this.el.dom.setSelectionRange(pos, pos);
43093                     return;
43094                 }
43095                 
43096                 this.el.dom.selectionStart = pos;
43097                 this.el.dom.selectionEnd = curr;
43098             },
43099             
43100             "end" : function(e){
43101                 e.preventDefault();
43102                 
43103                 var curr = this.el.dom.selectionStart;
43104                 var lines = this.getValue().split("\n");
43105                 
43106                 if(!lines.length){
43107                     return;
43108                 }
43109                 
43110                 if(e.ctrlKey){
43111                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43112                     return;
43113                 }
43114                 
43115                 var pos = 0;
43116                 
43117                 for (var i = 0; i < lines.length;i++) {
43118                     
43119                     pos += lines[i].length;
43120                     
43121                     if(i != 0){
43122                         pos += 1;
43123                     }
43124                     
43125                     if(pos < curr){
43126                         continue;
43127                     }
43128                     
43129                     break;
43130                 }
43131                 
43132                 if(!e.shiftKey){
43133                     this.el.dom.setSelectionRange(pos, pos);
43134                     return;
43135                 }
43136                 
43137                 this.el.dom.selectionStart = curr;
43138                 this.el.dom.selectionEnd = pos;
43139             },
43140
43141             scope : this,
43142
43143             doRelay : function(foo, bar, hname){
43144                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43145             },
43146
43147             forceKeyDown: true
43148         });
43149         
43150 //        if(this.autosave && this.w){
43151 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43152 //        }
43153     },
43154
43155     // private
43156     onResize : function(w, h)
43157     {
43158         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43159         var ew = false;
43160         var eh = false;
43161         
43162         if(this.el ){
43163             if(typeof w == 'number'){
43164                 var aw = w - this.wrap.getFrameWidth('lr');
43165                 this.el.setWidth(this.adjustWidth('textarea', aw));
43166                 ew = aw;
43167             }
43168             if(typeof h == 'number'){
43169                 var tbh = 0;
43170                 for (var i =0; i < this.toolbars.length;i++) {
43171                     // fixme - ask toolbars for heights?
43172                     tbh += this.toolbars[i].tb.el.getHeight();
43173                     if (this.toolbars[i].footer) {
43174                         tbh += this.toolbars[i].footer.el.getHeight();
43175                     }
43176                 }
43177                 
43178                 
43179                 
43180                 
43181                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43182                 ah -= 5; // knock a few pixes off for look..
43183 //                Roo.log(ah);
43184                 this.el.setHeight(this.adjustWidth('textarea', ah));
43185                 var eh = ah;
43186             }
43187         }
43188         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43189         this.editorcore.onResize(ew,eh);
43190         
43191     },
43192
43193     /**
43194      * Toggles the editor between standard and source edit mode.
43195      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43196      */
43197     toggleSourceEdit : function(sourceEditMode)
43198     {
43199         this.editorcore.toggleSourceEdit(sourceEditMode);
43200         
43201         if(this.editorcore.sourceEditMode){
43202             Roo.log('editor - showing textarea');
43203             
43204 //            Roo.log('in');
43205 //            Roo.log(this.syncValue());
43206             this.editorcore.syncValue();
43207             this.el.removeClass('x-hidden');
43208             this.el.dom.removeAttribute('tabIndex');
43209             this.el.focus();
43210             
43211             for (var i = 0; i < this.toolbars.length; i++) {
43212                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43213                     this.toolbars[i].tb.hide();
43214                     this.toolbars[i].footer.hide();
43215                 }
43216             }
43217             
43218         }else{
43219             Roo.log('editor - hiding textarea');
43220 //            Roo.log('out')
43221 //            Roo.log(this.pushValue()); 
43222             this.editorcore.pushValue();
43223             
43224             this.el.addClass('x-hidden');
43225             this.el.dom.setAttribute('tabIndex', -1);
43226             
43227             for (var i = 0; i < this.toolbars.length; i++) {
43228                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43229                     this.toolbars[i].tb.show();
43230                     this.toolbars[i].footer.show();
43231                 }
43232             }
43233             
43234             //this.deferFocus();
43235         }
43236         
43237         this.setSize(this.wrap.getSize());
43238         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43239         
43240         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43241     },
43242  
43243     // private (for BoxComponent)
43244     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43245
43246     // private (for BoxComponent)
43247     getResizeEl : function(){
43248         return this.wrap;
43249     },
43250
43251     // private (for BoxComponent)
43252     getPositionEl : function(){
43253         return this.wrap;
43254     },
43255
43256     // private
43257     initEvents : function(){
43258         this.originalValue = this.getValue();
43259     },
43260
43261     /**
43262      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43263      * @method
43264      */
43265     markInvalid : Roo.emptyFn,
43266     /**
43267      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43268      * @method
43269      */
43270     clearInvalid : Roo.emptyFn,
43271
43272     setValue : function(v){
43273         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43274         this.editorcore.pushValue();
43275     },
43276
43277      
43278     // private
43279     deferFocus : function(){
43280         this.focus.defer(10, this);
43281     },
43282
43283     // doc'ed in Field
43284     focus : function(){
43285         this.editorcore.focus();
43286         
43287     },
43288       
43289
43290     // private
43291     onDestroy : function(){
43292         
43293         
43294         
43295         if(this.rendered){
43296             
43297             for (var i =0; i < this.toolbars.length;i++) {
43298                 // fixme - ask toolbars for heights?
43299                 this.toolbars[i].onDestroy();
43300             }
43301             
43302             this.wrap.dom.innerHTML = '';
43303             this.wrap.remove();
43304         }
43305     },
43306
43307     // private
43308     onFirstFocus : function(){
43309         //Roo.log("onFirstFocus");
43310         this.editorcore.onFirstFocus();
43311          for (var i =0; i < this.toolbars.length;i++) {
43312             this.toolbars[i].onFirstFocus();
43313         }
43314         
43315     },
43316     
43317     // private
43318     syncValue : function()
43319     {
43320         this.editorcore.syncValue();
43321     },
43322     
43323     pushValue : function()
43324     {
43325         this.editorcore.pushValue();
43326     },
43327     
43328     setStylesheets : function(stylesheets)
43329     {
43330         this.editorcore.setStylesheets(stylesheets);
43331     },
43332     
43333     removeStylesheets : function()
43334     {
43335         this.editorcore.removeStylesheets();
43336     }
43337      
43338     
43339     // hide stuff that is not compatible
43340     /**
43341      * @event blur
43342      * @hide
43343      */
43344     /**
43345      * @event change
43346      * @hide
43347      */
43348     /**
43349      * @event focus
43350      * @hide
43351      */
43352     /**
43353      * @event specialkey
43354      * @hide
43355      */
43356     /**
43357      * @cfg {String} fieldClass @hide
43358      */
43359     /**
43360      * @cfg {String} focusClass @hide
43361      */
43362     /**
43363      * @cfg {String} autoCreate @hide
43364      */
43365     /**
43366      * @cfg {String} inputType @hide
43367      */
43368     /**
43369      * @cfg {String} invalidClass @hide
43370      */
43371     /**
43372      * @cfg {String} invalidText @hide
43373      */
43374     /**
43375      * @cfg {String} msgFx @hide
43376      */
43377     /**
43378      * @cfg {String} validateOnBlur @hide
43379      */
43380 });
43381  
43382     // <script type="text/javascript">
43383 /*
43384  * Based on
43385  * Ext JS Library 1.1.1
43386  * Copyright(c) 2006-2007, Ext JS, LLC.
43387  *  
43388  
43389  */
43390
43391 /**
43392  * @class Roo.form.HtmlEditorToolbar1
43393  * Basic Toolbar
43394  * 
43395  * Usage:
43396  *
43397  new Roo.form.HtmlEditor({
43398     ....
43399     toolbars : [
43400         new Roo.form.HtmlEditorToolbar1({
43401             disable : { fonts: 1 , format: 1, ..., ... , ...],
43402             btns : [ .... ]
43403         })
43404     }
43405      
43406  * 
43407  * @cfg {Object} disable List of elements to disable..
43408  * @cfg {Array} btns List of additional buttons.
43409  * 
43410  * 
43411  * NEEDS Extra CSS? 
43412  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43413  */
43414  
43415 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43416 {
43417     
43418     Roo.apply(this, config);
43419     
43420     // default disabled, based on 'good practice'..
43421     this.disable = this.disable || {};
43422     Roo.applyIf(this.disable, {
43423         fontSize : true,
43424         colors : true,
43425         specialElements : true
43426     });
43427     
43428     
43429     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43430     // dont call parent... till later.
43431 }
43432
43433 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43434     
43435     tb: false,
43436     
43437     rendered: false,
43438     
43439     editor : false,
43440     editorcore : false,
43441     /**
43442      * @cfg {Object} disable  List of toolbar elements to disable
43443          
43444      */
43445     disable : false,
43446     
43447     
43448      /**
43449      * @cfg {String} createLinkText The default text for the create link prompt
43450      */
43451     createLinkText : 'Please enter the URL for the link:',
43452     /**
43453      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43454      */
43455     defaultLinkValue : 'http:/'+'/',
43456    
43457     
43458       /**
43459      * @cfg {Array} fontFamilies An array of available font families
43460      */
43461     fontFamilies : [
43462         'Arial',
43463         'Courier New',
43464         'Tahoma',
43465         'Times New Roman',
43466         'Verdana'
43467     ],
43468     
43469     specialChars : [
43470            "&#169;",
43471           "&#174;",     
43472           "&#8482;",    
43473           "&#163;" ,    
43474          // "&#8212;",    
43475           "&#8230;",    
43476           "&#247;" ,    
43477         //  "&#225;" ,     ?? a acute?
43478            "&#8364;"    , //Euro
43479        //   "&#8220;"    ,
43480         //  "&#8221;"    ,
43481         //  "&#8226;"    ,
43482           "&#176;"  //   , // degrees
43483
43484          // "&#233;"     , // e ecute
43485          // "&#250;"     , // u ecute?
43486     ],
43487     
43488     specialElements : [
43489         {
43490             text: "Insert Table",
43491             xtype: 'MenuItem',
43492             xns : Roo.Menu,
43493             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43494                 
43495         },
43496         {    
43497             text: "Insert Image",
43498             xtype: 'MenuItem',
43499             xns : Roo.Menu,
43500             ihtml : '<img src="about:blank"/>'
43501             
43502         }
43503         
43504          
43505     ],
43506     
43507     
43508     inputElements : [ 
43509             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43510             "input:submit", "input:button", "select", "textarea", "label" ],
43511     formats : [
43512         ["p"] ,  
43513         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43514         ["pre"],[ "code"], 
43515         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43516         ['div'],['span']
43517     ],
43518     
43519     cleanStyles : [
43520         "font-size"
43521     ],
43522      /**
43523      * @cfg {String} defaultFont default font to use.
43524      */
43525     defaultFont: 'tahoma',
43526    
43527     fontSelect : false,
43528     
43529     
43530     formatCombo : false,
43531     
43532     init : function(editor)
43533     {
43534         this.editor = editor;
43535         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43536         var editorcore = this.editorcore;
43537         
43538         var _t = this;
43539         
43540         var fid = editorcore.frameId;
43541         var etb = this;
43542         function btn(id, toggle, handler){
43543             var xid = fid + '-'+ id ;
43544             return {
43545                 id : xid,
43546                 cmd : id,
43547                 cls : 'x-btn-icon x-edit-'+id,
43548                 enableToggle:toggle !== false,
43549                 scope: _t, // was editor...
43550                 handler:handler||_t.relayBtnCmd,
43551                 clickEvent:'mousedown',
43552                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43553                 tabIndex:-1
43554             };
43555         }
43556         
43557         
43558         
43559         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43560         this.tb = tb;
43561          // stop form submits
43562         tb.el.on('click', function(e){
43563             e.preventDefault(); // what does this do?
43564         });
43565
43566         if(!this.disable.font) { // && !Roo.isSafari){
43567             /* why no safari for fonts 
43568             editor.fontSelect = tb.el.createChild({
43569                 tag:'select',
43570                 tabIndex: -1,
43571                 cls:'x-font-select',
43572                 html: this.createFontOptions()
43573             });
43574             
43575             editor.fontSelect.on('change', function(){
43576                 var font = editor.fontSelect.dom.value;
43577                 editor.relayCmd('fontname', font);
43578                 editor.deferFocus();
43579             }, editor);
43580             
43581             tb.add(
43582                 editor.fontSelect.dom,
43583                 '-'
43584             );
43585             */
43586             
43587         };
43588         if(!this.disable.formats){
43589             this.formatCombo = new Roo.form.ComboBox({
43590                 store: new Roo.data.SimpleStore({
43591                     id : 'tag',
43592                     fields: ['tag'],
43593                     data : this.formats // from states.js
43594                 }),
43595                 blockFocus : true,
43596                 name : '',
43597                 //autoCreate : {tag: "div",  size: "20"},
43598                 displayField:'tag',
43599                 typeAhead: false,
43600                 mode: 'local',
43601                 editable : false,
43602                 triggerAction: 'all',
43603                 emptyText:'Add tag',
43604                 selectOnFocus:true,
43605                 width:135,
43606                 listeners : {
43607                     'select': function(c, r, i) {
43608                         editorcore.insertTag(r.get('tag'));
43609                         editor.focus();
43610                     }
43611                 }
43612
43613             });
43614             tb.addField(this.formatCombo);
43615             
43616         }
43617         
43618         if(!this.disable.format){
43619             tb.add(
43620                 btn('bold'),
43621                 btn('italic'),
43622                 btn('underline')
43623             );
43624         };
43625         if(!this.disable.fontSize){
43626             tb.add(
43627                 '-',
43628                 
43629                 
43630                 btn('increasefontsize', false, editorcore.adjustFont),
43631                 btn('decreasefontsize', false, editorcore.adjustFont)
43632             );
43633         };
43634         
43635         
43636         if(!this.disable.colors){
43637             tb.add(
43638                 '-', {
43639                     id:editorcore.frameId +'-forecolor',
43640                     cls:'x-btn-icon x-edit-forecolor',
43641                     clickEvent:'mousedown',
43642                     tooltip: this.buttonTips['forecolor'] || undefined,
43643                     tabIndex:-1,
43644                     menu : new Roo.menu.ColorMenu({
43645                         allowReselect: true,
43646                         focus: Roo.emptyFn,
43647                         value:'000000',
43648                         plain:true,
43649                         selectHandler: function(cp, color){
43650                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43651                             editor.deferFocus();
43652                         },
43653                         scope: editorcore,
43654                         clickEvent:'mousedown'
43655                     })
43656                 }, {
43657                     id:editorcore.frameId +'backcolor',
43658                     cls:'x-btn-icon x-edit-backcolor',
43659                     clickEvent:'mousedown',
43660                     tooltip: this.buttonTips['backcolor'] || undefined,
43661                     tabIndex:-1,
43662                     menu : new Roo.menu.ColorMenu({
43663                         focus: Roo.emptyFn,
43664                         value:'FFFFFF',
43665                         plain:true,
43666                         allowReselect: true,
43667                         selectHandler: function(cp, color){
43668                             if(Roo.isGecko){
43669                                 editorcore.execCmd('useCSS', false);
43670                                 editorcore.execCmd('hilitecolor', color);
43671                                 editorcore.execCmd('useCSS', true);
43672                                 editor.deferFocus();
43673                             }else{
43674                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43675                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43676                                 editor.deferFocus();
43677                             }
43678                         },
43679                         scope:editorcore,
43680                         clickEvent:'mousedown'
43681                     })
43682                 }
43683             );
43684         };
43685         // now add all the items...
43686         
43687
43688         if(!this.disable.alignments){
43689             tb.add(
43690                 '-',
43691                 btn('justifyleft'),
43692                 btn('justifycenter'),
43693                 btn('justifyright')
43694             );
43695         };
43696
43697         //if(!Roo.isSafari){
43698             if(!this.disable.links){
43699                 tb.add(
43700                     '-',
43701                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43702                 );
43703             };
43704
43705             if(!this.disable.lists){
43706                 tb.add(
43707                     '-',
43708                     btn('insertorderedlist'),
43709                     btn('insertunorderedlist')
43710                 );
43711             }
43712             if(!this.disable.sourceEdit){
43713                 tb.add(
43714                     '-',
43715                     btn('sourceedit', true, function(btn){
43716                         this.toggleSourceEdit(btn.pressed);
43717                     })
43718                 );
43719             }
43720         //}
43721         
43722         var smenu = { };
43723         // special menu.. - needs to be tidied up..
43724         if (!this.disable.special) {
43725             smenu = {
43726                 text: "&#169;",
43727                 cls: 'x-edit-none',
43728                 
43729                 menu : {
43730                     items : []
43731                 }
43732             };
43733             for (var i =0; i < this.specialChars.length; i++) {
43734                 smenu.menu.items.push({
43735                     
43736                     html: this.specialChars[i],
43737                     handler: function(a,b) {
43738                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43739                         //editor.insertAtCursor(a.html);
43740                         
43741                     },
43742                     tabIndex:-1
43743                 });
43744             }
43745             
43746             
43747             tb.add(smenu);
43748             
43749             
43750         }
43751         
43752         var cmenu = { };
43753         if (!this.disable.cleanStyles) {
43754             cmenu = {
43755                 cls: 'x-btn-icon x-btn-clear',
43756                 
43757                 menu : {
43758                     items : []
43759                 }
43760             };
43761             for (var i =0; i < this.cleanStyles.length; i++) {
43762                 cmenu.menu.items.push({
43763                     actiontype : this.cleanStyles[i],
43764                     html: 'Remove ' + this.cleanStyles[i],
43765                     handler: function(a,b) {
43766 //                        Roo.log(a);
43767 //                        Roo.log(b);
43768                         var c = Roo.get(editorcore.doc.body);
43769                         c.select('[style]').each(function(s) {
43770                             s.dom.style.removeProperty(a.actiontype);
43771                         });
43772                         editorcore.syncValue();
43773                     },
43774                     tabIndex:-1
43775                 });
43776             }
43777              cmenu.menu.items.push({
43778                 actiontype : 'tablewidths',
43779                 html: 'Remove Table Widths',
43780                 handler: function(a,b) {
43781                     editorcore.cleanTableWidths();
43782                     editorcore.syncValue();
43783                 },
43784                 tabIndex:-1
43785             });
43786             cmenu.menu.items.push({
43787                 actiontype : 'word',
43788                 html: 'Remove MS Word Formating',
43789                 handler: function(a,b) {
43790                     editorcore.cleanWord();
43791                     editorcore.syncValue();
43792                 },
43793                 tabIndex:-1
43794             });
43795             
43796             cmenu.menu.items.push({
43797                 actiontype : 'all',
43798                 html: 'Remove All Styles',
43799                 handler: function(a,b) {
43800                     
43801                     var c = Roo.get(editorcore.doc.body);
43802                     c.select('[style]').each(function(s) {
43803                         s.dom.removeAttribute('style');
43804                     });
43805                     editorcore.syncValue();
43806                 },
43807                 tabIndex:-1
43808             });
43809              cmenu.menu.items.push({
43810                 actiontype : 'tidy',
43811                 html: 'Tidy HTML Source',
43812                 handler: function(a,b) {
43813                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43814                     editorcore.syncValue();
43815                 },
43816                 tabIndex:-1
43817             });
43818             
43819             
43820             tb.add(cmenu);
43821         }
43822          
43823         if (!this.disable.specialElements) {
43824             var semenu = {
43825                 text: "Other;",
43826                 cls: 'x-edit-none',
43827                 menu : {
43828                     items : []
43829                 }
43830             };
43831             for (var i =0; i < this.specialElements.length; i++) {
43832                 semenu.menu.items.push(
43833                     Roo.apply({ 
43834                         handler: function(a,b) {
43835                             editor.insertAtCursor(this.ihtml);
43836                         }
43837                     }, this.specialElements[i])
43838                 );
43839                     
43840             }
43841             
43842             tb.add(semenu);
43843             
43844             
43845         }
43846          
43847         
43848         if (this.btns) {
43849             for(var i =0; i< this.btns.length;i++) {
43850                 var b = Roo.factory(this.btns[i],Roo.form);
43851                 b.cls =  'x-edit-none';
43852                 
43853                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43854                     b.cls += ' x-init-enable';
43855                 }
43856                 
43857                 b.scope = editorcore;
43858                 tb.add(b);
43859             }
43860         
43861         }
43862         
43863         
43864         
43865         // disable everything...
43866         
43867         this.tb.items.each(function(item){
43868             
43869            if(
43870                 item.id != editorcore.frameId+ '-sourceedit' && 
43871                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43872             ){
43873                 
43874                 item.disable();
43875             }
43876         });
43877         this.rendered = true;
43878         
43879         // the all the btns;
43880         editor.on('editorevent', this.updateToolbar, this);
43881         // other toolbars need to implement this..
43882         //editor.on('editmodechange', this.updateToolbar, this);
43883     },
43884     
43885     
43886     relayBtnCmd : function(btn) {
43887         this.editorcore.relayCmd(btn.cmd);
43888     },
43889     // private used internally
43890     createLink : function(){
43891         Roo.log("create link?");
43892         var url = prompt(this.createLinkText, this.defaultLinkValue);
43893         if(url && url != 'http:/'+'/'){
43894             this.editorcore.relayCmd('createlink', url);
43895         }
43896     },
43897
43898     
43899     /**
43900      * Protected method that will not generally be called directly. It triggers
43901      * a toolbar update by reading the markup state of the current selection in the editor.
43902      */
43903     updateToolbar: function(){
43904
43905         if(!this.editorcore.activated){
43906             this.editor.onFirstFocus();
43907             return;
43908         }
43909
43910         var btns = this.tb.items.map, 
43911             doc = this.editorcore.doc,
43912             frameId = this.editorcore.frameId;
43913
43914         if(!this.disable.font && !Roo.isSafari){
43915             /*
43916             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43917             if(name != this.fontSelect.dom.value){
43918                 this.fontSelect.dom.value = name;
43919             }
43920             */
43921         }
43922         if(!this.disable.format){
43923             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43924             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43925             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43926         }
43927         if(!this.disable.alignments){
43928             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43929             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43930             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43931         }
43932         if(!Roo.isSafari && !this.disable.lists){
43933             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43934             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43935         }
43936         
43937         var ans = this.editorcore.getAllAncestors();
43938         if (this.formatCombo) {
43939             
43940             
43941             var store = this.formatCombo.store;
43942             this.formatCombo.setValue("");
43943             for (var i =0; i < ans.length;i++) {
43944                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43945                     // select it..
43946                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43947                     break;
43948                 }
43949             }
43950         }
43951         
43952         
43953         
43954         // hides menus... - so this cant be on a menu...
43955         Roo.menu.MenuMgr.hideAll();
43956
43957         //this.editorsyncValue();
43958     },
43959    
43960     
43961     createFontOptions : function(){
43962         var buf = [], fs = this.fontFamilies, ff, lc;
43963         
43964         
43965         
43966         for(var i = 0, len = fs.length; i< len; i++){
43967             ff = fs[i];
43968             lc = ff.toLowerCase();
43969             buf.push(
43970                 '<option value="',lc,'" style="font-family:',ff,';"',
43971                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43972                     ff,
43973                 '</option>'
43974             );
43975         }
43976         return buf.join('');
43977     },
43978     
43979     toggleSourceEdit : function(sourceEditMode){
43980         
43981         Roo.log("toolbar toogle");
43982         if(sourceEditMode === undefined){
43983             sourceEditMode = !this.sourceEditMode;
43984         }
43985         this.sourceEditMode = sourceEditMode === true;
43986         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43987         // just toggle the button?
43988         if(btn.pressed !== this.sourceEditMode){
43989             btn.toggle(this.sourceEditMode);
43990             return;
43991         }
43992         
43993         if(sourceEditMode){
43994             Roo.log("disabling buttons");
43995             this.tb.items.each(function(item){
43996                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43997                     item.disable();
43998                 }
43999             });
44000           
44001         }else{
44002             Roo.log("enabling buttons");
44003             if(this.editorcore.initialized){
44004                 this.tb.items.each(function(item){
44005                     item.enable();
44006                 });
44007             }
44008             
44009         }
44010         Roo.log("calling toggole on editor");
44011         // tell the editor that it's been pressed..
44012         this.editor.toggleSourceEdit(sourceEditMode);
44013        
44014     },
44015      /**
44016      * Object collection of toolbar tooltips for the buttons in the editor. The key
44017      * is the command id associated with that button and the value is a valid QuickTips object.
44018      * For example:
44019 <pre><code>
44020 {
44021     bold : {
44022         title: 'Bold (Ctrl+B)',
44023         text: 'Make the selected text bold.',
44024         cls: 'x-html-editor-tip'
44025     },
44026     italic : {
44027         title: 'Italic (Ctrl+I)',
44028         text: 'Make the selected text italic.',
44029         cls: 'x-html-editor-tip'
44030     },
44031     ...
44032 </code></pre>
44033     * @type Object
44034      */
44035     buttonTips : {
44036         bold : {
44037             title: 'Bold (Ctrl+B)',
44038             text: 'Make the selected text bold.',
44039             cls: 'x-html-editor-tip'
44040         },
44041         italic : {
44042             title: 'Italic (Ctrl+I)',
44043             text: 'Make the selected text italic.',
44044             cls: 'x-html-editor-tip'
44045         },
44046         underline : {
44047             title: 'Underline (Ctrl+U)',
44048             text: 'Underline the selected text.',
44049             cls: 'x-html-editor-tip'
44050         },
44051         increasefontsize : {
44052             title: 'Grow Text',
44053             text: 'Increase the font size.',
44054             cls: 'x-html-editor-tip'
44055         },
44056         decreasefontsize : {
44057             title: 'Shrink Text',
44058             text: 'Decrease the font size.',
44059             cls: 'x-html-editor-tip'
44060         },
44061         backcolor : {
44062             title: 'Text Highlight Color',
44063             text: 'Change the background color of the selected text.',
44064             cls: 'x-html-editor-tip'
44065         },
44066         forecolor : {
44067             title: 'Font Color',
44068             text: 'Change the color of the selected text.',
44069             cls: 'x-html-editor-tip'
44070         },
44071         justifyleft : {
44072             title: 'Align Text Left',
44073             text: 'Align text to the left.',
44074             cls: 'x-html-editor-tip'
44075         },
44076         justifycenter : {
44077             title: 'Center Text',
44078             text: 'Center text in the editor.',
44079             cls: 'x-html-editor-tip'
44080         },
44081         justifyright : {
44082             title: 'Align Text Right',
44083             text: 'Align text to the right.',
44084             cls: 'x-html-editor-tip'
44085         },
44086         insertunorderedlist : {
44087             title: 'Bullet List',
44088             text: 'Start a bulleted list.',
44089             cls: 'x-html-editor-tip'
44090         },
44091         insertorderedlist : {
44092             title: 'Numbered List',
44093             text: 'Start a numbered list.',
44094             cls: 'x-html-editor-tip'
44095         },
44096         createlink : {
44097             title: 'Hyperlink',
44098             text: 'Make the selected text a hyperlink.',
44099             cls: 'x-html-editor-tip'
44100         },
44101         sourceedit : {
44102             title: 'Source Edit',
44103             text: 'Switch to source editing mode.',
44104             cls: 'x-html-editor-tip'
44105         }
44106     },
44107     // private
44108     onDestroy : function(){
44109         if(this.rendered){
44110             
44111             this.tb.items.each(function(item){
44112                 if(item.menu){
44113                     item.menu.removeAll();
44114                     if(item.menu.el){
44115                         item.menu.el.destroy();
44116                     }
44117                 }
44118                 item.destroy();
44119             });
44120              
44121         }
44122     },
44123     onFirstFocus: function() {
44124         this.tb.items.each(function(item){
44125            item.enable();
44126         });
44127     }
44128 });
44129
44130
44131
44132
44133 // <script type="text/javascript">
44134 /*
44135  * Based on
44136  * Ext JS Library 1.1.1
44137  * Copyright(c) 2006-2007, Ext JS, LLC.
44138  *  
44139  
44140  */
44141
44142  
44143 /**
44144  * @class Roo.form.HtmlEditor.ToolbarContext
44145  * Context Toolbar
44146  * 
44147  * Usage:
44148  *
44149  new Roo.form.HtmlEditor({
44150     ....
44151     toolbars : [
44152         { xtype: 'ToolbarStandard', styles : {} }
44153         { xtype: 'ToolbarContext', disable : {} }
44154     ]
44155 })
44156
44157      
44158  * 
44159  * @config : {Object} disable List of elements to disable.. (not done yet.)
44160  * @config : {Object} styles  Map of styles available.
44161  * 
44162  */
44163
44164 Roo.form.HtmlEditor.ToolbarContext = function(config)
44165 {
44166     
44167     Roo.apply(this, config);
44168     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44169     // dont call parent... till later.
44170     this.styles = this.styles || {};
44171 }
44172
44173  
44174
44175 Roo.form.HtmlEditor.ToolbarContext.types = {
44176     'IMG' : {
44177         width : {
44178             title: "Width",
44179             width: 40
44180         },
44181         height:  {
44182             title: "Height",
44183             width: 40
44184         },
44185         align: {
44186             title: "Align",
44187             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44188             width : 80
44189             
44190         },
44191         border: {
44192             title: "Border",
44193             width: 40
44194         },
44195         alt: {
44196             title: "Alt",
44197             width: 120
44198         },
44199         src : {
44200             title: "Src",
44201             width: 220
44202         }
44203         
44204     },
44205     'A' : {
44206         name : {
44207             title: "Name",
44208             width: 50
44209         },
44210         target:  {
44211             title: "Target",
44212             width: 120
44213         },
44214         href:  {
44215             title: "Href",
44216             width: 220
44217         } // border?
44218         
44219     },
44220     'TABLE' : {
44221         rows : {
44222             title: "Rows",
44223             width: 20
44224         },
44225         cols : {
44226             title: "Cols",
44227             width: 20
44228         },
44229         width : {
44230             title: "Width",
44231             width: 40
44232         },
44233         height : {
44234             title: "Height",
44235             width: 40
44236         },
44237         border : {
44238             title: "Border",
44239             width: 20
44240         }
44241     },
44242     'TD' : {
44243         width : {
44244             title: "Width",
44245             width: 40
44246         },
44247         height : {
44248             title: "Height",
44249             width: 40
44250         },   
44251         align: {
44252             title: "Align",
44253             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44254             width: 80
44255         },
44256         valign: {
44257             title: "Valign",
44258             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44259             width: 80
44260         },
44261         colspan: {
44262             title: "Colspan",
44263             width: 20
44264             
44265         },
44266          'font-family'  : {
44267             title : "Font",
44268             style : 'fontFamily',
44269             displayField: 'display',
44270             optname : 'font-family',
44271             width: 140
44272         }
44273     },
44274     'INPUT' : {
44275         name : {
44276             title: "name",
44277             width: 120
44278         },
44279         value : {
44280             title: "Value",
44281             width: 120
44282         },
44283         width : {
44284             title: "Width",
44285             width: 40
44286         }
44287     },
44288     'LABEL' : {
44289         'for' : {
44290             title: "For",
44291             width: 120
44292         }
44293     },
44294     'TEXTAREA' : {
44295           name : {
44296             title: "name",
44297             width: 120
44298         },
44299         rows : {
44300             title: "Rows",
44301             width: 20
44302         },
44303         cols : {
44304             title: "Cols",
44305             width: 20
44306         }
44307     },
44308     'SELECT' : {
44309         name : {
44310             title: "name",
44311             width: 120
44312         },
44313         selectoptions : {
44314             title: "Options",
44315             width: 200
44316         }
44317     },
44318     
44319     // should we really allow this??
44320     // should this just be 
44321     'BODY' : {
44322         title : {
44323             title: "Title",
44324             width: 200,
44325             disabled : true
44326         }
44327     },
44328     'SPAN' : {
44329         'font-family'  : {
44330             title : "Font",
44331             style : 'fontFamily',
44332             displayField: 'display',
44333             optname : 'font-family',
44334             width: 140
44335         }
44336     },
44337     'DIV' : {
44338         'font-family'  : {
44339             title : "Font",
44340             style : 'fontFamily',
44341             displayField: 'display',
44342             optname : 'font-family',
44343             width: 140
44344         }
44345     },
44346      'P' : {
44347         'font-family'  : {
44348             title : "Font",
44349             style : 'fontFamily',
44350             displayField: 'display',
44351             optname : 'font-family',
44352             width: 140
44353         }
44354     },
44355     
44356     '*' : {
44357         // empty..
44358     }
44359
44360 };
44361
44362 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44363 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44364
44365 Roo.form.HtmlEditor.ToolbarContext.options = {
44366         'font-family'  : [ 
44367                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44368                 [ 'Courier New', 'Courier New'],
44369                 [ 'Tahoma', 'Tahoma'],
44370                 [ 'Times New Roman,serif', 'Times'],
44371                 [ 'Verdana','Verdana' ]
44372         ]
44373 };
44374
44375 // fixme - these need to be configurable..
44376  
44377
44378 Roo.form.HtmlEditor.ToolbarContext.types
44379
44380
44381 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44382     
44383     tb: false,
44384     
44385     rendered: false,
44386     
44387     editor : false,
44388     editorcore : false,
44389     /**
44390      * @cfg {Object} disable  List of toolbar elements to disable
44391          
44392      */
44393     disable : false,
44394     /**
44395      * @cfg {Object} styles List of styles 
44396      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44397      *
44398      * These must be defined in the page, so they get rendered correctly..
44399      * .headline { }
44400      * TD.underline { }
44401      * 
44402      */
44403     styles : false,
44404     
44405     options: false,
44406     
44407     toolbars : false,
44408     
44409     init : function(editor)
44410     {
44411         this.editor = editor;
44412         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44413         var editorcore = this.editorcore;
44414         
44415         var fid = editorcore.frameId;
44416         var etb = this;
44417         function btn(id, toggle, handler){
44418             var xid = fid + '-'+ id ;
44419             return {
44420                 id : xid,
44421                 cmd : id,
44422                 cls : 'x-btn-icon x-edit-'+id,
44423                 enableToggle:toggle !== false,
44424                 scope: editorcore, // was editor...
44425                 handler:handler||editorcore.relayBtnCmd,
44426                 clickEvent:'mousedown',
44427                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44428                 tabIndex:-1
44429             };
44430         }
44431         // create a new element.
44432         var wdiv = editor.wrap.createChild({
44433                 tag: 'div'
44434             }, editor.wrap.dom.firstChild.nextSibling, true);
44435         
44436         // can we do this more than once??
44437         
44438          // stop form submits
44439       
44440  
44441         // disable everything...
44442         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44443         this.toolbars = {};
44444            
44445         for (var i in  ty) {
44446           
44447             this.toolbars[i] = this.buildToolbar(ty[i],i);
44448         }
44449         this.tb = this.toolbars.BODY;
44450         this.tb.el.show();
44451         this.buildFooter();
44452         this.footer.show();
44453         editor.on('hide', function( ) { this.footer.hide() }, this);
44454         editor.on('show', function( ) { this.footer.show() }, this);
44455         
44456          
44457         this.rendered = true;
44458         
44459         // the all the btns;
44460         editor.on('editorevent', this.updateToolbar, this);
44461         // other toolbars need to implement this..
44462         //editor.on('editmodechange', this.updateToolbar, this);
44463     },
44464     
44465     
44466     
44467     /**
44468      * Protected method that will not generally be called directly. It triggers
44469      * a toolbar update by reading the markup state of the current selection in the editor.
44470      *
44471      * Note you can force an update by calling on('editorevent', scope, false)
44472      */
44473     updateToolbar: function(editor,ev,sel){
44474
44475         //Roo.log(ev);
44476         // capture mouse up - this is handy for selecting images..
44477         // perhaps should go somewhere else...
44478         if(!this.editorcore.activated){
44479              this.editor.onFirstFocus();
44480             return;
44481         }
44482         
44483         
44484         
44485         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44486         // selectNode - might want to handle IE?
44487         if (ev &&
44488             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44489             ev.target && ev.target.tagName == 'IMG') {
44490             // they have click on an image...
44491             // let's see if we can change the selection...
44492             sel = ev.target;
44493          
44494               var nodeRange = sel.ownerDocument.createRange();
44495             try {
44496                 nodeRange.selectNode(sel);
44497             } catch (e) {
44498                 nodeRange.selectNodeContents(sel);
44499             }
44500             //nodeRange.collapse(true);
44501             var s = this.editorcore.win.getSelection();
44502             s.removeAllRanges();
44503             s.addRange(nodeRange);
44504         }  
44505         
44506       
44507         var updateFooter = sel ? false : true;
44508         
44509         
44510         var ans = this.editorcore.getAllAncestors();
44511         
44512         // pick
44513         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44514         
44515         if (!sel) { 
44516             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44517             sel = sel ? sel : this.editorcore.doc.body;
44518             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44519             
44520         }
44521         // pick a menu that exists..
44522         var tn = sel.tagName.toUpperCase();
44523         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44524         
44525         tn = sel.tagName.toUpperCase();
44526         
44527         var lastSel = this.tb.selectedNode
44528         
44529         this.tb.selectedNode = sel;
44530         
44531         // if current menu does not match..
44532         
44533         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44534                 
44535             this.tb.el.hide();
44536             ///console.log("show: " + tn);
44537             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44538             this.tb.el.show();
44539             // update name
44540             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44541             
44542             
44543             // update attributes
44544             if (this.tb.fields) {
44545                 this.tb.fields.each(function(e) {
44546                     if (e.stylename) {
44547                         e.setValue(sel.style[e.stylename]);
44548                         return;
44549                     } 
44550                    e.setValue(sel.getAttribute(e.attrname));
44551                 });
44552             }
44553             
44554             var hasStyles = false;
44555             for(var i in this.styles) {
44556                 hasStyles = true;
44557                 break;
44558             }
44559             
44560             // update styles
44561             if (hasStyles) { 
44562                 var st = this.tb.fields.item(0);
44563                 
44564                 st.store.removeAll();
44565                
44566                 
44567                 var cn = sel.className.split(/\s+/);
44568                 
44569                 var avs = [];
44570                 if (this.styles['*']) {
44571                     
44572                     Roo.each(this.styles['*'], function(v) {
44573                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44574                     });
44575                 }
44576                 if (this.styles[tn]) { 
44577                     Roo.each(this.styles[tn], function(v) {
44578                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44579                     });
44580                 }
44581                 
44582                 st.store.loadData(avs);
44583                 st.collapse();
44584                 st.setValue(cn);
44585             }
44586             // flag our selected Node.
44587             this.tb.selectedNode = sel;
44588            
44589            
44590             Roo.menu.MenuMgr.hideAll();
44591
44592         }
44593         
44594         if (!updateFooter) {
44595             //this.footDisp.dom.innerHTML = ''; 
44596             return;
44597         }
44598         // update the footer
44599         //
44600         var html = '';
44601         
44602         this.footerEls = ans.reverse();
44603         Roo.each(this.footerEls, function(a,i) {
44604             if (!a) { return; }
44605             html += html.length ? ' &gt; '  :  '';
44606             
44607             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44608             
44609         });
44610        
44611         // 
44612         var sz = this.footDisp.up('td').getSize();
44613         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44614         this.footDisp.dom.style.marginLeft = '5px';
44615         
44616         this.footDisp.dom.style.overflow = 'hidden';
44617         
44618         this.footDisp.dom.innerHTML = html;
44619             
44620         //this.editorsyncValue();
44621     },
44622      
44623     
44624    
44625        
44626     // private
44627     onDestroy : function(){
44628         if(this.rendered){
44629             
44630             this.tb.items.each(function(item){
44631                 if(item.menu){
44632                     item.menu.removeAll();
44633                     if(item.menu.el){
44634                         item.menu.el.destroy();
44635                     }
44636                 }
44637                 item.destroy();
44638             });
44639              
44640         }
44641     },
44642     onFirstFocus: function() {
44643         // need to do this for all the toolbars..
44644         this.tb.items.each(function(item){
44645            item.enable();
44646         });
44647     },
44648     buildToolbar: function(tlist, nm)
44649     {
44650         var editor = this.editor;
44651         var editorcore = this.editorcore;
44652          // create a new element.
44653         var wdiv = editor.wrap.createChild({
44654                 tag: 'div'
44655             }, editor.wrap.dom.firstChild.nextSibling, true);
44656         
44657        
44658         var tb = new Roo.Toolbar(wdiv);
44659         // add the name..
44660         
44661         tb.add(nm+ ":&nbsp;");
44662         
44663         var styles = [];
44664         for(var i in this.styles) {
44665             styles.push(i);
44666         }
44667         
44668         // styles...
44669         if (styles && styles.length) {
44670             
44671             // this needs a multi-select checkbox...
44672             tb.addField( new Roo.form.ComboBox({
44673                 store: new Roo.data.SimpleStore({
44674                     id : 'val',
44675                     fields: ['val', 'selected'],
44676                     data : [] 
44677                 }),
44678                 name : '-roo-edit-className',
44679                 attrname : 'className',
44680                 displayField: 'val',
44681                 typeAhead: false,
44682                 mode: 'local',
44683                 editable : false,
44684                 triggerAction: 'all',
44685                 emptyText:'Select Style',
44686                 selectOnFocus:true,
44687                 width: 130,
44688                 listeners : {
44689                     'select': function(c, r, i) {
44690                         // initial support only for on class per el..
44691                         tb.selectedNode.className =  r ? r.get('val') : '';
44692                         editorcore.syncValue();
44693                     }
44694                 }
44695     
44696             }));
44697         }
44698         
44699         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44700         var tbops = tbc.options;
44701         
44702         for (var i in tlist) {
44703             
44704             var item = tlist[i];
44705             tb.add(item.title + ":&nbsp;");
44706             
44707             
44708             //optname == used so you can configure the options available..
44709             var opts = item.opts ? item.opts : false;
44710             if (item.optname) {
44711                 opts = tbops[item.optname];
44712            
44713             }
44714             
44715             if (opts) {
44716                 // opts == pulldown..
44717                 tb.addField( new Roo.form.ComboBox({
44718                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44719                         id : 'val',
44720                         fields: ['val', 'display'],
44721                         data : opts  
44722                     }),
44723                     name : '-roo-edit-' + i,
44724                     attrname : i,
44725                     stylename : item.style ? item.style : false,
44726                     displayField: item.displayField ? item.displayField : 'val',
44727                     valueField :  'val',
44728                     typeAhead: false,
44729                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44730                     editable : false,
44731                     triggerAction: 'all',
44732                     emptyText:'Select',
44733                     selectOnFocus:true,
44734                     width: item.width ? item.width  : 130,
44735                     listeners : {
44736                         'select': function(c, r, i) {
44737                             if (c.stylename) {
44738                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44739                                 return;
44740                             }
44741                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44742                         }
44743                     }
44744
44745                 }));
44746                 continue;
44747                     
44748                  
44749                 
44750                 tb.addField( new Roo.form.TextField({
44751                     name: i,
44752                     width: 100,
44753                     //allowBlank:false,
44754                     value: ''
44755                 }));
44756                 continue;
44757             }
44758             tb.addField( new Roo.form.TextField({
44759                 name: '-roo-edit-' + i,
44760                 attrname : i,
44761                 
44762                 width: item.width,
44763                 //allowBlank:true,
44764                 value: '',
44765                 listeners: {
44766                     'change' : function(f, nv, ov) {
44767                         tb.selectedNode.setAttribute(f.attrname, nv);
44768                     }
44769                 }
44770             }));
44771              
44772         }
44773         
44774         var _this = this;
44775         
44776         if(nm == 'BODY'){
44777             tb.addSeparator();
44778         
44779             tb.addButton( {
44780                 text: 'Stylesheets',
44781
44782                 listeners : {
44783                     click : function ()
44784                     {
44785                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44786                     }
44787                 }
44788             });
44789         }
44790         
44791         tb.addFill();
44792         tb.addButton( {
44793             text: 'Remove Tag',
44794     
44795             listeners : {
44796                 click : function ()
44797                 {
44798                     // remove
44799                     // undo does not work.
44800                      
44801                     var sn = tb.selectedNode;
44802                     
44803                     var pn = sn.parentNode;
44804                     
44805                     var stn =  sn.childNodes[0];
44806                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44807                     while (sn.childNodes.length) {
44808                         var node = sn.childNodes[0];
44809                         sn.removeChild(node);
44810                         //Roo.log(node);
44811                         pn.insertBefore(node, sn);
44812                         
44813                     }
44814                     pn.removeChild(sn);
44815                     var range = editorcore.createRange();
44816         
44817                     range.setStart(stn,0);
44818                     range.setEnd(en,0); //????
44819                     //range.selectNode(sel);
44820                     
44821                     
44822                     var selection = editorcore.getSelection();
44823                     selection.removeAllRanges();
44824                     selection.addRange(range);
44825                     
44826                     
44827                     
44828                     //_this.updateToolbar(null, null, pn);
44829                     _this.updateToolbar(null, null, null);
44830                     _this.footDisp.dom.innerHTML = ''; 
44831                 }
44832             }
44833             
44834                     
44835                 
44836             
44837         });
44838         
44839         
44840         tb.el.on('click', function(e){
44841             e.preventDefault(); // what does this do?
44842         });
44843         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44844         tb.el.hide();
44845         tb.name = nm;
44846         // dont need to disable them... as they will get hidden
44847         return tb;
44848          
44849         
44850     },
44851     buildFooter : function()
44852     {
44853         
44854         var fel = this.editor.wrap.createChild();
44855         this.footer = new Roo.Toolbar(fel);
44856         // toolbar has scrolly on left / right?
44857         var footDisp= new Roo.Toolbar.Fill();
44858         var _t = this;
44859         this.footer.add(
44860             {
44861                 text : '&lt;',
44862                 xtype: 'Button',
44863                 handler : function() {
44864                     _t.footDisp.scrollTo('left',0,true)
44865                 }
44866             }
44867         );
44868         this.footer.add( footDisp );
44869         this.footer.add( 
44870             {
44871                 text : '&gt;',
44872                 xtype: 'Button',
44873                 handler : function() {
44874                     // no animation..
44875                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44876                 }
44877             }
44878         );
44879         var fel = Roo.get(footDisp.el);
44880         fel.addClass('x-editor-context');
44881         this.footDispWrap = fel; 
44882         this.footDispWrap.overflow  = 'hidden';
44883         
44884         this.footDisp = fel.createChild();
44885         this.footDispWrap.on('click', this.onContextClick, this)
44886         
44887         
44888     },
44889     onContextClick : function (ev,dom)
44890     {
44891         ev.preventDefault();
44892         var  cn = dom.className;
44893         //Roo.log(cn);
44894         if (!cn.match(/x-ed-loc-/)) {
44895             return;
44896         }
44897         var n = cn.split('-').pop();
44898         var ans = this.footerEls;
44899         var sel = ans[n];
44900         
44901          // pick
44902         var range = this.editorcore.createRange();
44903         
44904         range.selectNodeContents(sel);
44905         //range.selectNode(sel);
44906         
44907         
44908         var selection = this.editorcore.getSelection();
44909         selection.removeAllRanges();
44910         selection.addRange(range);
44911         
44912         
44913         
44914         this.updateToolbar(null, null, sel);
44915         
44916         
44917     }
44918     
44919     
44920     
44921     
44922     
44923 });
44924
44925
44926
44927
44928
44929 /*
44930  * Based on:
44931  * Ext JS Library 1.1.1
44932  * Copyright(c) 2006-2007, Ext JS, LLC.
44933  *
44934  * Originally Released Under LGPL - original licence link has changed is not relivant.
44935  *
44936  * Fork - LGPL
44937  * <script type="text/javascript">
44938  */
44939  
44940 /**
44941  * @class Roo.form.BasicForm
44942  * @extends Roo.util.Observable
44943  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44944  * @constructor
44945  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44946  * @param {Object} config Configuration options
44947  */
44948 Roo.form.BasicForm = function(el, config){
44949     this.allItems = [];
44950     this.childForms = [];
44951     Roo.apply(this, config);
44952     /*
44953      * The Roo.form.Field items in this form.
44954      * @type MixedCollection
44955      */
44956      
44957      
44958     this.items = new Roo.util.MixedCollection(false, function(o){
44959         return o.id || (o.id = Roo.id());
44960     });
44961     this.addEvents({
44962         /**
44963          * @event beforeaction
44964          * Fires before any action is performed. Return false to cancel the action.
44965          * @param {Form} this
44966          * @param {Action} action The action to be performed
44967          */
44968         beforeaction: true,
44969         /**
44970          * @event actionfailed
44971          * Fires when an action fails.
44972          * @param {Form} this
44973          * @param {Action} action The action that failed
44974          */
44975         actionfailed : true,
44976         /**
44977          * @event actioncomplete
44978          * Fires when an action is completed.
44979          * @param {Form} this
44980          * @param {Action} action The action that completed
44981          */
44982         actioncomplete : true
44983     });
44984     if(el){
44985         this.initEl(el);
44986     }
44987     Roo.form.BasicForm.superclass.constructor.call(this);
44988 };
44989
44990 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44991     /**
44992      * @cfg {String} method
44993      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44994      */
44995     /**
44996      * @cfg {DataReader} reader
44997      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44998      * This is optional as there is built-in support for processing JSON.
44999      */
45000     /**
45001      * @cfg {DataReader} errorReader
45002      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45003      * This is completely optional as there is built-in support for processing JSON.
45004      */
45005     /**
45006      * @cfg {String} url
45007      * The URL to use for form actions if one isn't supplied in the action options.
45008      */
45009     /**
45010      * @cfg {Boolean} fileUpload
45011      * Set to true if this form is a file upload.
45012      */
45013      
45014     /**
45015      * @cfg {Object} baseParams
45016      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45017      */
45018      /**
45019      
45020     /**
45021      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45022      */
45023     timeout: 30,
45024
45025     // private
45026     activeAction : null,
45027
45028     /**
45029      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45030      * or setValues() data instead of when the form was first created.
45031      */
45032     trackResetOnLoad : false,
45033     
45034     
45035     /**
45036      * childForms - used for multi-tab forms
45037      * @type {Array}
45038      */
45039     childForms : false,
45040     
45041     /**
45042      * allItems - full list of fields.
45043      * @type {Array}
45044      */
45045     allItems : false,
45046     
45047     /**
45048      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45049      * element by passing it or its id or mask the form itself by passing in true.
45050      * @type Mixed
45051      */
45052     waitMsgTarget : false,
45053
45054     // private
45055     initEl : function(el){
45056         this.el = Roo.get(el);
45057         this.id = this.el.id || Roo.id();
45058         this.el.on('submit', this.onSubmit, this);
45059         this.el.addClass('x-form');
45060     },
45061
45062     // private
45063     onSubmit : function(e){
45064         e.stopEvent();
45065     },
45066
45067     /**
45068      * Returns true if client-side validation on the form is successful.
45069      * @return Boolean
45070      */
45071     isValid : function(){
45072         var valid = true;
45073         this.items.each(function(f){
45074            if(!f.validate()){
45075                valid = false;
45076            }
45077         });
45078         return valid;
45079     },
45080
45081     /**
45082      * Returns true if any fields in this form have changed since their original load.
45083      * @return Boolean
45084      */
45085     isDirty : function(){
45086         var dirty = false;
45087         this.items.each(function(f){
45088            if(f.isDirty()){
45089                dirty = true;
45090                return false;
45091            }
45092         });
45093         return dirty;
45094     },
45095
45096     /**
45097      * Performs a predefined action (submit or load) or custom actions you define on this form.
45098      * @param {String} actionName The name of the action type
45099      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45100      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45101      * accept other config options):
45102      * <pre>
45103 Property          Type             Description
45104 ----------------  ---------------  ----------------------------------------------------------------------------------
45105 url               String           The url for the action (defaults to the form's url)
45106 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45107 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45108 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45109                                    validate the form on the client (defaults to false)
45110      * </pre>
45111      * @return {BasicForm} this
45112      */
45113     doAction : function(action, options){
45114         if(typeof action == 'string'){
45115             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45116         }
45117         if(this.fireEvent('beforeaction', this, action) !== false){
45118             this.beforeAction(action);
45119             action.run.defer(100, action);
45120         }
45121         return this;
45122     },
45123
45124     /**
45125      * Shortcut to do a submit action.
45126      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45127      * @return {BasicForm} this
45128      */
45129     submit : function(options){
45130         this.doAction('submit', options);
45131         return this;
45132     },
45133
45134     /**
45135      * Shortcut to do a load action.
45136      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45137      * @return {BasicForm} this
45138      */
45139     load : function(options){
45140         this.doAction('load', options);
45141         return this;
45142     },
45143
45144     /**
45145      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45146      * @param {Record} record The record to edit
45147      * @return {BasicForm} this
45148      */
45149     updateRecord : function(record){
45150         record.beginEdit();
45151         var fs = record.fields;
45152         fs.each(function(f){
45153             var field = this.findField(f.name);
45154             if(field){
45155                 record.set(f.name, field.getValue());
45156             }
45157         }, this);
45158         record.endEdit();
45159         return this;
45160     },
45161
45162     /**
45163      * Loads an Roo.data.Record into this form.
45164      * @param {Record} record The record to load
45165      * @return {BasicForm} this
45166      */
45167     loadRecord : function(record){
45168         this.setValues(record.data);
45169         return this;
45170     },
45171
45172     // private
45173     beforeAction : function(action){
45174         var o = action.options;
45175         
45176        
45177         if(this.waitMsgTarget === true){
45178             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45179         }else if(this.waitMsgTarget){
45180             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45181             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45182         }else {
45183             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45184         }
45185          
45186     },
45187
45188     // private
45189     afterAction : function(action, success){
45190         this.activeAction = null;
45191         var o = action.options;
45192         
45193         if(this.waitMsgTarget === true){
45194             this.el.unmask();
45195         }else if(this.waitMsgTarget){
45196             this.waitMsgTarget.unmask();
45197         }else{
45198             Roo.MessageBox.updateProgress(1);
45199             Roo.MessageBox.hide();
45200         }
45201          
45202         if(success){
45203             if(o.reset){
45204                 this.reset();
45205             }
45206             Roo.callback(o.success, o.scope, [this, action]);
45207             this.fireEvent('actioncomplete', this, action);
45208             
45209         }else{
45210             
45211             // failure condition..
45212             // we have a scenario where updates need confirming.
45213             // eg. if a locking scenario exists..
45214             // we look for { errors : { needs_confirm : true }} in the response.
45215             if (
45216                 (typeof(action.result) != 'undefined')  &&
45217                 (typeof(action.result.errors) != 'undefined')  &&
45218                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45219            ){
45220                 var _t = this;
45221                 Roo.MessageBox.confirm(
45222                     "Change requires confirmation",
45223                     action.result.errorMsg,
45224                     function(r) {
45225                         if (r != 'yes') {
45226                             return;
45227                         }
45228                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45229                     }
45230                     
45231                 );
45232                 
45233                 
45234                 
45235                 return;
45236             }
45237             
45238             Roo.callback(o.failure, o.scope, [this, action]);
45239             // show an error message if no failed handler is set..
45240             if (!this.hasListener('actionfailed')) {
45241                 Roo.MessageBox.alert("Error",
45242                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45243                         action.result.errorMsg :
45244                         "Saving Failed, please check your entries or try again"
45245                 );
45246             }
45247             
45248             this.fireEvent('actionfailed', this, action);
45249         }
45250         
45251     },
45252
45253     /**
45254      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45255      * @param {String} id The value to search for
45256      * @return Field
45257      */
45258     findField : function(id){
45259         var field = this.items.get(id);
45260         if(!field){
45261             this.items.each(function(f){
45262                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45263                     field = f;
45264                     return false;
45265                 }
45266             });
45267         }
45268         return field || null;
45269     },
45270
45271     /**
45272      * Add a secondary form to this one, 
45273      * Used to provide tabbed forms. One form is primary, with hidden values 
45274      * which mirror the elements from the other forms.
45275      * 
45276      * @param {Roo.form.Form} form to add.
45277      * 
45278      */
45279     addForm : function(form)
45280     {
45281        
45282         if (this.childForms.indexOf(form) > -1) {
45283             // already added..
45284             return;
45285         }
45286         this.childForms.push(form);
45287         var n = '';
45288         Roo.each(form.allItems, function (fe) {
45289             
45290             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45291             if (this.findField(n)) { // already added..
45292                 return;
45293             }
45294             var add = new Roo.form.Hidden({
45295                 name : n
45296             });
45297             add.render(this.el);
45298             
45299             this.add( add );
45300         }, this);
45301         
45302     },
45303     /**
45304      * Mark fields in this form invalid in bulk.
45305      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45306      * @return {BasicForm} this
45307      */
45308     markInvalid : function(errors){
45309         if(errors instanceof Array){
45310             for(var i = 0, len = errors.length; i < len; i++){
45311                 var fieldError = errors[i];
45312                 var f = this.findField(fieldError.id);
45313                 if(f){
45314                     f.markInvalid(fieldError.msg);
45315                 }
45316             }
45317         }else{
45318             var field, id;
45319             for(id in errors){
45320                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45321                     field.markInvalid(errors[id]);
45322                 }
45323             }
45324         }
45325         Roo.each(this.childForms || [], function (f) {
45326             f.markInvalid(errors);
45327         });
45328         
45329         return this;
45330     },
45331
45332     /**
45333      * Set values for fields in this form in bulk.
45334      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45335      * @return {BasicForm} this
45336      */
45337     setValues : function(values){
45338         if(values instanceof Array){ // array of objects
45339             for(var i = 0, len = values.length; i < len; i++){
45340                 var v = values[i];
45341                 var f = this.findField(v.id);
45342                 if(f){
45343                     f.setValue(v.value);
45344                     if(this.trackResetOnLoad){
45345                         f.originalValue = f.getValue();
45346                     }
45347                 }
45348             }
45349         }else{ // object hash
45350             var field, id;
45351             for(id in values){
45352                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45353                     
45354                     if (field.setFromData && 
45355                         field.valueField && 
45356                         field.displayField &&
45357                         // combos' with local stores can 
45358                         // be queried via setValue()
45359                         // to set their value..
45360                         (field.store && !field.store.isLocal)
45361                         ) {
45362                         // it's a combo
45363                         var sd = { };
45364                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45365                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45366                         field.setFromData(sd);
45367                         
45368                     } else {
45369                         field.setValue(values[id]);
45370                     }
45371                     
45372                     
45373                     if(this.trackResetOnLoad){
45374                         field.originalValue = field.getValue();
45375                     }
45376                 }
45377             }
45378         }
45379          
45380         Roo.each(this.childForms || [], function (f) {
45381             f.setValues(values);
45382         });
45383                 
45384         return this;
45385     },
45386
45387     /**
45388      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45389      * they are returned as an array.
45390      * @param {Boolean} asString
45391      * @return {Object}
45392      */
45393     getValues : function(asString){
45394         if (this.childForms) {
45395             // copy values from the child forms
45396             Roo.each(this.childForms, function (f) {
45397                 this.setValues(f.getValues());
45398             }, this);
45399         }
45400         
45401         
45402         
45403         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45404         if(asString === true){
45405             return fs;
45406         }
45407         return Roo.urlDecode(fs);
45408     },
45409     
45410     /**
45411      * Returns the fields in this form as an object with key/value pairs. 
45412      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45413      * @return {Object}
45414      */
45415     getFieldValues : function(with_hidden)
45416     {
45417         if (this.childForms) {
45418             // copy values from the child forms
45419             // should this call getFieldValues - probably not as we do not currently copy
45420             // hidden fields when we generate..
45421             Roo.each(this.childForms, function (f) {
45422                 this.setValues(f.getValues());
45423             }, this);
45424         }
45425         
45426         var ret = {};
45427         this.items.each(function(f){
45428             if (!f.getName()) {
45429                 return;
45430             }
45431             var v = f.getValue();
45432             if (f.inputType =='radio') {
45433                 if (typeof(ret[f.getName()]) == 'undefined') {
45434                     ret[f.getName()] = ''; // empty..
45435                 }
45436                 
45437                 if (!f.el.dom.checked) {
45438                     return;
45439                     
45440                 }
45441                 v = f.el.dom.value;
45442                 
45443             }
45444             
45445             // not sure if this supported any more..
45446             if ((typeof(v) == 'object') && f.getRawValue) {
45447                 v = f.getRawValue() ; // dates..
45448             }
45449             // combo boxes where name != hiddenName...
45450             if (f.name != f.getName()) {
45451                 ret[f.name] = f.getRawValue();
45452             }
45453             ret[f.getName()] = v;
45454         });
45455         
45456         return ret;
45457     },
45458
45459     /**
45460      * Clears all invalid messages in this form.
45461      * @return {BasicForm} this
45462      */
45463     clearInvalid : function(){
45464         this.items.each(function(f){
45465            f.clearInvalid();
45466         });
45467         
45468         Roo.each(this.childForms || [], function (f) {
45469             f.clearInvalid();
45470         });
45471         
45472         
45473         return this;
45474     },
45475
45476     /**
45477      * Resets this form.
45478      * @return {BasicForm} this
45479      */
45480     reset : function(){
45481         this.items.each(function(f){
45482             f.reset();
45483         });
45484         
45485         Roo.each(this.childForms || [], function (f) {
45486             f.reset();
45487         });
45488        
45489         
45490         return this;
45491     },
45492
45493     /**
45494      * Add Roo.form components to this form.
45495      * @param {Field} field1
45496      * @param {Field} field2 (optional)
45497      * @param {Field} etc (optional)
45498      * @return {BasicForm} this
45499      */
45500     add : function(){
45501         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45502         return this;
45503     },
45504
45505
45506     /**
45507      * Removes a field from the items collection (does NOT remove its markup).
45508      * @param {Field} field
45509      * @return {BasicForm} this
45510      */
45511     remove : function(field){
45512         this.items.remove(field);
45513         return this;
45514     },
45515
45516     /**
45517      * Looks at the fields in this form, checks them for an id attribute,
45518      * and calls applyTo on the existing dom element with that id.
45519      * @return {BasicForm} this
45520      */
45521     render : function(){
45522         this.items.each(function(f){
45523             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45524                 f.applyTo(f.id);
45525             }
45526         });
45527         return this;
45528     },
45529
45530     /**
45531      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45532      * @param {Object} values
45533      * @return {BasicForm} this
45534      */
45535     applyToFields : function(o){
45536         this.items.each(function(f){
45537            Roo.apply(f, o);
45538         });
45539         return this;
45540     },
45541
45542     /**
45543      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45544      * @param {Object} values
45545      * @return {BasicForm} this
45546      */
45547     applyIfToFields : function(o){
45548         this.items.each(function(f){
45549            Roo.applyIf(f, o);
45550         });
45551         return this;
45552     }
45553 });
45554
45555 // back compat
45556 Roo.BasicForm = Roo.form.BasicForm;/*
45557  * Based on:
45558  * Ext JS Library 1.1.1
45559  * Copyright(c) 2006-2007, Ext JS, LLC.
45560  *
45561  * Originally Released Under LGPL - original licence link has changed is not relivant.
45562  *
45563  * Fork - LGPL
45564  * <script type="text/javascript">
45565  */
45566
45567 /**
45568  * @class Roo.form.Form
45569  * @extends Roo.form.BasicForm
45570  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45571  * @constructor
45572  * @param {Object} config Configuration options
45573  */
45574 Roo.form.Form = function(config){
45575     var xitems =  [];
45576     if (config.items) {
45577         xitems = config.items;
45578         delete config.items;
45579     }
45580    
45581     
45582     Roo.form.Form.superclass.constructor.call(this, null, config);
45583     this.url = this.url || this.action;
45584     if(!this.root){
45585         this.root = new Roo.form.Layout(Roo.applyIf({
45586             id: Roo.id()
45587         }, config));
45588     }
45589     this.active = this.root;
45590     /**
45591      * Array of all the buttons that have been added to this form via {@link addButton}
45592      * @type Array
45593      */
45594     this.buttons = [];
45595     this.allItems = [];
45596     this.addEvents({
45597         /**
45598          * @event clientvalidation
45599          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45600          * @param {Form} this
45601          * @param {Boolean} valid true if the form has passed client-side validation
45602          */
45603         clientvalidation: true,
45604         /**
45605          * @event rendered
45606          * Fires when the form is rendered
45607          * @param {Roo.form.Form} form
45608          */
45609         rendered : true
45610     });
45611     
45612     if (this.progressUrl) {
45613             // push a hidden field onto the list of fields..
45614             this.addxtype( {
45615                     xns: Roo.form, 
45616                     xtype : 'Hidden', 
45617                     name : 'UPLOAD_IDENTIFIER' 
45618             });
45619         }
45620         
45621     
45622     Roo.each(xitems, this.addxtype, this);
45623     
45624     
45625     
45626 };
45627
45628 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45629     /**
45630      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45631      */
45632     /**
45633      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45634      */
45635     /**
45636      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45637      */
45638     buttonAlign:'center',
45639
45640     /**
45641      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45642      */
45643     minButtonWidth:75,
45644
45645     /**
45646      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45647      * This property cascades to child containers if not set.
45648      */
45649     labelAlign:'left',
45650
45651     /**
45652      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45653      * fires a looping event with that state. This is required to bind buttons to the valid
45654      * state using the config value formBind:true on the button.
45655      */
45656     monitorValid : false,
45657
45658     /**
45659      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45660      */
45661     monitorPoll : 200,
45662     
45663     /**
45664      * @cfg {String} progressUrl - Url to return progress data 
45665      */
45666     
45667     progressUrl : false,
45668   
45669     /**
45670      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45671      * fields are added and the column is closed. If no fields are passed the column remains open
45672      * until end() is called.
45673      * @param {Object} config The config to pass to the column
45674      * @param {Field} field1 (optional)
45675      * @param {Field} field2 (optional)
45676      * @param {Field} etc (optional)
45677      * @return Column The column container object
45678      */
45679     column : function(c){
45680         var col = new Roo.form.Column(c);
45681         this.start(col);
45682         if(arguments.length > 1){ // duplicate code required because of Opera
45683             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45684             this.end();
45685         }
45686         return col;
45687     },
45688
45689     /**
45690      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45691      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45692      * until end() is called.
45693      * @param {Object} config The config to pass to the fieldset
45694      * @param {Field} field1 (optional)
45695      * @param {Field} field2 (optional)
45696      * @param {Field} etc (optional)
45697      * @return FieldSet The fieldset container object
45698      */
45699     fieldset : function(c){
45700         var fs = new Roo.form.FieldSet(c);
45701         this.start(fs);
45702         if(arguments.length > 1){ // duplicate code required because of Opera
45703             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45704             this.end();
45705         }
45706         return fs;
45707     },
45708
45709     /**
45710      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45711      * fields are added and the container is closed. If no fields are passed the container remains open
45712      * until end() is called.
45713      * @param {Object} config The config to pass to the Layout
45714      * @param {Field} field1 (optional)
45715      * @param {Field} field2 (optional)
45716      * @param {Field} etc (optional)
45717      * @return Layout The container object
45718      */
45719     container : function(c){
45720         var l = new Roo.form.Layout(c);
45721         this.start(l);
45722         if(arguments.length > 1){ // duplicate code required because of Opera
45723             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45724             this.end();
45725         }
45726         return l;
45727     },
45728
45729     /**
45730      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45731      * @param {Object} container A Roo.form.Layout or subclass of Layout
45732      * @return {Form} this
45733      */
45734     start : function(c){
45735         // cascade label info
45736         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45737         this.active.stack.push(c);
45738         c.ownerCt = this.active;
45739         this.active = c;
45740         return this;
45741     },
45742
45743     /**
45744      * Closes the current open container
45745      * @return {Form} this
45746      */
45747     end : function(){
45748         if(this.active == this.root){
45749             return this;
45750         }
45751         this.active = this.active.ownerCt;
45752         return this;
45753     },
45754
45755     /**
45756      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45757      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45758      * as the label of the field.
45759      * @param {Field} field1
45760      * @param {Field} field2 (optional)
45761      * @param {Field} etc. (optional)
45762      * @return {Form} this
45763      */
45764     add : function(){
45765         this.active.stack.push.apply(this.active.stack, arguments);
45766         this.allItems.push.apply(this.allItems,arguments);
45767         var r = [];
45768         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45769             if(a[i].isFormField){
45770                 r.push(a[i]);
45771             }
45772         }
45773         if(r.length > 0){
45774             Roo.form.Form.superclass.add.apply(this, r);
45775         }
45776         return this;
45777     },
45778     
45779
45780     
45781     
45782     
45783      /**
45784      * Find any element that has been added to a form, using it's ID or name
45785      * This can include framesets, columns etc. along with regular fields..
45786      * @param {String} id - id or name to find.
45787      
45788      * @return {Element} e - or false if nothing found.
45789      */
45790     findbyId : function(id)
45791     {
45792         var ret = false;
45793         if (!id) {
45794             return ret;
45795         }
45796         Roo.each(this.allItems, function(f){
45797             if (f.id == id || f.name == id ){
45798                 ret = f;
45799                 return false;
45800             }
45801         });
45802         return ret;
45803     },
45804
45805     
45806     
45807     /**
45808      * Render this form into the passed container. This should only be called once!
45809      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45810      * @return {Form} this
45811      */
45812     render : function(ct)
45813     {
45814         
45815         
45816         
45817         ct = Roo.get(ct);
45818         var o = this.autoCreate || {
45819             tag: 'form',
45820             method : this.method || 'POST',
45821             id : this.id || Roo.id()
45822         };
45823         this.initEl(ct.createChild(o));
45824
45825         this.root.render(this.el);
45826         
45827        
45828              
45829         this.items.each(function(f){
45830             f.render('x-form-el-'+f.id);
45831         });
45832
45833         if(this.buttons.length > 0){
45834             // tables are required to maintain order and for correct IE layout
45835             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45836                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45837                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45838             }}, null, true);
45839             var tr = tb.getElementsByTagName('tr')[0];
45840             for(var i = 0, len = this.buttons.length; i < len; i++) {
45841                 var b = this.buttons[i];
45842                 var td = document.createElement('td');
45843                 td.className = 'x-form-btn-td';
45844                 b.render(tr.appendChild(td));
45845             }
45846         }
45847         if(this.monitorValid){ // initialize after render
45848             this.startMonitoring();
45849         }
45850         this.fireEvent('rendered', this);
45851         return this;
45852     },
45853
45854     /**
45855      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45856      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45857      * object or a valid Roo.DomHelper element config
45858      * @param {Function} handler The function called when the button is clicked
45859      * @param {Object} scope (optional) The scope of the handler function
45860      * @return {Roo.Button}
45861      */
45862     addButton : function(config, handler, scope){
45863         var bc = {
45864             handler: handler,
45865             scope: scope,
45866             minWidth: this.minButtonWidth,
45867             hideParent:true
45868         };
45869         if(typeof config == "string"){
45870             bc.text = config;
45871         }else{
45872             Roo.apply(bc, config);
45873         }
45874         var btn = new Roo.Button(null, bc);
45875         this.buttons.push(btn);
45876         return btn;
45877     },
45878
45879      /**
45880      * Adds a series of form elements (using the xtype property as the factory method.
45881      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45882      * @param {Object} config 
45883      */
45884     
45885     addxtype : function()
45886     {
45887         var ar = Array.prototype.slice.call(arguments, 0);
45888         var ret = false;
45889         for(var i = 0; i < ar.length; i++) {
45890             if (!ar[i]) {
45891                 continue; // skip -- if this happends something invalid got sent, we 
45892                 // should ignore it, as basically that interface element will not show up
45893                 // and that should be pretty obvious!!
45894             }
45895             
45896             if (Roo.form[ar[i].xtype]) {
45897                 ar[i].form = this;
45898                 var fe = Roo.factory(ar[i], Roo.form);
45899                 if (!ret) {
45900                     ret = fe;
45901                 }
45902                 fe.form = this;
45903                 if (fe.store) {
45904                     fe.store.form = this;
45905                 }
45906                 if (fe.isLayout) {  
45907                          
45908                     this.start(fe);
45909                     this.allItems.push(fe);
45910                     if (fe.items && fe.addxtype) {
45911                         fe.addxtype.apply(fe, fe.items);
45912                         delete fe.items;
45913                     }
45914                      this.end();
45915                     continue;
45916                 }
45917                 
45918                 
45919                  
45920                 this.add(fe);
45921               //  console.log('adding ' + ar[i].xtype);
45922             }
45923             if (ar[i].xtype == 'Button') {  
45924                 //console.log('adding button');
45925                 //console.log(ar[i]);
45926                 this.addButton(ar[i]);
45927                 this.allItems.push(fe);
45928                 continue;
45929             }
45930             
45931             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45932                 alert('end is not supported on xtype any more, use items');
45933             //    this.end();
45934             //    //console.log('adding end');
45935             }
45936             
45937         }
45938         return ret;
45939     },
45940     
45941     /**
45942      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45943      * option "monitorValid"
45944      */
45945     startMonitoring : function(){
45946         if(!this.bound){
45947             this.bound = true;
45948             Roo.TaskMgr.start({
45949                 run : this.bindHandler,
45950                 interval : this.monitorPoll || 200,
45951                 scope: this
45952             });
45953         }
45954     },
45955
45956     /**
45957      * Stops monitoring of the valid state of this form
45958      */
45959     stopMonitoring : function(){
45960         this.bound = false;
45961     },
45962
45963     // private
45964     bindHandler : function(){
45965         if(!this.bound){
45966             return false; // stops binding
45967         }
45968         var valid = true;
45969         this.items.each(function(f){
45970             if(!f.isValid(true)){
45971                 valid = false;
45972                 return false;
45973             }
45974         });
45975         for(var i = 0, len = this.buttons.length; i < len; i++){
45976             var btn = this.buttons[i];
45977             if(btn.formBind === true && btn.disabled === valid){
45978                 btn.setDisabled(!valid);
45979             }
45980         }
45981         this.fireEvent('clientvalidation', this, valid);
45982     }
45983     
45984     
45985     
45986     
45987     
45988     
45989     
45990     
45991 });
45992
45993
45994 // back compat
45995 Roo.Form = Roo.form.Form;
45996 /*
45997  * Based on:
45998  * Ext JS Library 1.1.1
45999  * Copyright(c) 2006-2007, Ext JS, LLC.
46000  *
46001  * Originally Released Under LGPL - original licence link has changed is not relivant.
46002  *
46003  * Fork - LGPL
46004  * <script type="text/javascript">
46005  */
46006
46007 // as we use this in bootstrap.
46008 Roo.namespace('Roo.form');
46009  /**
46010  * @class Roo.form.Action
46011  * Internal Class used to handle form actions
46012  * @constructor
46013  * @param {Roo.form.BasicForm} el The form element or its id
46014  * @param {Object} config Configuration options
46015  */
46016
46017  
46018  
46019 // define the action interface
46020 Roo.form.Action = function(form, options){
46021     this.form = form;
46022     this.options = options || {};
46023 };
46024 /**
46025  * Client Validation Failed
46026  * @const 
46027  */
46028 Roo.form.Action.CLIENT_INVALID = 'client';
46029 /**
46030  * Server Validation Failed
46031  * @const 
46032  */
46033 Roo.form.Action.SERVER_INVALID = 'server';
46034  /**
46035  * Connect to Server Failed
46036  * @const 
46037  */
46038 Roo.form.Action.CONNECT_FAILURE = 'connect';
46039 /**
46040  * Reading Data from Server Failed
46041  * @const 
46042  */
46043 Roo.form.Action.LOAD_FAILURE = 'load';
46044
46045 Roo.form.Action.prototype = {
46046     type : 'default',
46047     failureType : undefined,
46048     response : undefined,
46049     result : undefined,
46050
46051     // interface method
46052     run : function(options){
46053
46054     },
46055
46056     // interface method
46057     success : function(response){
46058
46059     },
46060
46061     // interface method
46062     handleResponse : function(response){
46063
46064     },
46065
46066     // default connection failure
46067     failure : function(response){
46068         
46069         this.response = response;
46070         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46071         this.form.afterAction(this, false);
46072     },
46073
46074     processResponse : function(response){
46075         this.response = response;
46076         if(!response.responseText){
46077             return true;
46078         }
46079         this.result = this.handleResponse(response);
46080         return this.result;
46081     },
46082
46083     // utility functions used internally
46084     getUrl : function(appendParams){
46085         var url = this.options.url || this.form.url || this.form.el.dom.action;
46086         if(appendParams){
46087             var p = this.getParams();
46088             if(p){
46089                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46090             }
46091         }
46092         return url;
46093     },
46094
46095     getMethod : function(){
46096         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46097     },
46098
46099     getParams : function(){
46100         var bp = this.form.baseParams;
46101         var p = this.options.params;
46102         if(p){
46103             if(typeof p == "object"){
46104                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46105             }else if(typeof p == 'string' && bp){
46106                 p += '&' + Roo.urlEncode(bp);
46107             }
46108         }else if(bp){
46109             p = Roo.urlEncode(bp);
46110         }
46111         return p;
46112     },
46113
46114     createCallback : function(){
46115         return {
46116             success: this.success,
46117             failure: this.failure,
46118             scope: this,
46119             timeout: (this.form.timeout*1000),
46120             upload: this.form.fileUpload ? this.success : undefined
46121         };
46122     }
46123 };
46124
46125 Roo.form.Action.Submit = function(form, options){
46126     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46127 };
46128
46129 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46130     type : 'submit',
46131
46132     haveProgress : false,
46133     uploadComplete : false,
46134     
46135     // uploadProgress indicator.
46136     uploadProgress : function()
46137     {
46138         if (!this.form.progressUrl) {
46139             return;
46140         }
46141         
46142         if (!this.haveProgress) {
46143             Roo.MessageBox.progress("Uploading", "Uploading");
46144         }
46145         if (this.uploadComplete) {
46146            Roo.MessageBox.hide();
46147            return;
46148         }
46149         
46150         this.haveProgress = true;
46151    
46152         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46153         
46154         var c = new Roo.data.Connection();
46155         c.request({
46156             url : this.form.progressUrl,
46157             params: {
46158                 id : uid
46159             },
46160             method: 'GET',
46161             success : function(req){
46162                //console.log(data);
46163                 var rdata = false;
46164                 var edata;
46165                 try  {
46166                    rdata = Roo.decode(req.responseText)
46167                 } catch (e) {
46168                     Roo.log("Invalid data from server..");
46169                     Roo.log(edata);
46170                     return;
46171                 }
46172                 if (!rdata || !rdata.success) {
46173                     Roo.log(rdata);
46174                     Roo.MessageBox.alert(Roo.encode(rdata));
46175                     return;
46176                 }
46177                 var data = rdata.data;
46178                 
46179                 if (this.uploadComplete) {
46180                    Roo.MessageBox.hide();
46181                    return;
46182                 }
46183                    
46184                 if (data){
46185                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46186                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46187                     );
46188                 }
46189                 this.uploadProgress.defer(2000,this);
46190             },
46191        
46192             failure: function(data) {
46193                 Roo.log('progress url failed ');
46194                 Roo.log(data);
46195             },
46196             scope : this
46197         });
46198            
46199     },
46200     
46201     
46202     run : function()
46203     {
46204         // run get Values on the form, so it syncs any secondary forms.
46205         this.form.getValues();
46206         
46207         var o = this.options;
46208         var method = this.getMethod();
46209         var isPost = method == 'POST';
46210         if(o.clientValidation === false || this.form.isValid()){
46211             
46212             if (this.form.progressUrl) {
46213                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46214                     (new Date() * 1) + '' + Math.random());
46215                     
46216             } 
46217             
46218             
46219             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46220                 form:this.form.el.dom,
46221                 url:this.getUrl(!isPost),
46222                 method: method,
46223                 params:isPost ? this.getParams() : null,
46224                 isUpload: this.form.fileUpload
46225             }));
46226             
46227             this.uploadProgress();
46228
46229         }else if (o.clientValidation !== false){ // client validation failed
46230             this.failureType = Roo.form.Action.CLIENT_INVALID;
46231             this.form.afterAction(this, false);
46232         }
46233     },
46234
46235     success : function(response)
46236     {
46237         this.uploadComplete= true;
46238         if (this.haveProgress) {
46239             Roo.MessageBox.hide();
46240         }
46241         
46242         
46243         var result = this.processResponse(response);
46244         if(result === true || result.success){
46245             this.form.afterAction(this, true);
46246             return;
46247         }
46248         if(result.errors){
46249             this.form.markInvalid(result.errors);
46250             this.failureType = Roo.form.Action.SERVER_INVALID;
46251         }
46252         this.form.afterAction(this, false);
46253     },
46254     failure : function(response)
46255     {
46256         this.uploadComplete= true;
46257         if (this.haveProgress) {
46258             Roo.MessageBox.hide();
46259         }
46260         
46261         this.response = response;
46262         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46263         this.form.afterAction(this, false);
46264     },
46265     
46266     handleResponse : function(response){
46267         if(this.form.errorReader){
46268             var rs = this.form.errorReader.read(response);
46269             var errors = [];
46270             if(rs.records){
46271                 for(var i = 0, len = rs.records.length; i < len; i++) {
46272                     var r = rs.records[i];
46273                     errors[i] = r.data;
46274                 }
46275             }
46276             if(errors.length < 1){
46277                 errors = null;
46278             }
46279             return {
46280                 success : rs.success,
46281                 errors : errors
46282             };
46283         }
46284         var ret = false;
46285         try {
46286             ret = Roo.decode(response.responseText);
46287         } catch (e) {
46288             ret = {
46289                 success: false,
46290                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46291                 errors : []
46292             };
46293         }
46294         return ret;
46295         
46296     }
46297 });
46298
46299
46300 Roo.form.Action.Load = function(form, options){
46301     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46302     this.reader = this.form.reader;
46303 };
46304
46305 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46306     type : 'load',
46307
46308     run : function(){
46309         
46310         Roo.Ajax.request(Roo.apply(
46311                 this.createCallback(), {
46312                     method:this.getMethod(),
46313                     url:this.getUrl(false),
46314                     params:this.getParams()
46315         }));
46316     },
46317
46318     success : function(response){
46319         
46320         var result = this.processResponse(response);
46321         if(result === true || !result.success || !result.data){
46322             this.failureType = Roo.form.Action.LOAD_FAILURE;
46323             this.form.afterAction(this, false);
46324             return;
46325         }
46326         this.form.clearInvalid();
46327         this.form.setValues(result.data);
46328         this.form.afterAction(this, true);
46329     },
46330
46331     handleResponse : function(response){
46332         if(this.form.reader){
46333             var rs = this.form.reader.read(response);
46334             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46335             return {
46336                 success : rs.success,
46337                 data : data
46338             };
46339         }
46340         return Roo.decode(response.responseText);
46341     }
46342 });
46343
46344 Roo.form.Action.ACTION_TYPES = {
46345     'load' : Roo.form.Action.Load,
46346     'submit' : Roo.form.Action.Submit
46347 };/*
46348  * Based on:
46349  * Ext JS Library 1.1.1
46350  * Copyright(c) 2006-2007, Ext JS, LLC.
46351  *
46352  * Originally Released Under LGPL - original licence link has changed is not relivant.
46353  *
46354  * Fork - LGPL
46355  * <script type="text/javascript">
46356  */
46357  
46358 /**
46359  * @class Roo.form.Layout
46360  * @extends Roo.Component
46361  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46362  * @constructor
46363  * @param {Object} config Configuration options
46364  */
46365 Roo.form.Layout = function(config){
46366     var xitems = [];
46367     if (config.items) {
46368         xitems = config.items;
46369         delete config.items;
46370     }
46371     Roo.form.Layout.superclass.constructor.call(this, config);
46372     this.stack = [];
46373     Roo.each(xitems, this.addxtype, this);
46374      
46375 };
46376
46377 Roo.extend(Roo.form.Layout, Roo.Component, {
46378     /**
46379      * @cfg {String/Object} autoCreate
46380      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46381      */
46382     /**
46383      * @cfg {String/Object/Function} style
46384      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46385      * a function which returns such a specification.
46386      */
46387     /**
46388      * @cfg {String} labelAlign
46389      * Valid values are "left," "top" and "right" (defaults to "left")
46390      */
46391     /**
46392      * @cfg {Number} labelWidth
46393      * Fixed width in pixels of all field labels (defaults to undefined)
46394      */
46395     /**
46396      * @cfg {Boolean} clear
46397      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46398      */
46399     clear : true,
46400     /**
46401      * @cfg {String} labelSeparator
46402      * The separator to use after field labels (defaults to ':')
46403      */
46404     labelSeparator : ':',
46405     /**
46406      * @cfg {Boolean} hideLabels
46407      * True to suppress the display of field labels in this layout (defaults to false)
46408      */
46409     hideLabels : false,
46410
46411     // private
46412     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46413     
46414     isLayout : true,
46415     
46416     // private
46417     onRender : function(ct, position){
46418         if(this.el){ // from markup
46419             this.el = Roo.get(this.el);
46420         }else {  // generate
46421             var cfg = this.getAutoCreate();
46422             this.el = ct.createChild(cfg, position);
46423         }
46424         if(this.style){
46425             this.el.applyStyles(this.style);
46426         }
46427         if(this.labelAlign){
46428             this.el.addClass('x-form-label-'+this.labelAlign);
46429         }
46430         if(this.hideLabels){
46431             this.labelStyle = "display:none";
46432             this.elementStyle = "padding-left:0;";
46433         }else{
46434             if(typeof this.labelWidth == 'number'){
46435                 this.labelStyle = "width:"+this.labelWidth+"px;";
46436                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46437             }
46438             if(this.labelAlign == 'top'){
46439                 this.labelStyle = "width:auto;";
46440                 this.elementStyle = "padding-left:0;";
46441             }
46442         }
46443         var stack = this.stack;
46444         var slen = stack.length;
46445         if(slen > 0){
46446             if(!this.fieldTpl){
46447                 var t = new Roo.Template(
46448                     '<div class="x-form-item {5}">',
46449                         '<label for="{0}" style="{2}">{1}{4}</label>',
46450                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46451                         '</div>',
46452                     '</div><div class="x-form-clear-left"></div>'
46453                 );
46454                 t.disableFormats = true;
46455                 t.compile();
46456                 Roo.form.Layout.prototype.fieldTpl = t;
46457             }
46458             for(var i = 0; i < slen; i++) {
46459                 if(stack[i].isFormField){
46460                     this.renderField(stack[i]);
46461                 }else{
46462                     this.renderComponent(stack[i]);
46463                 }
46464             }
46465         }
46466         if(this.clear){
46467             this.el.createChild({cls:'x-form-clear'});
46468         }
46469     },
46470
46471     // private
46472     renderField : function(f){
46473         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46474                f.id, //0
46475                f.fieldLabel, //1
46476                f.labelStyle||this.labelStyle||'', //2
46477                this.elementStyle||'', //3
46478                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46479                f.itemCls||this.itemCls||''  //5
46480        ], true).getPrevSibling());
46481     },
46482
46483     // private
46484     renderComponent : function(c){
46485         c.render(c.isLayout ? this.el : this.el.createChild());    
46486     },
46487     /**
46488      * Adds a object form elements (using the xtype property as the factory method.)
46489      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46490      * @param {Object} config 
46491      */
46492     addxtype : function(o)
46493     {
46494         // create the lement.
46495         o.form = this.form;
46496         var fe = Roo.factory(o, Roo.form);
46497         this.form.allItems.push(fe);
46498         this.stack.push(fe);
46499         
46500         if (fe.isFormField) {
46501             this.form.items.add(fe);
46502         }
46503          
46504         return fe;
46505     }
46506 });
46507
46508 /**
46509  * @class Roo.form.Column
46510  * @extends Roo.form.Layout
46511  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46512  * @constructor
46513  * @param {Object} config Configuration options
46514  */
46515 Roo.form.Column = function(config){
46516     Roo.form.Column.superclass.constructor.call(this, config);
46517 };
46518
46519 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46520     /**
46521      * @cfg {Number/String} width
46522      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46523      */
46524     /**
46525      * @cfg {String/Object} autoCreate
46526      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46527      */
46528
46529     // private
46530     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46531
46532     // private
46533     onRender : function(ct, position){
46534         Roo.form.Column.superclass.onRender.call(this, ct, position);
46535         if(this.width){
46536             this.el.setWidth(this.width);
46537         }
46538     }
46539 });
46540
46541
46542 /**
46543  * @class Roo.form.Row
46544  * @extends Roo.form.Layout
46545  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46546  * @constructor
46547  * @param {Object} config Configuration options
46548  */
46549
46550  
46551 Roo.form.Row = function(config){
46552     Roo.form.Row.superclass.constructor.call(this, config);
46553 };
46554  
46555 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46556       /**
46557      * @cfg {Number/String} width
46558      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46559      */
46560     /**
46561      * @cfg {Number/String} height
46562      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46563      */
46564     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46565     
46566     padWidth : 20,
46567     // private
46568     onRender : function(ct, position){
46569         //console.log('row render');
46570         if(!this.rowTpl){
46571             var t = new Roo.Template(
46572                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46573                     '<label for="{0}" style="{2}">{1}{4}</label>',
46574                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46575                     '</div>',
46576                 '</div>'
46577             );
46578             t.disableFormats = true;
46579             t.compile();
46580             Roo.form.Layout.prototype.rowTpl = t;
46581         }
46582         this.fieldTpl = this.rowTpl;
46583         
46584         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46585         var labelWidth = 100;
46586         
46587         if ((this.labelAlign != 'top')) {
46588             if (typeof this.labelWidth == 'number') {
46589                 labelWidth = this.labelWidth
46590             }
46591             this.padWidth =  20 + labelWidth;
46592             
46593         }
46594         
46595         Roo.form.Column.superclass.onRender.call(this, ct, position);
46596         if(this.width){
46597             this.el.setWidth(this.width);
46598         }
46599         if(this.height){
46600             this.el.setHeight(this.height);
46601         }
46602     },
46603     
46604     // private
46605     renderField : function(f){
46606         f.fieldEl = this.fieldTpl.append(this.el, [
46607                f.id, f.fieldLabel,
46608                f.labelStyle||this.labelStyle||'',
46609                this.elementStyle||'',
46610                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46611                f.itemCls||this.itemCls||'',
46612                f.width ? f.width + this.padWidth : 160 + this.padWidth
46613        ],true);
46614     }
46615 });
46616  
46617
46618 /**
46619  * @class Roo.form.FieldSet
46620  * @extends Roo.form.Layout
46621  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46622  * @constructor
46623  * @param {Object} config Configuration options
46624  */
46625 Roo.form.FieldSet = function(config){
46626     Roo.form.FieldSet.superclass.constructor.call(this, config);
46627 };
46628
46629 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46630     /**
46631      * @cfg {String} legend
46632      * The text to display as the legend for the FieldSet (defaults to '')
46633      */
46634     /**
46635      * @cfg {String/Object} autoCreate
46636      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46637      */
46638
46639     // private
46640     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46641
46642     // private
46643     onRender : function(ct, position){
46644         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46645         if(this.legend){
46646             this.setLegend(this.legend);
46647         }
46648     },
46649
46650     // private
46651     setLegend : function(text){
46652         if(this.rendered){
46653             this.el.child('legend').update(text);
46654         }
46655     }
46656 });/*
46657  * Based on:
46658  * Ext JS Library 1.1.1
46659  * Copyright(c) 2006-2007, Ext JS, LLC.
46660  *
46661  * Originally Released Under LGPL - original licence link has changed is not relivant.
46662  *
46663  * Fork - LGPL
46664  * <script type="text/javascript">
46665  */
46666 /**
46667  * @class Roo.form.VTypes
46668  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46669  * @singleton
46670  */
46671 Roo.form.VTypes = function(){
46672     // closure these in so they are only created once.
46673     var alpha = /^[a-zA-Z_]+$/;
46674     var alphanum = /^[a-zA-Z0-9_]+$/;
46675     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46676     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46677
46678     // All these messages and functions are configurable
46679     return {
46680         /**
46681          * The function used to validate email addresses
46682          * @param {String} value The email address
46683          */
46684         'email' : function(v){
46685             return email.test(v);
46686         },
46687         /**
46688          * The error text to display when the email validation function returns false
46689          * @type String
46690          */
46691         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46692         /**
46693          * The keystroke filter mask to be applied on email input
46694          * @type RegExp
46695          */
46696         'emailMask' : /[a-z0-9_\.\-@]/i,
46697
46698         /**
46699          * The function used to validate URLs
46700          * @param {String} value The URL
46701          */
46702         'url' : function(v){
46703             return url.test(v);
46704         },
46705         /**
46706          * The error text to display when the url validation function returns false
46707          * @type String
46708          */
46709         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46710         
46711         /**
46712          * The function used to validate alpha values
46713          * @param {String} value The value
46714          */
46715         'alpha' : function(v){
46716             return alpha.test(v);
46717         },
46718         /**
46719          * The error text to display when the alpha validation function returns false
46720          * @type String
46721          */
46722         'alphaText' : 'This field should only contain letters and _',
46723         /**
46724          * The keystroke filter mask to be applied on alpha input
46725          * @type RegExp
46726          */
46727         'alphaMask' : /[a-z_]/i,
46728
46729         /**
46730          * The function used to validate alphanumeric values
46731          * @param {String} value The value
46732          */
46733         'alphanum' : function(v){
46734             return alphanum.test(v);
46735         },
46736         /**
46737          * The error text to display when the alphanumeric validation function returns false
46738          * @type String
46739          */
46740         'alphanumText' : 'This field should only contain letters, numbers and _',
46741         /**
46742          * The keystroke filter mask to be applied on alphanumeric input
46743          * @type RegExp
46744          */
46745         'alphanumMask' : /[a-z0-9_]/i
46746     };
46747 }();//<script type="text/javascript">
46748
46749 /**
46750  * @class Roo.form.FCKeditor
46751  * @extends Roo.form.TextArea
46752  * Wrapper around the FCKEditor http://www.fckeditor.net
46753  * @constructor
46754  * Creates a new FCKeditor
46755  * @param {Object} config Configuration options
46756  */
46757 Roo.form.FCKeditor = function(config){
46758     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46759     this.addEvents({
46760          /**
46761          * @event editorinit
46762          * Fired when the editor is initialized - you can add extra handlers here..
46763          * @param {FCKeditor} this
46764          * @param {Object} the FCK object.
46765          */
46766         editorinit : true
46767     });
46768     
46769     
46770 };
46771 Roo.form.FCKeditor.editors = { };
46772 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46773 {
46774     //defaultAutoCreate : {
46775     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46776     //},
46777     // private
46778     /**
46779      * @cfg {Object} fck options - see fck manual for details.
46780      */
46781     fckconfig : false,
46782     
46783     /**
46784      * @cfg {Object} fck toolbar set (Basic or Default)
46785      */
46786     toolbarSet : 'Basic',
46787     /**
46788      * @cfg {Object} fck BasePath
46789      */ 
46790     basePath : '/fckeditor/',
46791     
46792     
46793     frame : false,
46794     
46795     value : '',
46796     
46797    
46798     onRender : function(ct, position)
46799     {
46800         if(!this.el){
46801             this.defaultAutoCreate = {
46802                 tag: "textarea",
46803                 style:"width:300px;height:60px;",
46804                 autocomplete: "new-password"
46805             };
46806         }
46807         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46808         /*
46809         if(this.grow){
46810             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46811             if(this.preventScrollbars){
46812                 this.el.setStyle("overflow", "hidden");
46813             }
46814             this.el.setHeight(this.growMin);
46815         }
46816         */
46817         //console.log('onrender' + this.getId() );
46818         Roo.form.FCKeditor.editors[this.getId()] = this;
46819          
46820
46821         this.replaceTextarea() ;
46822         
46823     },
46824     
46825     getEditor : function() {
46826         return this.fckEditor;
46827     },
46828     /**
46829      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46830      * @param {Mixed} value The value to set
46831      */
46832     
46833     
46834     setValue : function(value)
46835     {
46836         //console.log('setValue: ' + value);
46837         
46838         if(typeof(value) == 'undefined') { // not sure why this is happending...
46839             return;
46840         }
46841         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46842         
46843         //if(!this.el || !this.getEditor()) {
46844         //    this.value = value;
46845             //this.setValue.defer(100,this,[value]);    
46846         //    return;
46847         //} 
46848         
46849         if(!this.getEditor()) {
46850             return;
46851         }
46852         
46853         this.getEditor().SetData(value);
46854         
46855         //
46856
46857     },
46858
46859     /**
46860      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46861      * @return {Mixed} value The field value
46862      */
46863     getValue : function()
46864     {
46865         
46866         if (this.frame && this.frame.dom.style.display == 'none') {
46867             return Roo.form.FCKeditor.superclass.getValue.call(this);
46868         }
46869         
46870         if(!this.el || !this.getEditor()) {
46871            
46872            // this.getValue.defer(100,this); 
46873             return this.value;
46874         }
46875        
46876         
46877         var value=this.getEditor().GetData();
46878         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46879         return Roo.form.FCKeditor.superclass.getValue.call(this);
46880         
46881
46882     },
46883
46884     /**
46885      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46886      * @return {Mixed} value The field value
46887      */
46888     getRawValue : function()
46889     {
46890         if (this.frame && this.frame.dom.style.display == 'none') {
46891             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46892         }
46893         
46894         if(!this.el || !this.getEditor()) {
46895             //this.getRawValue.defer(100,this); 
46896             return this.value;
46897             return;
46898         }
46899         
46900         
46901         
46902         var value=this.getEditor().GetData();
46903         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46904         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46905          
46906     },
46907     
46908     setSize : function(w,h) {
46909         
46910         
46911         
46912         //if (this.frame && this.frame.dom.style.display == 'none') {
46913         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46914         //    return;
46915         //}
46916         //if(!this.el || !this.getEditor()) {
46917         //    this.setSize.defer(100,this, [w,h]); 
46918         //    return;
46919         //}
46920         
46921         
46922         
46923         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46924         
46925         this.frame.dom.setAttribute('width', w);
46926         this.frame.dom.setAttribute('height', h);
46927         this.frame.setSize(w,h);
46928         
46929     },
46930     
46931     toggleSourceEdit : function(value) {
46932         
46933       
46934          
46935         this.el.dom.style.display = value ? '' : 'none';
46936         this.frame.dom.style.display = value ?  'none' : '';
46937         
46938     },
46939     
46940     
46941     focus: function(tag)
46942     {
46943         if (this.frame.dom.style.display == 'none') {
46944             return Roo.form.FCKeditor.superclass.focus.call(this);
46945         }
46946         if(!this.el || !this.getEditor()) {
46947             this.focus.defer(100,this, [tag]); 
46948             return;
46949         }
46950         
46951         
46952         
46953         
46954         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46955         this.getEditor().Focus();
46956         if (tgs.length) {
46957             if (!this.getEditor().Selection.GetSelection()) {
46958                 this.focus.defer(100,this, [tag]); 
46959                 return;
46960             }
46961             
46962             
46963             var r = this.getEditor().EditorDocument.createRange();
46964             r.setStart(tgs[0],0);
46965             r.setEnd(tgs[0],0);
46966             this.getEditor().Selection.GetSelection().removeAllRanges();
46967             this.getEditor().Selection.GetSelection().addRange(r);
46968             this.getEditor().Focus();
46969         }
46970         
46971     },
46972     
46973     
46974     
46975     replaceTextarea : function()
46976     {
46977         if ( document.getElementById( this.getId() + '___Frame' ) )
46978             return ;
46979         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46980         //{
46981             // We must check the elements firstly using the Id and then the name.
46982         var oTextarea = document.getElementById( this.getId() );
46983         
46984         var colElementsByName = document.getElementsByName( this.getId() ) ;
46985          
46986         oTextarea.style.display = 'none' ;
46987
46988         if ( oTextarea.tabIndex ) {            
46989             this.TabIndex = oTextarea.tabIndex ;
46990         }
46991         
46992         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46993         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46994         this.frame = Roo.get(this.getId() + '___Frame')
46995     },
46996     
46997     _getConfigHtml : function()
46998     {
46999         var sConfig = '' ;
47000
47001         for ( var o in this.fckconfig ) {
47002             sConfig += sConfig.length > 0  ? '&amp;' : '';
47003             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47004         }
47005
47006         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47007     },
47008     
47009     
47010     _getIFrameHtml : function()
47011     {
47012         var sFile = 'fckeditor.html' ;
47013         /* no idea what this is about..
47014         try
47015         {
47016             if ( (/fcksource=true/i).test( window.top.location.search ) )
47017                 sFile = 'fckeditor.original.html' ;
47018         }
47019         catch (e) { 
47020         */
47021
47022         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47023         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47024         
47025         
47026         var html = '<iframe id="' + this.getId() +
47027             '___Frame" src="' + sLink +
47028             '" width="' + this.width +
47029             '" height="' + this.height + '"' +
47030             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47031             ' frameborder="0" scrolling="no"></iframe>' ;
47032
47033         return html ;
47034     },
47035     
47036     _insertHtmlBefore : function( html, element )
47037     {
47038         if ( element.insertAdjacentHTML )       {
47039             // IE
47040             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47041         } else { // Gecko
47042             var oRange = document.createRange() ;
47043             oRange.setStartBefore( element ) ;
47044             var oFragment = oRange.createContextualFragment( html );
47045             element.parentNode.insertBefore( oFragment, element ) ;
47046         }
47047     }
47048     
47049     
47050   
47051     
47052     
47053     
47054     
47055
47056 });
47057
47058 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47059
47060 function FCKeditor_OnComplete(editorInstance){
47061     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47062     f.fckEditor = editorInstance;
47063     //console.log("loaded");
47064     f.fireEvent('editorinit', f, editorInstance);
47065
47066   
47067
47068  
47069
47070
47071
47072
47073
47074
47075
47076
47077
47078
47079
47080
47081
47082
47083
47084 //<script type="text/javascript">
47085 /**
47086  * @class Roo.form.GridField
47087  * @extends Roo.form.Field
47088  * Embed a grid (or editable grid into a form)
47089  * STATUS ALPHA
47090  * 
47091  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47092  * it needs 
47093  * xgrid.store = Roo.data.Store
47094  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47095  * xgrid.store.reader = Roo.data.JsonReader 
47096  * 
47097  * 
47098  * @constructor
47099  * Creates a new GridField
47100  * @param {Object} config Configuration options
47101  */
47102 Roo.form.GridField = function(config){
47103     Roo.form.GridField.superclass.constructor.call(this, config);
47104      
47105 };
47106
47107 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47108     /**
47109      * @cfg {Number} width  - used to restrict width of grid..
47110      */
47111     width : 100,
47112     /**
47113      * @cfg {Number} height - used to restrict height of grid..
47114      */
47115     height : 50,
47116      /**
47117      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47118          * 
47119          *}
47120      */
47121     xgrid : false, 
47122     /**
47123      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47124      * {tag: "input", type: "checkbox", autocomplete: "off"})
47125      */
47126    // defaultAutoCreate : { tag: 'div' },
47127     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47128     /**
47129      * @cfg {String} addTitle Text to include for adding a title.
47130      */
47131     addTitle : false,
47132     //
47133     onResize : function(){
47134         Roo.form.Field.superclass.onResize.apply(this, arguments);
47135     },
47136
47137     initEvents : function(){
47138         // Roo.form.Checkbox.superclass.initEvents.call(this);
47139         // has no events...
47140        
47141     },
47142
47143
47144     getResizeEl : function(){
47145         return this.wrap;
47146     },
47147
47148     getPositionEl : function(){
47149         return this.wrap;
47150     },
47151
47152     // private
47153     onRender : function(ct, position){
47154         
47155         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47156         var style = this.style;
47157         delete this.style;
47158         
47159         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47160         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47161         this.viewEl = this.wrap.createChild({ tag: 'div' });
47162         if (style) {
47163             this.viewEl.applyStyles(style);
47164         }
47165         if (this.width) {
47166             this.viewEl.setWidth(this.width);
47167         }
47168         if (this.height) {
47169             this.viewEl.setHeight(this.height);
47170         }
47171         //if(this.inputValue !== undefined){
47172         //this.setValue(this.value);
47173         
47174         
47175         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47176         
47177         
47178         this.grid.render();
47179         this.grid.getDataSource().on('remove', this.refreshValue, this);
47180         this.grid.getDataSource().on('update', this.refreshValue, this);
47181         this.grid.on('afteredit', this.refreshValue, this);
47182  
47183     },
47184      
47185     
47186     /**
47187      * Sets the value of the item. 
47188      * @param {String} either an object  or a string..
47189      */
47190     setValue : function(v){
47191         //this.value = v;
47192         v = v || []; // empty set..
47193         // this does not seem smart - it really only affects memoryproxy grids..
47194         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47195             var ds = this.grid.getDataSource();
47196             // assumes a json reader..
47197             var data = {}
47198             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47199             ds.loadData( data);
47200         }
47201         // clear selection so it does not get stale.
47202         if (this.grid.sm) { 
47203             this.grid.sm.clearSelections();
47204         }
47205         
47206         Roo.form.GridField.superclass.setValue.call(this, v);
47207         this.refreshValue();
47208         // should load data in the grid really....
47209     },
47210     
47211     // private
47212     refreshValue: function() {
47213          var val = [];
47214         this.grid.getDataSource().each(function(r) {
47215             val.push(r.data);
47216         });
47217         this.el.dom.value = Roo.encode(val);
47218     }
47219     
47220      
47221     
47222     
47223 });/*
47224  * Based on:
47225  * Ext JS Library 1.1.1
47226  * Copyright(c) 2006-2007, Ext JS, LLC.
47227  *
47228  * Originally Released Under LGPL - original licence link has changed is not relivant.
47229  *
47230  * Fork - LGPL
47231  * <script type="text/javascript">
47232  */
47233 /**
47234  * @class Roo.form.DisplayField
47235  * @extends Roo.form.Field
47236  * A generic Field to display non-editable data.
47237  * @constructor
47238  * Creates a new Display Field item.
47239  * @param {Object} config Configuration options
47240  */
47241 Roo.form.DisplayField = function(config){
47242     Roo.form.DisplayField.superclass.constructor.call(this, config);
47243     
47244 };
47245
47246 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47247     inputType:      'hidden',
47248     allowBlank:     true,
47249     readOnly:         true,
47250     
47251  
47252     /**
47253      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47254      */
47255     focusClass : undefined,
47256     /**
47257      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47258      */
47259     fieldClass: 'x-form-field',
47260     
47261      /**
47262      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47263      */
47264     valueRenderer: undefined,
47265     
47266     width: 100,
47267     /**
47268      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47269      * {tag: "input", type: "checkbox", autocomplete: "off"})
47270      */
47271      
47272  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47273
47274     onResize : function(){
47275         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47276         
47277     },
47278
47279     initEvents : function(){
47280         // Roo.form.Checkbox.superclass.initEvents.call(this);
47281         // has no events...
47282        
47283     },
47284
47285
47286     getResizeEl : function(){
47287         return this.wrap;
47288     },
47289
47290     getPositionEl : function(){
47291         return this.wrap;
47292     },
47293
47294     // private
47295     onRender : function(ct, position){
47296         
47297         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47298         //if(this.inputValue !== undefined){
47299         this.wrap = this.el.wrap();
47300         
47301         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47302         
47303         if (this.bodyStyle) {
47304             this.viewEl.applyStyles(this.bodyStyle);
47305         }
47306         //this.viewEl.setStyle('padding', '2px');
47307         
47308         this.setValue(this.value);
47309         
47310     },
47311 /*
47312     // private
47313     initValue : Roo.emptyFn,
47314
47315   */
47316
47317         // private
47318     onClick : function(){
47319         
47320     },
47321
47322     /**
47323      * Sets the checked state of the checkbox.
47324      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47325      */
47326     setValue : function(v){
47327         this.value = v;
47328         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47329         // this might be called before we have a dom element..
47330         if (!this.viewEl) {
47331             return;
47332         }
47333         this.viewEl.dom.innerHTML = html;
47334         Roo.form.DisplayField.superclass.setValue.call(this, v);
47335
47336     }
47337 });/*
47338  * 
47339  * Licence- LGPL
47340  * 
47341  */
47342
47343 /**
47344  * @class Roo.form.DayPicker
47345  * @extends Roo.form.Field
47346  * A Day picker show [M] [T] [W] ....
47347  * @constructor
47348  * Creates a new Day Picker
47349  * @param {Object} config Configuration options
47350  */
47351 Roo.form.DayPicker= function(config){
47352     Roo.form.DayPicker.superclass.constructor.call(this, config);
47353      
47354 };
47355
47356 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47357     /**
47358      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47359      */
47360     focusClass : undefined,
47361     /**
47362      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47363      */
47364     fieldClass: "x-form-field",
47365    
47366     /**
47367      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47368      * {tag: "input", type: "checkbox", autocomplete: "off"})
47369      */
47370     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47371     
47372    
47373     actionMode : 'viewEl', 
47374     //
47375     // private
47376  
47377     inputType : 'hidden',
47378     
47379      
47380     inputElement: false, // real input element?
47381     basedOn: false, // ????
47382     
47383     isFormField: true, // not sure where this is needed!!!!
47384
47385     onResize : function(){
47386         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47387         if(!this.boxLabel){
47388             this.el.alignTo(this.wrap, 'c-c');
47389         }
47390     },
47391
47392     initEvents : function(){
47393         Roo.form.Checkbox.superclass.initEvents.call(this);
47394         this.el.on("click", this.onClick,  this);
47395         this.el.on("change", this.onClick,  this);
47396     },
47397
47398
47399     getResizeEl : function(){
47400         return this.wrap;
47401     },
47402
47403     getPositionEl : function(){
47404         return this.wrap;
47405     },
47406
47407     
47408     // private
47409     onRender : function(ct, position){
47410         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47411        
47412         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47413         
47414         var r1 = '<table><tr>';
47415         var r2 = '<tr class="x-form-daypick-icons">';
47416         for (var i=0; i < 7; i++) {
47417             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47418             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47419         }
47420         
47421         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47422         viewEl.select('img').on('click', this.onClick, this);
47423         this.viewEl = viewEl;   
47424         
47425         
47426         // this will not work on Chrome!!!
47427         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47428         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47429         
47430         
47431           
47432
47433     },
47434
47435     // private
47436     initValue : Roo.emptyFn,
47437
47438     /**
47439      * Returns the checked state of the checkbox.
47440      * @return {Boolean} True if checked, else false
47441      */
47442     getValue : function(){
47443         return this.el.dom.value;
47444         
47445     },
47446
47447         // private
47448     onClick : function(e){ 
47449         //this.setChecked(!this.checked);
47450         Roo.get(e.target).toggleClass('x-menu-item-checked');
47451         this.refreshValue();
47452         //if(this.el.dom.checked != this.checked){
47453         //    this.setValue(this.el.dom.checked);
47454        // }
47455     },
47456     
47457     // private
47458     refreshValue : function()
47459     {
47460         var val = '';
47461         this.viewEl.select('img',true).each(function(e,i,n)  {
47462             val += e.is(".x-menu-item-checked") ? String(n) : '';
47463         });
47464         this.setValue(val, true);
47465     },
47466
47467     /**
47468      * Sets the checked state of the checkbox.
47469      * On is always based on a string comparison between inputValue and the param.
47470      * @param {Boolean/String} value - the value to set 
47471      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47472      */
47473     setValue : function(v,suppressEvent){
47474         if (!this.el.dom) {
47475             return;
47476         }
47477         var old = this.el.dom.value ;
47478         this.el.dom.value = v;
47479         if (suppressEvent) {
47480             return ;
47481         }
47482          
47483         // update display..
47484         this.viewEl.select('img',true).each(function(e,i,n)  {
47485             
47486             var on = e.is(".x-menu-item-checked");
47487             var newv = v.indexOf(String(n)) > -1;
47488             if (on != newv) {
47489                 e.toggleClass('x-menu-item-checked');
47490             }
47491             
47492         });
47493         
47494         
47495         this.fireEvent('change', this, v, old);
47496         
47497         
47498     },
47499    
47500     // handle setting of hidden value by some other method!!?!?
47501     setFromHidden: function()
47502     {
47503         if(!this.el){
47504             return;
47505         }
47506         //console.log("SET FROM HIDDEN");
47507         //alert('setFrom hidden');
47508         this.setValue(this.el.dom.value);
47509     },
47510     
47511     onDestroy : function()
47512     {
47513         if(this.viewEl){
47514             Roo.get(this.viewEl).remove();
47515         }
47516          
47517         Roo.form.DayPicker.superclass.onDestroy.call(this);
47518     }
47519
47520 });/*
47521  * RooJS Library 1.1.1
47522  * Copyright(c) 2008-2011  Alan Knowles
47523  *
47524  * License - LGPL
47525  */
47526  
47527
47528 /**
47529  * @class Roo.form.ComboCheck
47530  * @extends Roo.form.ComboBox
47531  * A combobox for multiple select items.
47532  *
47533  * FIXME - could do with a reset button..
47534  * 
47535  * @constructor
47536  * Create a new ComboCheck
47537  * @param {Object} config Configuration options
47538  */
47539 Roo.form.ComboCheck = function(config){
47540     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47541     // should verify some data...
47542     // like
47543     // hiddenName = required..
47544     // displayField = required
47545     // valudField == required
47546     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47547     var _t = this;
47548     Roo.each(req, function(e) {
47549         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47550             throw "Roo.form.ComboCheck : missing value for: " + e;
47551         }
47552     });
47553     
47554     
47555 };
47556
47557 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47558      
47559      
47560     editable : false,
47561      
47562     selectedClass: 'x-menu-item-checked', 
47563     
47564     // private
47565     onRender : function(ct, position){
47566         var _t = this;
47567         
47568         
47569         
47570         if(!this.tpl){
47571             var cls = 'x-combo-list';
47572
47573             
47574             this.tpl =  new Roo.Template({
47575                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47576                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47577                    '<span>{' + this.displayField + '}</span>' +
47578                     '</div>' 
47579                 
47580             });
47581         }
47582  
47583         
47584         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47585         this.view.singleSelect = false;
47586         this.view.multiSelect = true;
47587         this.view.toggleSelect = true;
47588         this.pageTb.add(new Roo.Toolbar.Fill(), {
47589             
47590             text: 'Done',
47591             handler: function()
47592             {
47593                 _t.collapse();
47594             }
47595         });
47596     },
47597     
47598     onViewOver : function(e, t){
47599         // do nothing...
47600         return;
47601         
47602     },
47603     
47604     onViewClick : function(doFocus,index){
47605         return;
47606         
47607     },
47608     select: function () {
47609         //Roo.log("SELECT CALLED");
47610     },
47611      
47612     selectByValue : function(xv, scrollIntoView){
47613         var ar = this.getValueArray();
47614         var sels = [];
47615         
47616         Roo.each(ar, function(v) {
47617             if(v === undefined || v === null){
47618                 return;
47619             }
47620             var r = this.findRecord(this.valueField, v);
47621             if(r){
47622                 sels.push(this.store.indexOf(r))
47623                 
47624             }
47625         },this);
47626         this.view.select(sels);
47627         return false;
47628     },
47629     
47630     
47631     
47632     onSelect : function(record, index){
47633        // Roo.log("onselect Called");
47634        // this is only called by the clear button now..
47635         this.view.clearSelections();
47636         this.setValue('[]');
47637         if (this.value != this.valueBefore) {
47638             this.fireEvent('change', this, this.value, this.valueBefore);
47639             this.valueBefore = this.value;
47640         }
47641     },
47642     getValueArray : function()
47643     {
47644         var ar = [] ;
47645         
47646         try {
47647             //Roo.log(this.value);
47648             if (typeof(this.value) == 'undefined') {
47649                 return [];
47650             }
47651             var ar = Roo.decode(this.value);
47652             return  ar instanceof Array ? ar : []; //?? valid?
47653             
47654         } catch(e) {
47655             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47656             return [];
47657         }
47658          
47659     },
47660     expand : function ()
47661     {
47662         
47663         Roo.form.ComboCheck.superclass.expand.call(this);
47664         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47665         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47666         
47667
47668     },
47669     
47670     collapse : function(){
47671         Roo.form.ComboCheck.superclass.collapse.call(this);
47672         var sl = this.view.getSelectedIndexes();
47673         var st = this.store;
47674         var nv = [];
47675         var tv = [];
47676         var r;
47677         Roo.each(sl, function(i) {
47678             r = st.getAt(i);
47679             nv.push(r.get(this.valueField));
47680         },this);
47681         this.setValue(Roo.encode(nv));
47682         if (this.value != this.valueBefore) {
47683
47684             this.fireEvent('change', this, this.value, this.valueBefore);
47685             this.valueBefore = this.value;
47686         }
47687         
47688     },
47689     
47690     setValue : function(v){
47691         // Roo.log(v);
47692         this.value = v;
47693         
47694         var vals = this.getValueArray();
47695         var tv = [];
47696         Roo.each(vals, function(k) {
47697             var r = this.findRecord(this.valueField, k);
47698             if(r){
47699                 tv.push(r.data[this.displayField]);
47700             }else if(this.valueNotFoundText !== undefined){
47701                 tv.push( this.valueNotFoundText );
47702             }
47703         },this);
47704        // Roo.log(tv);
47705         
47706         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47707         this.hiddenField.value = v;
47708         this.value = v;
47709     }
47710     
47711 });/*
47712  * Based on:
47713  * Ext JS Library 1.1.1
47714  * Copyright(c) 2006-2007, Ext JS, LLC.
47715  *
47716  * Originally Released Under LGPL - original licence link has changed is not relivant.
47717  *
47718  * Fork - LGPL
47719  * <script type="text/javascript">
47720  */
47721  
47722 /**
47723  * @class Roo.form.Signature
47724  * @extends Roo.form.Field
47725  * Signature field.  
47726  * @constructor
47727  * 
47728  * @param {Object} config Configuration options
47729  */
47730
47731 Roo.form.Signature = function(config){
47732     Roo.form.Signature.superclass.constructor.call(this, config);
47733     
47734     this.addEvents({// not in used??
47735          /**
47736          * @event confirm
47737          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47738              * @param {Roo.form.Signature} combo This combo box
47739              */
47740         'confirm' : true,
47741         /**
47742          * @event reset
47743          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47744              * @param {Roo.form.ComboBox} combo This combo box
47745              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47746              */
47747         'reset' : true
47748     });
47749 };
47750
47751 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47752     /**
47753      * @cfg {Object} labels Label to use when rendering a form.
47754      * defaults to 
47755      * labels : { 
47756      *      clear : "Clear",
47757      *      confirm : "Confirm"
47758      *  }
47759      */
47760     labels : { 
47761         clear : "Clear",
47762         confirm : "Confirm"
47763     },
47764     /**
47765      * @cfg {Number} width The signature panel width (defaults to 300)
47766      */
47767     width: 300,
47768     /**
47769      * @cfg {Number} height The signature panel height (defaults to 100)
47770      */
47771     height : 100,
47772     /**
47773      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47774      */
47775     allowBlank : false,
47776     
47777     //private
47778     // {Object} signPanel The signature SVG panel element (defaults to {})
47779     signPanel : {},
47780     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47781     isMouseDown : false,
47782     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47783     isConfirmed : false,
47784     // {String} signatureTmp SVG mapping string (defaults to empty string)
47785     signatureTmp : '',
47786     
47787     
47788     defaultAutoCreate : { // modified by initCompnoent..
47789         tag: "input",
47790         type:"hidden"
47791     },
47792
47793     // private
47794     onRender : function(ct, position){
47795         
47796         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47797         
47798         this.wrap = this.el.wrap({
47799             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47800         });
47801         
47802         this.createToolbar(this);
47803         this.signPanel = this.wrap.createChild({
47804                 tag: 'div',
47805                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47806             }, this.el
47807         );
47808             
47809         this.svgID = Roo.id();
47810         this.svgEl = this.signPanel.createChild({
47811               xmlns : 'http://www.w3.org/2000/svg',
47812               tag : 'svg',
47813               id : this.svgID + "-svg",
47814               width: this.width,
47815               height: this.height,
47816               viewBox: '0 0 '+this.width+' '+this.height,
47817               cn : [
47818                 {
47819                     tag: "rect",
47820                     id: this.svgID + "-svg-r",
47821                     width: this.width,
47822                     height: this.height,
47823                     fill: "#ffa"
47824                 },
47825                 {
47826                     tag: "line",
47827                     id: this.svgID + "-svg-l",
47828                     x1: "0", // start
47829                     y1: (this.height*0.8), // start set the line in 80% of height
47830                     x2: this.width, // end
47831                     y2: (this.height*0.8), // end set the line in 80% of height
47832                     'stroke': "#666",
47833                     'stroke-width': "1",
47834                     'stroke-dasharray': "3",
47835                     'shape-rendering': "crispEdges",
47836                     'pointer-events': "none"
47837                 },
47838                 {
47839                     tag: "path",
47840                     id: this.svgID + "-svg-p",
47841                     'stroke': "navy",
47842                     'stroke-width': "3",
47843                     'fill': "none",
47844                     'pointer-events': 'none'
47845                 }
47846               ]
47847         });
47848         this.createSVG();
47849         this.svgBox = this.svgEl.dom.getScreenCTM();
47850     },
47851     createSVG : function(){ 
47852         var svg = this.signPanel;
47853         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47854         var t = this;
47855
47856         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47857         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47858         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47859         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47860         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47861         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47862         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47863         
47864     },
47865     isTouchEvent : function(e){
47866         return e.type.match(/^touch/);
47867     },
47868     getCoords : function (e) {
47869         var pt    = this.svgEl.dom.createSVGPoint();
47870         pt.x = e.clientX; 
47871         pt.y = e.clientY;
47872         if (this.isTouchEvent(e)) {
47873             pt.x =  e.targetTouches[0].clientX 
47874             pt.y = e.targetTouches[0].clientY;
47875         }
47876         var a = this.svgEl.dom.getScreenCTM();
47877         var b = a.inverse();
47878         var mx = pt.matrixTransform(b);
47879         return mx.x + ',' + mx.y;
47880     },
47881     //mouse event headler 
47882     down : function (e) {
47883         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47884         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47885         
47886         this.isMouseDown = true;
47887         
47888         e.preventDefault();
47889     },
47890     move : function (e) {
47891         if (this.isMouseDown) {
47892             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47893             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47894         }
47895         
47896         e.preventDefault();
47897     },
47898     up : function (e) {
47899         this.isMouseDown = false;
47900         var sp = this.signatureTmp.split(' ');
47901         
47902         if(sp.length > 1){
47903             if(!sp[sp.length-2].match(/^L/)){
47904                 sp.pop();
47905                 sp.pop();
47906                 sp.push("");
47907                 this.signatureTmp = sp.join(" ");
47908             }
47909         }
47910         if(this.getValue() != this.signatureTmp){
47911             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47912             this.isConfirmed = false;
47913         }
47914         e.preventDefault();
47915     },
47916     
47917     /**
47918      * Protected method that will not generally be called directly. It
47919      * is called when the editor creates its toolbar. Override this method if you need to
47920      * add custom toolbar buttons.
47921      * @param {HtmlEditor} editor
47922      */
47923     createToolbar : function(editor){
47924          function btn(id, toggle, handler){
47925             var xid = fid + '-'+ id ;
47926             return {
47927                 id : xid,
47928                 cmd : id,
47929                 cls : 'x-btn-icon x-edit-'+id,
47930                 enableToggle:toggle !== false,
47931                 scope: editor, // was editor...
47932                 handler:handler||editor.relayBtnCmd,
47933                 clickEvent:'mousedown',
47934                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47935                 tabIndex:-1
47936             };
47937         }
47938         
47939         
47940         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47941         this.tb = tb;
47942         this.tb.add(
47943            {
47944                 cls : ' x-signature-btn x-signature-'+id,
47945                 scope: editor, // was editor...
47946                 handler: this.reset,
47947                 clickEvent:'mousedown',
47948                 text: this.labels.clear
47949             },
47950             {
47951                  xtype : 'Fill',
47952                  xns: Roo.Toolbar
47953             }, 
47954             {
47955                 cls : '  x-signature-btn x-signature-'+id,
47956                 scope: editor, // was editor...
47957                 handler: this.confirmHandler,
47958                 clickEvent:'mousedown',
47959                 text: this.labels.confirm
47960             }
47961         );
47962     
47963     },
47964     //public
47965     /**
47966      * when user is clicked confirm then show this image.....
47967      * 
47968      * @return {String} Image Data URI
47969      */
47970     getImageDataURI : function(){
47971         var svg = this.svgEl.dom.parentNode.innerHTML;
47972         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47973         return src; 
47974     },
47975     /**
47976      * 
47977      * @return {Boolean} this.isConfirmed
47978      */
47979     getConfirmed : function(){
47980         return this.isConfirmed;
47981     },
47982     /**
47983      * 
47984      * @return {Number} this.width
47985      */
47986     getWidth : function(){
47987         return this.width;
47988     },
47989     /**
47990      * 
47991      * @return {Number} this.height
47992      */
47993     getHeight : function(){
47994         return this.height;
47995     },
47996     // private
47997     getSignature : function(){
47998         return this.signatureTmp;
47999     },
48000     // private
48001     reset : function(){
48002         this.signatureTmp = '';
48003         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48004         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48005         this.isConfirmed = false;
48006         Roo.form.Signature.superclass.reset.call(this);
48007     },
48008     setSignature : function(s){
48009         this.signatureTmp = s;
48010         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48011         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48012         this.setValue(s);
48013         this.isConfirmed = false;
48014         Roo.form.Signature.superclass.reset.call(this);
48015     }, 
48016     test : function(){
48017 //        Roo.log(this.signPanel.dom.contentWindow.up())
48018     },
48019     //private
48020     setConfirmed : function(){
48021         
48022         
48023         
48024 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48025     },
48026     // private
48027     confirmHandler : function(){
48028         if(!this.getSignature()){
48029             return;
48030         }
48031         
48032         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48033         this.setValue(this.getSignature());
48034         this.isConfirmed = true;
48035         
48036         this.fireEvent('confirm', this);
48037     },
48038     // private
48039     // Subclasses should provide the validation implementation by overriding this
48040     validateValue : function(value){
48041         if(this.allowBlank){
48042             return true;
48043         }
48044         
48045         if(this.isConfirmed){
48046             return true;
48047         }
48048         return false;
48049     }
48050 });/*
48051  * Based on:
48052  * Ext JS Library 1.1.1
48053  * Copyright(c) 2006-2007, Ext JS, LLC.
48054  *
48055  * Originally Released Under LGPL - original licence link has changed is not relivant.
48056  *
48057  * Fork - LGPL
48058  * <script type="text/javascript">
48059  */
48060  
48061
48062 /**
48063  * @class Roo.form.ComboBox
48064  * @extends Roo.form.TriggerField
48065  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48066  * @constructor
48067  * Create a new ComboBox.
48068  * @param {Object} config Configuration options
48069  */
48070 Roo.form.Select = function(config){
48071     Roo.form.Select.superclass.constructor.call(this, config);
48072      
48073 };
48074
48075 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48076     /**
48077      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48078      */
48079     /**
48080      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48081      * rendering into an Roo.Editor, defaults to false)
48082      */
48083     /**
48084      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48085      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48086      */
48087     /**
48088      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48089      */
48090     /**
48091      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48092      * the dropdown list (defaults to undefined, with no header element)
48093      */
48094
48095      /**
48096      * @cfg {String/Roo.Template} tpl The template to use to render the output
48097      */
48098      
48099     // private
48100     defaultAutoCreate : {tag: "select"  },
48101     /**
48102      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48103      */
48104     listWidth: undefined,
48105     /**
48106      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48107      * mode = 'remote' or 'text' if mode = 'local')
48108      */
48109     displayField: undefined,
48110     /**
48111      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48112      * mode = 'remote' or 'value' if mode = 'local'). 
48113      * Note: use of a valueField requires the user make a selection
48114      * in order for a value to be mapped.
48115      */
48116     valueField: undefined,
48117     
48118     
48119     /**
48120      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48121      * field's data value (defaults to the underlying DOM element's name)
48122      */
48123     hiddenName: undefined,
48124     /**
48125      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48126      */
48127     listClass: '',
48128     /**
48129      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48130      */
48131     selectedClass: 'x-combo-selected',
48132     /**
48133      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48134      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48135      * which displays a downward arrow icon).
48136      */
48137     triggerClass : 'x-form-arrow-trigger',
48138     /**
48139      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48140      */
48141     shadow:'sides',
48142     /**
48143      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48144      * anchor positions (defaults to 'tl-bl')
48145      */
48146     listAlign: 'tl-bl?',
48147     /**
48148      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48149      */
48150     maxHeight: 300,
48151     /**
48152      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48153      * query specified by the allQuery config option (defaults to 'query')
48154      */
48155     triggerAction: 'query',
48156     /**
48157      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48158      * (defaults to 4, does not apply if editable = false)
48159      */
48160     minChars : 4,
48161     /**
48162      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48163      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48164      */
48165     typeAhead: false,
48166     /**
48167      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48168      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48169      */
48170     queryDelay: 500,
48171     /**
48172      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48173      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48174      */
48175     pageSize: 0,
48176     /**
48177      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48178      * when editable = true (defaults to false)
48179      */
48180     selectOnFocus:false,
48181     /**
48182      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48183      */
48184     queryParam: 'query',
48185     /**
48186      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48187      * when mode = 'remote' (defaults to 'Loading...')
48188      */
48189     loadingText: 'Loading...',
48190     /**
48191      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48192      */
48193     resizable: false,
48194     /**
48195      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48196      */
48197     handleHeight : 8,
48198     /**
48199      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48200      * traditional select (defaults to true)
48201      */
48202     editable: true,
48203     /**
48204      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48205      */
48206     allQuery: '',
48207     /**
48208      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48209      */
48210     mode: 'remote',
48211     /**
48212      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48213      * listWidth has a higher value)
48214      */
48215     minListWidth : 70,
48216     /**
48217      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48218      * allow the user to set arbitrary text into the field (defaults to false)
48219      */
48220     forceSelection:false,
48221     /**
48222      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48223      * if typeAhead = true (defaults to 250)
48224      */
48225     typeAheadDelay : 250,
48226     /**
48227      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48228      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48229      */
48230     valueNotFoundText : undefined,
48231     
48232     /**
48233      * @cfg {String} defaultValue The value displayed after loading the store.
48234      */
48235     defaultValue: '',
48236     
48237     /**
48238      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48239      */
48240     blockFocus : false,
48241     
48242     /**
48243      * @cfg {Boolean} disableClear Disable showing of clear button.
48244      */
48245     disableClear : false,
48246     /**
48247      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48248      */
48249     alwaysQuery : false,
48250     
48251     //private
48252     addicon : false,
48253     editicon: false,
48254     
48255     // element that contains real text value.. (when hidden is used..)
48256      
48257     // private
48258     onRender : function(ct, position){
48259         Roo.form.Field.prototype.onRender.call(this, ct, position);
48260         
48261         if(this.store){
48262             this.store.on('beforeload', this.onBeforeLoad, this);
48263             this.store.on('load', this.onLoad, this);
48264             this.store.on('loadexception', this.onLoadException, this);
48265             this.store.load({});
48266         }
48267         
48268         
48269         
48270     },
48271
48272     // private
48273     initEvents : function(){
48274         //Roo.form.ComboBox.superclass.initEvents.call(this);
48275  
48276     },
48277
48278     onDestroy : function(){
48279        
48280         if(this.store){
48281             this.store.un('beforeload', this.onBeforeLoad, this);
48282             this.store.un('load', this.onLoad, this);
48283             this.store.un('loadexception', this.onLoadException, this);
48284         }
48285         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48286     },
48287
48288     // private
48289     fireKey : function(e){
48290         if(e.isNavKeyPress() && !this.list.isVisible()){
48291             this.fireEvent("specialkey", this, e);
48292         }
48293     },
48294
48295     // private
48296     onResize: function(w, h){
48297         
48298         return; 
48299     
48300         
48301     },
48302
48303     /**
48304      * Allow or prevent the user from directly editing the field text.  If false is passed,
48305      * the user will only be able to select from the items defined in the dropdown list.  This method
48306      * is the runtime equivalent of setting the 'editable' config option at config time.
48307      * @param {Boolean} value True to allow the user to directly edit the field text
48308      */
48309     setEditable : function(value){
48310          
48311     },
48312
48313     // private
48314     onBeforeLoad : function(){
48315         
48316         Roo.log("Select before load");
48317         return;
48318     
48319         this.innerList.update(this.loadingText ?
48320                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48321         //this.restrictHeight();
48322         this.selectedIndex = -1;
48323     },
48324
48325     // private
48326     onLoad : function(){
48327
48328     
48329         var dom = this.el.dom;
48330         dom.innerHTML = '';
48331          var od = dom.ownerDocument;
48332          
48333         if (this.emptyText) {
48334             var op = od.createElement('option');
48335             op.setAttribute('value', '');
48336             op.innerHTML = String.format('{0}', this.emptyText);
48337             dom.appendChild(op);
48338         }
48339         if(this.store.getCount() > 0){
48340            
48341             var vf = this.valueField;
48342             var df = this.displayField;
48343             this.store.data.each(function(r) {
48344                 // which colmsn to use... testing - cdoe / title..
48345                 var op = od.createElement('option');
48346                 op.setAttribute('value', r.data[vf]);
48347                 op.innerHTML = String.format('{0}', r.data[df]);
48348                 dom.appendChild(op);
48349             });
48350             if (typeof(this.defaultValue != 'undefined')) {
48351                 this.setValue(this.defaultValue);
48352             }
48353             
48354              
48355         }else{
48356             //this.onEmptyResults();
48357         }
48358         //this.el.focus();
48359     },
48360     // private
48361     onLoadException : function()
48362     {
48363         dom.innerHTML = '';
48364             
48365         Roo.log("Select on load exception");
48366         return;
48367     
48368         this.collapse();
48369         Roo.log(this.store.reader.jsonData);
48370         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48371             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48372         }
48373         
48374         
48375     },
48376     // private
48377     onTypeAhead : function(){
48378          
48379     },
48380
48381     // private
48382     onSelect : function(record, index){
48383         Roo.log('on select?');
48384         return;
48385         if(this.fireEvent('beforeselect', this, record, index) !== false){
48386             this.setFromData(index > -1 ? record.data : false);
48387             this.collapse();
48388             this.fireEvent('select', this, record, index);
48389         }
48390     },
48391
48392     /**
48393      * Returns the currently selected field value or empty string if no value is set.
48394      * @return {String} value The selected value
48395      */
48396     getValue : function(){
48397         var dom = this.el.dom;
48398         this.value = dom.options[dom.selectedIndex].value;
48399         return this.value;
48400         
48401     },
48402
48403     /**
48404      * Clears any text/value currently set in the field
48405      */
48406     clearValue : function(){
48407         this.value = '';
48408         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48409         
48410     },
48411
48412     /**
48413      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48414      * will be displayed in the field.  If the value does not match the data value of an existing item,
48415      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48416      * Otherwise the field will be blank (although the value will still be set).
48417      * @param {String} value The value to match
48418      */
48419     setValue : function(v){
48420         var d = this.el.dom;
48421         for (var i =0; i < d.options.length;i++) {
48422             if (v == d.options[i].value) {
48423                 d.selectedIndex = i;
48424                 this.value = v;
48425                 return;
48426             }
48427         }
48428         this.clearValue();
48429     },
48430     /**
48431      * @property {Object} the last set data for the element
48432      */
48433     
48434     lastData : false,
48435     /**
48436      * Sets the value of the field based on a object which is related to the record format for the store.
48437      * @param {Object} value the value to set as. or false on reset?
48438      */
48439     setFromData : function(o){
48440         Roo.log('setfrom data?');
48441          
48442         
48443         
48444     },
48445     // private
48446     reset : function(){
48447         this.clearValue();
48448     },
48449     // private
48450     findRecord : function(prop, value){
48451         
48452         return false;
48453     
48454         var record;
48455         if(this.store.getCount() > 0){
48456             this.store.each(function(r){
48457                 if(r.data[prop] == value){
48458                     record = r;
48459                     return false;
48460                 }
48461                 return true;
48462             });
48463         }
48464         return record;
48465     },
48466     
48467     getName: function()
48468     {
48469         // returns hidden if it's set..
48470         if (!this.rendered) {return ''};
48471         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48472         
48473     },
48474      
48475
48476     
48477
48478     // private
48479     onEmptyResults : function(){
48480         Roo.log('empty results');
48481         //this.collapse();
48482     },
48483
48484     /**
48485      * Returns true if the dropdown list is expanded, else false.
48486      */
48487     isExpanded : function(){
48488         return false;
48489     },
48490
48491     /**
48492      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48493      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48494      * @param {String} value The data value of the item to select
48495      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48496      * selected item if it is not currently in view (defaults to true)
48497      * @return {Boolean} True if the value matched an item in the list, else false
48498      */
48499     selectByValue : function(v, scrollIntoView){
48500         Roo.log('select By Value');
48501         return false;
48502     
48503         if(v !== undefined && v !== null){
48504             var r = this.findRecord(this.valueField || this.displayField, v);
48505             if(r){
48506                 this.select(this.store.indexOf(r), scrollIntoView);
48507                 return true;
48508             }
48509         }
48510         return false;
48511     },
48512
48513     /**
48514      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48515      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48516      * @param {Number} index The zero-based index of the list item to select
48517      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48518      * selected item if it is not currently in view (defaults to true)
48519      */
48520     select : function(index, scrollIntoView){
48521         Roo.log('select ');
48522         return  ;
48523         
48524         this.selectedIndex = index;
48525         this.view.select(index);
48526         if(scrollIntoView !== false){
48527             var el = this.view.getNode(index);
48528             if(el){
48529                 this.innerList.scrollChildIntoView(el, false);
48530             }
48531         }
48532     },
48533
48534       
48535
48536     // private
48537     validateBlur : function(){
48538         
48539         return;
48540         
48541     },
48542
48543     // private
48544     initQuery : function(){
48545         this.doQuery(this.getRawValue());
48546     },
48547
48548     // private
48549     doForce : function(){
48550         if(this.el.dom.value.length > 0){
48551             this.el.dom.value =
48552                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48553              
48554         }
48555     },
48556
48557     /**
48558      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48559      * query allowing the query action to be canceled if needed.
48560      * @param {String} query The SQL query to execute
48561      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48562      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48563      * saved in the current store (defaults to false)
48564      */
48565     doQuery : function(q, forceAll){
48566         
48567         Roo.log('doQuery?');
48568         if(q === undefined || q === null){
48569             q = '';
48570         }
48571         var qe = {
48572             query: q,
48573             forceAll: forceAll,
48574             combo: this,
48575             cancel:false
48576         };
48577         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48578             return false;
48579         }
48580         q = qe.query;
48581         forceAll = qe.forceAll;
48582         if(forceAll === true || (q.length >= this.minChars)){
48583             if(this.lastQuery != q || this.alwaysQuery){
48584                 this.lastQuery = q;
48585                 if(this.mode == 'local'){
48586                     this.selectedIndex = -1;
48587                     if(forceAll){
48588                         this.store.clearFilter();
48589                     }else{
48590                         this.store.filter(this.displayField, q);
48591                     }
48592                     this.onLoad();
48593                 }else{
48594                     this.store.baseParams[this.queryParam] = q;
48595                     this.store.load({
48596                         params: this.getParams(q)
48597                     });
48598                     this.expand();
48599                 }
48600             }else{
48601                 this.selectedIndex = -1;
48602                 this.onLoad();   
48603             }
48604         }
48605     },
48606
48607     // private
48608     getParams : function(q){
48609         var p = {};
48610         //p[this.queryParam] = q;
48611         if(this.pageSize){
48612             p.start = 0;
48613             p.limit = this.pageSize;
48614         }
48615         return p;
48616     },
48617
48618     /**
48619      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48620      */
48621     collapse : function(){
48622         
48623     },
48624
48625     // private
48626     collapseIf : function(e){
48627         
48628     },
48629
48630     /**
48631      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48632      */
48633     expand : function(){
48634         
48635     } ,
48636
48637     // private
48638      
48639
48640     /** 
48641     * @cfg {Boolean} grow 
48642     * @hide 
48643     */
48644     /** 
48645     * @cfg {Number} growMin 
48646     * @hide 
48647     */
48648     /** 
48649     * @cfg {Number} growMax 
48650     * @hide 
48651     */
48652     /**
48653      * @hide
48654      * @method autoSize
48655      */
48656     
48657     setWidth : function()
48658     {
48659         
48660     },
48661     getResizeEl : function(){
48662         return this.el;
48663     }
48664 });//<script type="text/javasscript">
48665  
48666
48667 /**
48668  * @class Roo.DDView
48669  * A DnD enabled version of Roo.View.
48670  * @param {Element/String} container The Element in which to create the View.
48671  * @param {String} tpl The template string used to create the markup for each element of the View
48672  * @param {Object} config The configuration properties. These include all the config options of
48673  * {@link Roo.View} plus some specific to this class.<br>
48674  * <p>
48675  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48676  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48677  * <p>
48678  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48679 .x-view-drag-insert-above {
48680         border-top:1px dotted #3366cc;
48681 }
48682 .x-view-drag-insert-below {
48683         border-bottom:1px dotted #3366cc;
48684 }
48685 </code></pre>
48686  * 
48687  */
48688  
48689 Roo.DDView = function(container, tpl, config) {
48690     Roo.DDView.superclass.constructor.apply(this, arguments);
48691     this.getEl().setStyle("outline", "0px none");
48692     this.getEl().unselectable();
48693     if (this.dragGroup) {
48694                 this.setDraggable(this.dragGroup.split(","));
48695     }
48696     if (this.dropGroup) {
48697                 this.setDroppable(this.dropGroup.split(","));
48698     }
48699     if (this.deletable) {
48700         this.setDeletable();
48701     }
48702     this.isDirtyFlag = false;
48703         this.addEvents({
48704                 "drop" : true
48705         });
48706 };
48707
48708 Roo.extend(Roo.DDView, Roo.View, {
48709 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48710 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48711 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48712 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48713
48714         isFormField: true,
48715
48716         reset: Roo.emptyFn,
48717         
48718         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48719
48720         validate: function() {
48721                 return true;
48722         },
48723         
48724         destroy: function() {
48725                 this.purgeListeners();
48726                 this.getEl.removeAllListeners();
48727                 this.getEl().remove();
48728                 if (this.dragZone) {
48729                         if (this.dragZone.destroy) {
48730                                 this.dragZone.destroy();
48731                         }
48732                 }
48733                 if (this.dropZone) {
48734                         if (this.dropZone.destroy) {
48735                                 this.dropZone.destroy();
48736                         }
48737                 }
48738         },
48739
48740 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48741         getName: function() {
48742                 return this.name;
48743         },
48744
48745 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48746         setValue: function(v) {
48747                 if (!this.store) {
48748                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48749                 }
48750                 var data = {};
48751                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48752                 this.store.proxy = new Roo.data.MemoryProxy(data);
48753                 this.store.load();
48754         },
48755
48756 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48757         getValue: function() {
48758                 var result = '(';
48759                 this.store.each(function(rec) {
48760                         result += rec.id + ',';
48761                 });
48762                 return result.substr(0, result.length - 1) + ')';
48763         },
48764         
48765         getIds: function() {
48766                 var i = 0, result = new Array(this.store.getCount());
48767                 this.store.each(function(rec) {
48768                         result[i++] = rec.id;
48769                 });
48770                 return result;
48771         },
48772         
48773         isDirty: function() {
48774                 return this.isDirtyFlag;
48775         },
48776
48777 /**
48778  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48779  *      whole Element becomes the target, and this causes the drop gesture to append.
48780  */
48781     getTargetFromEvent : function(e) {
48782                 var target = e.getTarget();
48783                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48784                 target = target.parentNode;
48785                 }
48786                 if (!target) {
48787                         target = this.el.dom.lastChild || this.el.dom;
48788                 }
48789                 return target;
48790     },
48791
48792 /**
48793  *      Create the drag data which consists of an object which has the property "ddel" as
48794  *      the drag proxy element. 
48795  */
48796     getDragData : function(e) {
48797         var target = this.findItemFromChild(e.getTarget());
48798                 if(target) {
48799                         this.handleSelection(e);
48800                         var selNodes = this.getSelectedNodes();
48801             var dragData = {
48802                 source: this,
48803                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48804                 nodes: selNodes,
48805                 records: []
48806                         };
48807                         var selectedIndices = this.getSelectedIndexes();
48808                         for (var i = 0; i < selectedIndices.length; i++) {
48809                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48810                         }
48811                         if (selNodes.length == 1) {
48812                                 dragData.ddel = target.cloneNode(true); // the div element
48813                         } else {
48814                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48815                                 div.className = 'multi-proxy';
48816                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48817                                         div.appendChild(selNodes[i].cloneNode(true));
48818                                 }
48819                                 dragData.ddel = div;
48820                         }
48821             //console.log(dragData)
48822             //console.log(dragData.ddel.innerHTML)
48823                         return dragData;
48824                 }
48825         //console.log('nodragData')
48826                 return false;
48827     },
48828     
48829 /**     Specify to which ddGroup items in this DDView may be dragged. */
48830     setDraggable: function(ddGroup) {
48831         if (ddGroup instanceof Array) {
48832                 Roo.each(ddGroup, this.setDraggable, this);
48833                 return;
48834         }
48835         if (this.dragZone) {
48836                 this.dragZone.addToGroup(ddGroup);
48837         } else {
48838                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48839                                 containerScroll: true,
48840                                 ddGroup: ddGroup 
48841
48842                         });
48843 //                      Draggability implies selection. DragZone's mousedown selects the element.
48844                         if (!this.multiSelect) { this.singleSelect = true; }
48845
48846 //                      Wire the DragZone's handlers up to methods in *this*
48847                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48848                 }
48849     },
48850
48851 /**     Specify from which ddGroup this DDView accepts drops. */
48852     setDroppable: function(ddGroup) {
48853         if (ddGroup instanceof Array) {
48854                 Roo.each(ddGroup, this.setDroppable, this);
48855                 return;
48856         }
48857         if (this.dropZone) {
48858                 this.dropZone.addToGroup(ddGroup);
48859         } else {
48860                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48861                                 containerScroll: true,
48862                                 ddGroup: ddGroup
48863                         });
48864
48865 //                      Wire the DropZone's handlers up to methods in *this*
48866                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48867                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48868                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48869                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48870                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48871                 }
48872     },
48873
48874 /**     Decide whether to drop above or below a View node. */
48875     getDropPoint : function(e, n, dd){
48876         if (n == this.el.dom) { return "above"; }
48877                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48878                 var c = t + (b - t) / 2;
48879                 var y = Roo.lib.Event.getPageY(e);
48880                 if(y <= c) {
48881                         return "above";
48882                 }else{
48883                         return "below";
48884                 }
48885     },
48886
48887     onNodeEnter : function(n, dd, e, data){
48888                 return false;
48889     },
48890     
48891     onNodeOver : function(n, dd, e, data){
48892                 var pt = this.getDropPoint(e, n, dd);
48893                 // set the insert point style on the target node
48894                 var dragElClass = this.dropNotAllowed;
48895                 if (pt) {
48896                         var targetElClass;
48897                         if (pt == "above"){
48898                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48899                                 targetElClass = "x-view-drag-insert-above";
48900                         } else {
48901                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48902                                 targetElClass = "x-view-drag-insert-below";
48903                         }
48904                         if (this.lastInsertClass != targetElClass){
48905                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48906                                 this.lastInsertClass = targetElClass;
48907                         }
48908                 }
48909                 return dragElClass;
48910         },
48911
48912     onNodeOut : function(n, dd, e, data){
48913                 this.removeDropIndicators(n);
48914     },
48915
48916     onNodeDrop : function(n, dd, e, data){
48917         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48918                 return false;
48919         }
48920         var pt = this.getDropPoint(e, n, dd);
48921                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48922                 if (pt == "below") { insertAt++; }
48923                 for (var i = 0; i < data.records.length; i++) {
48924                         var r = data.records[i];
48925                         var dup = this.store.getById(r.id);
48926                         if (dup && (dd != this.dragZone)) {
48927                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48928                         } else {
48929                                 if (data.copy) {
48930                                         this.store.insert(insertAt++, r.copy());
48931                                 } else {
48932                                         data.source.isDirtyFlag = true;
48933                                         r.store.remove(r);
48934                                         this.store.insert(insertAt++, r);
48935                                 }
48936                                 this.isDirtyFlag = true;
48937                         }
48938                 }
48939                 this.dragZone.cachedTarget = null;
48940                 return true;
48941     },
48942
48943     removeDropIndicators : function(n){
48944                 if(n){
48945                         Roo.fly(n).removeClass([
48946                                 "x-view-drag-insert-above",
48947                                 "x-view-drag-insert-below"]);
48948                         this.lastInsertClass = "_noclass";
48949                 }
48950     },
48951
48952 /**
48953  *      Utility method. Add a delete option to the DDView's context menu.
48954  *      @param {String} imageUrl The URL of the "delete" icon image.
48955  */
48956         setDeletable: function(imageUrl) {
48957                 if (!this.singleSelect && !this.multiSelect) {
48958                         this.singleSelect = true;
48959                 }
48960                 var c = this.getContextMenu();
48961                 this.contextMenu.on("itemclick", function(item) {
48962                         switch (item.id) {
48963                                 case "delete":
48964                                         this.remove(this.getSelectedIndexes());
48965                                         break;
48966                         }
48967                 }, this);
48968                 this.contextMenu.add({
48969                         icon: imageUrl,
48970                         id: "delete",
48971                         text: 'Delete'
48972                 });
48973         },
48974         
48975 /**     Return the context menu for this DDView. */
48976         getContextMenu: function() {
48977                 if (!this.contextMenu) {
48978 //                      Create the View's context menu
48979                         this.contextMenu = new Roo.menu.Menu({
48980                                 id: this.id + "-contextmenu"
48981                         });
48982                         this.el.on("contextmenu", this.showContextMenu, this);
48983                 }
48984                 return this.contextMenu;
48985         },
48986         
48987         disableContextMenu: function() {
48988                 if (this.contextMenu) {
48989                         this.el.un("contextmenu", this.showContextMenu, this);
48990                 }
48991         },
48992
48993         showContextMenu: function(e, item) {
48994         item = this.findItemFromChild(e.getTarget());
48995                 if (item) {
48996                         e.stopEvent();
48997                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48998                         this.contextMenu.showAt(e.getXY());
48999             }
49000     },
49001
49002 /**
49003  *      Remove {@link Roo.data.Record}s at the specified indices.
49004  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49005  */
49006     remove: function(selectedIndices) {
49007                 selectedIndices = [].concat(selectedIndices);
49008                 for (var i = 0; i < selectedIndices.length; i++) {
49009                         var rec = this.store.getAt(selectedIndices[i]);
49010                         this.store.remove(rec);
49011                 }
49012     },
49013
49014 /**
49015  *      Double click fires the event, but also, if this is draggable, and there is only one other
49016  *      related DropZone, it transfers the selected node.
49017  */
49018     onDblClick : function(e){
49019         var item = this.findItemFromChild(e.getTarget());
49020         if(item){
49021             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49022                 return false;
49023             }
49024             if (this.dragGroup) {
49025                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49026                     while (targets.indexOf(this.dropZone) > -1) {
49027                             targets.remove(this.dropZone);
49028                                 }
49029                     if (targets.length == 1) {
49030                                         this.dragZone.cachedTarget = null;
49031                         var el = Roo.get(targets[0].getEl());
49032                         var box = el.getBox(true);
49033                         targets[0].onNodeDrop(el.dom, {
49034                                 target: el.dom,
49035                                 xy: [box.x, box.y + box.height - 1]
49036                         }, null, this.getDragData(e));
49037                     }
49038                 }
49039         }
49040     },
49041     
49042     handleSelection: function(e) {
49043                 this.dragZone.cachedTarget = null;
49044         var item = this.findItemFromChild(e.getTarget());
49045         if (!item) {
49046                 this.clearSelections(true);
49047                 return;
49048         }
49049                 if (item && (this.multiSelect || this.singleSelect)){
49050                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49051                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49052                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49053                                 this.unselect(item);
49054                         } else {
49055                                 this.select(item, this.multiSelect && e.ctrlKey);
49056                                 this.lastSelection = item;
49057                         }
49058                 }
49059     },
49060
49061     onItemClick : function(item, index, e){
49062                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49063                         return false;
49064                 }
49065                 return true;
49066     },
49067
49068     unselect : function(nodeInfo, suppressEvent){
49069                 var node = this.getNode(nodeInfo);
49070                 if(node && this.isSelected(node)){
49071                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49072                                 Roo.fly(node).removeClass(this.selectedClass);
49073                                 this.selections.remove(node);
49074                                 if(!suppressEvent){
49075                                         this.fireEvent("selectionchange", this, this.selections);
49076                                 }
49077                         }
49078                 }
49079     }
49080 });
49081 /*
49082  * Based on:
49083  * Ext JS Library 1.1.1
49084  * Copyright(c) 2006-2007, Ext JS, LLC.
49085  *
49086  * Originally Released Under LGPL - original licence link has changed is not relivant.
49087  *
49088  * Fork - LGPL
49089  * <script type="text/javascript">
49090  */
49091  
49092 /**
49093  * @class Roo.LayoutManager
49094  * @extends Roo.util.Observable
49095  * Base class for layout managers.
49096  */
49097 Roo.LayoutManager = function(container, config){
49098     Roo.LayoutManager.superclass.constructor.call(this);
49099     this.el = Roo.get(container);
49100     // ie scrollbar fix
49101     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49102         document.body.scroll = "no";
49103     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49104         this.el.position('relative');
49105     }
49106     this.id = this.el.id;
49107     this.el.addClass("x-layout-container");
49108     /** false to disable window resize monitoring @type Boolean */
49109     this.monitorWindowResize = true;
49110     this.regions = {};
49111     this.addEvents({
49112         /**
49113          * @event layout
49114          * Fires when a layout is performed. 
49115          * @param {Roo.LayoutManager} this
49116          */
49117         "layout" : true,
49118         /**
49119          * @event regionresized
49120          * Fires when the user resizes a region. 
49121          * @param {Roo.LayoutRegion} region The resized region
49122          * @param {Number} newSize The new size (width for east/west, height for north/south)
49123          */
49124         "regionresized" : true,
49125         /**
49126          * @event regioncollapsed
49127          * Fires when a region is collapsed. 
49128          * @param {Roo.LayoutRegion} region The collapsed region
49129          */
49130         "regioncollapsed" : true,
49131         /**
49132          * @event regionexpanded
49133          * Fires when a region is expanded.  
49134          * @param {Roo.LayoutRegion} region The expanded region
49135          */
49136         "regionexpanded" : true
49137     });
49138     this.updating = false;
49139     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49140 };
49141
49142 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49143     /**
49144      * Returns true if this layout is currently being updated
49145      * @return {Boolean}
49146      */
49147     isUpdating : function(){
49148         return this.updating; 
49149     },
49150     
49151     /**
49152      * Suspend the LayoutManager from doing auto-layouts while
49153      * making multiple add or remove calls
49154      */
49155     beginUpdate : function(){
49156         this.updating = true;    
49157     },
49158     
49159     /**
49160      * Restore auto-layouts and optionally disable the manager from performing a layout
49161      * @param {Boolean} noLayout true to disable a layout update 
49162      */
49163     endUpdate : function(noLayout){
49164         this.updating = false;
49165         if(!noLayout){
49166             this.layout();
49167         }    
49168     },
49169     
49170     layout: function(){
49171         
49172     },
49173     
49174     onRegionResized : function(region, newSize){
49175         this.fireEvent("regionresized", region, newSize);
49176         this.layout();
49177     },
49178     
49179     onRegionCollapsed : function(region){
49180         this.fireEvent("regioncollapsed", region);
49181     },
49182     
49183     onRegionExpanded : function(region){
49184         this.fireEvent("regionexpanded", region);
49185     },
49186         
49187     /**
49188      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49189      * performs box-model adjustments.
49190      * @return {Object} The size as an object {width: (the width), height: (the height)}
49191      */
49192     getViewSize : function(){
49193         var size;
49194         if(this.el.dom != document.body){
49195             size = this.el.getSize();
49196         }else{
49197             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49198         }
49199         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49200         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49201         return size;
49202     },
49203     
49204     /**
49205      * Returns the Element this layout is bound to.
49206      * @return {Roo.Element}
49207      */
49208     getEl : function(){
49209         return this.el;
49210     },
49211     
49212     /**
49213      * Returns the specified region.
49214      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49215      * @return {Roo.LayoutRegion}
49216      */
49217     getRegion : function(target){
49218         return this.regions[target.toLowerCase()];
49219     },
49220     
49221     onWindowResize : function(){
49222         if(this.monitorWindowResize){
49223             this.layout();
49224         }
49225     }
49226 });/*
49227  * Based on:
49228  * Ext JS Library 1.1.1
49229  * Copyright(c) 2006-2007, Ext JS, LLC.
49230  *
49231  * Originally Released Under LGPL - original licence link has changed is not relivant.
49232  *
49233  * Fork - LGPL
49234  * <script type="text/javascript">
49235  */
49236 /**
49237  * @class Roo.BorderLayout
49238  * @extends Roo.LayoutManager
49239  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49240  * please see: <br><br>
49241  * <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>
49242  * <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>
49243  * Example:
49244  <pre><code>
49245  var layout = new Roo.BorderLayout(document.body, {
49246     north: {
49247         initialSize: 25,
49248         titlebar: false
49249     },
49250     west: {
49251         split:true,
49252         initialSize: 200,
49253         minSize: 175,
49254         maxSize: 400,
49255         titlebar: true,
49256         collapsible: true
49257     },
49258     east: {
49259         split:true,
49260         initialSize: 202,
49261         minSize: 175,
49262         maxSize: 400,
49263         titlebar: true,
49264         collapsible: true
49265     },
49266     south: {
49267         split:true,
49268         initialSize: 100,
49269         minSize: 100,
49270         maxSize: 200,
49271         titlebar: true,
49272         collapsible: true
49273     },
49274     center: {
49275         titlebar: true,
49276         autoScroll:true,
49277         resizeTabs: true,
49278         minTabWidth: 50,
49279         preferredTabWidth: 150
49280     }
49281 });
49282
49283 // shorthand
49284 var CP = Roo.ContentPanel;
49285
49286 layout.beginUpdate();
49287 layout.add("north", new CP("north", "North"));
49288 layout.add("south", new CP("south", {title: "South", closable: true}));
49289 layout.add("west", new CP("west", {title: "West"}));
49290 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49291 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49292 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49293 layout.getRegion("center").showPanel("center1");
49294 layout.endUpdate();
49295 </code></pre>
49296
49297 <b>The container the layout is rendered into can be either the body element or any other element.
49298 If it is not the body element, the container needs to either be an absolute positioned element,
49299 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49300 the container size if it is not the body element.</b>
49301
49302 * @constructor
49303 * Create a new BorderLayout
49304 * @param {String/HTMLElement/Element} container The container this layout is bound to
49305 * @param {Object} config Configuration options
49306  */
49307 Roo.BorderLayout = function(container, config){
49308     config = config || {};
49309     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49310     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49311     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49312         var target = this.factory.validRegions[i];
49313         if(config[target]){
49314             this.addRegion(target, config[target]);
49315         }
49316     }
49317 };
49318
49319 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49320     /**
49321      * Creates and adds a new region if it doesn't already exist.
49322      * @param {String} target The target region key (north, south, east, west or center).
49323      * @param {Object} config The regions config object
49324      * @return {BorderLayoutRegion} The new region
49325      */
49326     addRegion : function(target, config){
49327         if(!this.regions[target]){
49328             var r = this.factory.create(target, this, config);
49329             this.bindRegion(target, r);
49330         }
49331         return this.regions[target];
49332     },
49333
49334     // private (kinda)
49335     bindRegion : function(name, r){
49336         this.regions[name] = r;
49337         r.on("visibilitychange", this.layout, this);
49338         r.on("paneladded", this.layout, this);
49339         r.on("panelremoved", this.layout, this);
49340         r.on("invalidated", this.layout, this);
49341         r.on("resized", this.onRegionResized, this);
49342         r.on("collapsed", this.onRegionCollapsed, this);
49343         r.on("expanded", this.onRegionExpanded, this);
49344     },
49345
49346     /**
49347      * Performs a layout update.
49348      */
49349     layout : function(){
49350         if(this.updating) return;
49351         var size = this.getViewSize();
49352         var w = size.width;
49353         var h = size.height;
49354         var centerW = w;
49355         var centerH = h;
49356         var centerY = 0;
49357         var centerX = 0;
49358         //var x = 0, y = 0;
49359
49360         var rs = this.regions;
49361         var north = rs["north"];
49362         var south = rs["south"]; 
49363         var west = rs["west"];
49364         var east = rs["east"];
49365         var center = rs["center"];
49366         //if(this.hideOnLayout){ // not supported anymore
49367             //c.el.setStyle("display", "none");
49368         //}
49369         if(north && north.isVisible()){
49370             var b = north.getBox();
49371             var m = north.getMargins();
49372             b.width = w - (m.left+m.right);
49373             b.x = m.left;
49374             b.y = m.top;
49375             centerY = b.height + b.y + m.bottom;
49376             centerH -= centerY;
49377             north.updateBox(this.safeBox(b));
49378         }
49379         if(south && south.isVisible()){
49380             var b = south.getBox();
49381             var m = south.getMargins();
49382             b.width = w - (m.left+m.right);
49383             b.x = m.left;
49384             var totalHeight = (b.height + m.top + m.bottom);
49385             b.y = h - totalHeight + m.top;
49386             centerH -= totalHeight;
49387             south.updateBox(this.safeBox(b));
49388         }
49389         if(west && west.isVisible()){
49390             var b = west.getBox();
49391             var m = west.getMargins();
49392             b.height = centerH - (m.top+m.bottom);
49393             b.x = m.left;
49394             b.y = centerY + m.top;
49395             var totalWidth = (b.width + m.left + m.right);
49396             centerX += totalWidth;
49397             centerW -= totalWidth;
49398             west.updateBox(this.safeBox(b));
49399         }
49400         if(east && east.isVisible()){
49401             var b = east.getBox();
49402             var m = east.getMargins();
49403             b.height = centerH - (m.top+m.bottom);
49404             var totalWidth = (b.width + m.left + m.right);
49405             b.x = w - totalWidth + m.left;
49406             b.y = centerY + m.top;
49407             centerW -= totalWidth;
49408             east.updateBox(this.safeBox(b));
49409         }
49410         if(center){
49411             var m = center.getMargins();
49412             var centerBox = {
49413                 x: centerX + m.left,
49414                 y: centerY + m.top,
49415                 width: centerW - (m.left+m.right),
49416                 height: centerH - (m.top+m.bottom)
49417             };
49418             //if(this.hideOnLayout){
49419                 //center.el.setStyle("display", "block");
49420             //}
49421             center.updateBox(this.safeBox(centerBox));
49422         }
49423         this.el.repaint();
49424         this.fireEvent("layout", this);
49425     },
49426
49427     // private
49428     safeBox : function(box){
49429         box.width = Math.max(0, box.width);
49430         box.height = Math.max(0, box.height);
49431         return box;
49432     },
49433
49434     /**
49435      * Adds a ContentPanel (or subclass) to this layout.
49436      * @param {String} target The target region key (north, south, east, west or center).
49437      * @param {Roo.ContentPanel} panel The panel to add
49438      * @return {Roo.ContentPanel} The added panel
49439      */
49440     add : function(target, panel){
49441          
49442         target = target.toLowerCase();
49443         return this.regions[target].add(panel);
49444     },
49445
49446     /**
49447      * Remove a ContentPanel (or subclass) to this layout.
49448      * @param {String} target The target region key (north, south, east, west or center).
49449      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49450      * @return {Roo.ContentPanel} The removed panel
49451      */
49452     remove : function(target, panel){
49453         target = target.toLowerCase();
49454         return this.regions[target].remove(panel);
49455     },
49456
49457     /**
49458      * Searches all regions for a panel with the specified id
49459      * @param {String} panelId
49460      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49461      */
49462     findPanel : function(panelId){
49463         var rs = this.regions;
49464         for(var target in rs){
49465             if(typeof rs[target] != "function"){
49466                 var p = rs[target].getPanel(panelId);
49467                 if(p){
49468                     return p;
49469                 }
49470             }
49471         }
49472         return null;
49473     },
49474
49475     /**
49476      * Searches all regions for a panel with the specified id and activates (shows) it.
49477      * @param {String/ContentPanel} panelId The panels id or the panel itself
49478      * @return {Roo.ContentPanel} The shown panel or null
49479      */
49480     showPanel : function(panelId) {
49481       var rs = this.regions;
49482       for(var target in rs){
49483          var r = rs[target];
49484          if(typeof r != "function"){
49485             if(r.hasPanel(panelId)){
49486                return r.showPanel(panelId);
49487             }
49488          }
49489       }
49490       return null;
49491    },
49492
49493    /**
49494      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49495      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49496      */
49497     restoreState : function(provider){
49498         if(!provider){
49499             provider = Roo.state.Manager;
49500         }
49501         var sm = new Roo.LayoutStateManager();
49502         sm.init(this, provider);
49503     },
49504
49505     /**
49506      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49507      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49508      * a valid ContentPanel config object.  Example:
49509      * <pre><code>
49510 // Create the main layout
49511 var layout = new Roo.BorderLayout('main-ct', {
49512     west: {
49513         split:true,
49514         minSize: 175,
49515         titlebar: true
49516     },
49517     center: {
49518         title:'Components'
49519     }
49520 }, 'main-ct');
49521
49522 // Create and add multiple ContentPanels at once via configs
49523 layout.batchAdd({
49524    west: {
49525        id: 'source-files',
49526        autoCreate:true,
49527        title:'Ext Source Files',
49528        autoScroll:true,
49529        fitToFrame:true
49530    },
49531    center : {
49532        el: cview,
49533        autoScroll:true,
49534        fitToFrame:true,
49535        toolbar: tb,
49536        resizeEl:'cbody'
49537    }
49538 });
49539 </code></pre>
49540      * @param {Object} regions An object containing ContentPanel configs by region name
49541      */
49542     batchAdd : function(regions){
49543         this.beginUpdate();
49544         for(var rname in regions){
49545             var lr = this.regions[rname];
49546             if(lr){
49547                 this.addTypedPanels(lr, regions[rname]);
49548             }
49549         }
49550         this.endUpdate();
49551     },
49552
49553     // private
49554     addTypedPanels : function(lr, ps){
49555         if(typeof ps == 'string'){
49556             lr.add(new Roo.ContentPanel(ps));
49557         }
49558         else if(ps instanceof Array){
49559             for(var i =0, len = ps.length; i < len; i++){
49560                 this.addTypedPanels(lr, ps[i]);
49561             }
49562         }
49563         else if(!ps.events){ // raw config?
49564             var el = ps.el;
49565             delete ps.el; // prevent conflict
49566             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49567         }
49568         else {  // panel object assumed!
49569             lr.add(ps);
49570         }
49571     },
49572     /**
49573      * Adds a xtype elements to the layout.
49574      * <pre><code>
49575
49576 layout.addxtype({
49577        xtype : 'ContentPanel',
49578        region: 'west',
49579        items: [ .... ]
49580    }
49581 );
49582
49583 layout.addxtype({
49584         xtype : 'NestedLayoutPanel',
49585         region: 'west',
49586         layout: {
49587            center: { },
49588            west: { }   
49589         },
49590         items : [ ... list of content panels or nested layout panels.. ]
49591    }
49592 );
49593 </code></pre>
49594      * @param {Object} cfg Xtype definition of item to add.
49595      */
49596     addxtype : function(cfg)
49597     {
49598         // basically accepts a pannel...
49599         // can accept a layout region..!?!?
49600         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49601         
49602         if (!cfg.xtype.match(/Panel$/)) {
49603             return false;
49604         }
49605         var ret = false;
49606         
49607         if (typeof(cfg.region) == 'undefined') {
49608             Roo.log("Failed to add Panel, region was not set");
49609             Roo.log(cfg);
49610             return false;
49611         }
49612         var region = cfg.region;
49613         delete cfg.region;
49614         
49615           
49616         var xitems = [];
49617         if (cfg.items) {
49618             xitems = cfg.items;
49619             delete cfg.items;
49620         }
49621         var nb = false;
49622         
49623         switch(cfg.xtype) 
49624         {
49625             case 'ContentPanel':  // ContentPanel (el, cfg)
49626             case 'ScrollPanel':  // ContentPanel (el, cfg)
49627             case 'ViewPanel': 
49628                 if(cfg.autoCreate) {
49629                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49630                 } else {
49631                     var el = this.el.createChild();
49632                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49633                 }
49634                 
49635                 this.add(region, ret);
49636                 break;
49637             
49638             
49639             case 'TreePanel': // our new panel!
49640                 cfg.el = this.el.createChild();
49641                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49642                 this.add(region, ret);
49643                 break;
49644             
49645             case 'NestedLayoutPanel': 
49646                 // create a new Layout (which is  a Border Layout...
49647                 var el = this.el.createChild();
49648                 var clayout = cfg.layout;
49649                 delete cfg.layout;
49650                 clayout.items   = clayout.items  || [];
49651                 // replace this exitems with the clayout ones..
49652                 xitems = clayout.items;
49653                  
49654                 
49655                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49656                     cfg.background = false;
49657                 }
49658                 var layout = new Roo.BorderLayout(el, clayout);
49659                 
49660                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49661                 //console.log('adding nested layout panel '  + cfg.toSource());
49662                 this.add(region, ret);
49663                 nb = {}; /// find first...
49664                 break;
49665                 
49666             case 'GridPanel': 
49667             
49668                 // needs grid and region
49669                 
49670                 //var el = this.getRegion(region).el.createChild();
49671                 var el = this.el.createChild();
49672                 // create the grid first...
49673                 
49674                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49675                 delete cfg.grid;
49676                 if (region == 'center' && this.active ) {
49677                     cfg.background = false;
49678                 }
49679                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49680                 
49681                 this.add(region, ret);
49682                 if (cfg.background) {
49683                     ret.on('activate', function(gp) {
49684                         if (!gp.grid.rendered) {
49685                             gp.grid.render();
49686                         }
49687                     });
49688                 } else {
49689                     grid.render();
49690                 }
49691                 break;
49692            
49693            
49694            
49695                 
49696                 
49697                 
49698             default:
49699                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49700                     
49701                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49702                     this.add(region, ret);
49703                 } else {
49704                 
49705                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49706                     return null;
49707                 }
49708                 
49709              // GridPanel (grid, cfg)
49710             
49711         }
49712         this.beginUpdate();
49713         // add children..
49714         var region = '';
49715         var abn = {};
49716         Roo.each(xitems, function(i)  {
49717             region = nb && i.region ? i.region : false;
49718             
49719             var add = ret.addxtype(i);
49720            
49721             if (region) {
49722                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49723                 if (!i.background) {
49724                     abn[region] = nb[region] ;
49725                 }
49726             }
49727             
49728         });
49729         this.endUpdate();
49730
49731         // make the last non-background panel active..
49732         //if (nb) { Roo.log(abn); }
49733         if (nb) {
49734             
49735             for(var r in abn) {
49736                 region = this.getRegion(r);
49737                 if (region) {
49738                     // tried using nb[r], but it does not work..
49739                      
49740                     region.showPanel(abn[r]);
49741                    
49742                 }
49743             }
49744         }
49745         return ret;
49746         
49747     }
49748 });
49749
49750 /**
49751  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49752  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49753  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49754  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49755  * <pre><code>
49756 // shorthand
49757 var CP = Roo.ContentPanel;
49758
49759 var layout = Roo.BorderLayout.create({
49760     north: {
49761         initialSize: 25,
49762         titlebar: false,
49763         panels: [new CP("north", "North")]
49764     },
49765     west: {
49766         split:true,
49767         initialSize: 200,
49768         minSize: 175,
49769         maxSize: 400,
49770         titlebar: true,
49771         collapsible: true,
49772         panels: [new CP("west", {title: "West"})]
49773     },
49774     east: {
49775         split:true,
49776         initialSize: 202,
49777         minSize: 175,
49778         maxSize: 400,
49779         titlebar: true,
49780         collapsible: true,
49781         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49782     },
49783     south: {
49784         split:true,
49785         initialSize: 100,
49786         minSize: 100,
49787         maxSize: 200,
49788         titlebar: true,
49789         collapsible: true,
49790         panels: [new CP("south", {title: "South", closable: true})]
49791     },
49792     center: {
49793         titlebar: true,
49794         autoScroll:true,
49795         resizeTabs: true,
49796         minTabWidth: 50,
49797         preferredTabWidth: 150,
49798         panels: [
49799             new CP("center1", {title: "Close Me", closable: true}),
49800             new CP("center2", {title: "Center Panel", closable: false})
49801         ]
49802     }
49803 }, document.body);
49804
49805 layout.getRegion("center").showPanel("center1");
49806 </code></pre>
49807  * @param config
49808  * @param targetEl
49809  */
49810 Roo.BorderLayout.create = function(config, targetEl){
49811     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49812     layout.beginUpdate();
49813     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49814     for(var j = 0, jlen = regions.length; j < jlen; j++){
49815         var lr = regions[j];
49816         if(layout.regions[lr] && config[lr].panels){
49817             var r = layout.regions[lr];
49818             var ps = config[lr].panels;
49819             layout.addTypedPanels(r, ps);
49820         }
49821     }
49822     layout.endUpdate();
49823     return layout;
49824 };
49825
49826 // private
49827 Roo.BorderLayout.RegionFactory = {
49828     // private
49829     validRegions : ["north","south","east","west","center"],
49830
49831     // private
49832     create : function(target, mgr, config){
49833         target = target.toLowerCase();
49834         if(config.lightweight || config.basic){
49835             return new Roo.BasicLayoutRegion(mgr, config, target);
49836         }
49837         switch(target){
49838             case "north":
49839                 return new Roo.NorthLayoutRegion(mgr, config);
49840             case "south":
49841                 return new Roo.SouthLayoutRegion(mgr, config);
49842             case "east":
49843                 return new Roo.EastLayoutRegion(mgr, config);
49844             case "west":
49845                 return new Roo.WestLayoutRegion(mgr, config);
49846             case "center":
49847                 return new Roo.CenterLayoutRegion(mgr, config);
49848         }
49849         throw 'Layout region "'+target+'" not supported.';
49850     }
49851 };/*
49852  * Based on:
49853  * Ext JS Library 1.1.1
49854  * Copyright(c) 2006-2007, Ext JS, LLC.
49855  *
49856  * Originally Released Under LGPL - original licence link has changed is not relivant.
49857  *
49858  * Fork - LGPL
49859  * <script type="text/javascript">
49860  */
49861  
49862 /**
49863  * @class Roo.BasicLayoutRegion
49864  * @extends Roo.util.Observable
49865  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49866  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49867  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49868  */
49869 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49870     this.mgr = mgr;
49871     this.position  = pos;
49872     this.events = {
49873         /**
49874          * @scope Roo.BasicLayoutRegion
49875          */
49876         
49877         /**
49878          * @event beforeremove
49879          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49880          * @param {Roo.LayoutRegion} this
49881          * @param {Roo.ContentPanel} panel The panel
49882          * @param {Object} e The cancel event object
49883          */
49884         "beforeremove" : true,
49885         /**
49886          * @event invalidated
49887          * Fires when the layout for this region is changed.
49888          * @param {Roo.LayoutRegion} this
49889          */
49890         "invalidated" : true,
49891         /**
49892          * @event visibilitychange
49893          * Fires when this region is shown or hidden 
49894          * @param {Roo.LayoutRegion} this
49895          * @param {Boolean} visibility true or false
49896          */
49897         "visibilitychange" : true,
49898         /**
49899          * @event paneladded
49900          * Fires when a panel is added. 
49901          * @param {Roo.LayoutRegion} this
49902          * @param {Roo.ContentPanel} panel The panel
49903          */
49904         "paneladded" : true,
49905         /**
49906          * @event panelremoved
49907          * Fires when a panel is removed. 
49908          * @param {Roo.LayoutRegion} this
49909          * @param {Roo.ContentPanel} panel The panel
49910          */
49911         "panelremoved" : true,
49912         /**
49913          * @event collapsed
49914          * Fires when this region is collapsed.
49915          * @param {Roo.LayoutRegion} this
49916          */
49917         "collapsed" : true,
49918         /**
49919          * @event expanded
49920          * Fires when this region is expanded.
49921          * @param {Roo.LayoutRegion} this
49922          */
49923         "expanded" : true,
49924         /**
49925          * @event slideshow
49926          * Fires when this region is slid into view.
49927          * @param {Roo.LayoutRegion} this
49928          */
49929         "slideshow" : true,
49930         /**
49931          * @event slidehide
49932          * Fires when this region slides out of view. 
49933          * @param {Roo.LayoutRegion} this
49934          */
49935         "slidehide" : true,
49936         /**
49937          * @event panelactivated
49938          * Fires when a panel is activated. 
49939          * @param {Roo.LayoutRegion} this
49940          * @param {Roo.ContentPanel} panel The activated panel
49941          */
49942         "panelactivated" : true,
49943         /**
49944          * @event resized
49945          * Fires when the user resizes this region. 
49946          * @param {Roo.LayoutRegion} this
49947          * @param {Number} newSize The new size (width for east/west, height for north/south)
49948          */
49949         "resized" : true
49950     };
49951     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49952     this.panels = new Roo.util.MixedCollection();
49953     this.panels.getKey = this.getPanelId.createDelegate(this);
49954     this.box = null;
49955     this.activePanel = null;
49956     // ensure listeners are added...
49957     
49958     if (config.listeners || config.events) {
49959         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49960             listeners : config.listeners || {},
49961             events : config.events || {}
49962         });
49963     }
49964     
49965     if(skipConfig !== true){
49966         this.applyConfig(config);
49967     }
49968 };
49969
49970 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49971     getPanelId : function(p){
49972         return p.getId();
49973     },
49974     
49975     applyConfig : function(config){
49976         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49977         this.config = config;
49978         
49979     },
49980     
49981     /**
49982      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49983      * the width, for horizontal (north, south) the height.
49984      * @param {Number} newSize The new width or height
49985      */
49986     resizeTo : function(newSize){
49987         var el = this.el ? this.el :
49988                  (this.activePanel ? this.activePanel.getEl() : null);
49989         if(el){
49990             switch(this.position){
49991                 case "east":
49992                 case "west":
49993                     el.setWidth(newSize);
49994                     this.fireEvent("resized", this, newSize);
49995                 break;
49996                 case "north":
49997                 case "south":
49998                     el.setHeight(newSize);
49999                     this.fireEvent("resized", this, newSize);
50000                 break;                
50001             }
50002         }
50003     },
50004     
50005     getBox : function(){
50006         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50007     },
50008     
50009     getMargins : function(){
50010         return this.margins;
50011     },
50012     
50013     updateBox : function(box){
50014         this.box = box;
50015         var el = this.activePanel.getEl();
50016         el.dom.style.left = box.x + "px";
50017         el.dom.style.top = box.y + "px";
50018         this.activePanel.setSize(box.width, box.height);
50019     },
50020     
50021     /**
50022      * Returns the container element for this region.
50023      * @return {Roo.Element}
50024      */
50025     getEl : function(){
50026         return this.activePanel;
50027     },
50028     
50029     /**
50030      * Returns true if this region is currently visible.
50031      * @return {Boolean}
50032      */
50033     isVisible : function(){
50034         return this.activePanel ? true : false;
50035     },
50036     
50037     setActivePanel : function(panel){
50038         panel = this.getPanel(panel);
50039         if(this.activePanel && this.activePanel != panel){
50040             this.activePanel.setActiveState(false);
50041             this.activePanel.getEl().setLeftTop(-10000,-10000);
50042         }
50043         this.activePanel = panel;
50044         panel.setActiveState(true);
50045         if(this.box){
50046             panel.setSize(this.box.width, this.box.height);
50047         }
50048         this.fireEvent("panelactivated", this, panel);
50049         this.fireEvent("invalidated");
50050     },
50051     
50052     /**
50053      * Show the specified panel.
50054      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50055      * @return {Roo.ContentPanel} The shown panel or null
50056      */
50057     showPanel : function(panel){
50058         if(panel = this.getPanel(panel)){
50059             this.setActivePanel(panel);
50060         }
50061         return panel;
50062     },
50063     
50064     /**
50065      * Get the active panel for this region.
50066      * @return {Roo.ContentPanel} The active panel or null
50067      */
50068     getActivePanel : function(){
50069         return this.activePanel;
50070     },
50071     
50072     /**
50073      * Add the passed ContentPanel(s)
50074      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50075      * @return {Roo.ContentPanel} The panel added (if only one was added)
50076      */
50077     add : function(panel){
50078         if(arguments.length > 1){
50079             for(var i = 0, len = arguments.length; i < len; i++) {
50080                 this.add(arguments[i]);
50081             }
50082             return null;
50083         }
50084         if(this.hasPanel(panel)){
50085             this.showPanel(panel);
50086             return panel;
50087         }
50088         var el = panel.getEl();
50089         if(el.dom.parentNode != this.mgr.el.dom){
50090             this.mgr.el.dom.appendChild(el.dom);
50091         }
50092         if(panel.setRegion){
50093             panel.setRegion(this);
50094         }
50095         this.panels.add(panel);
50096         el.setStyle("position", "absolute");
50097         if(!panel.background){
50098             this.setActivePanel(panel);
50099             if(this.config.initialSize && this.panels.getCount()==1){
50100                 this.resizeTo(this.config.initialSize);
50101             }
50102         }
50103         this.fireEvent("paneladded", this, panel);
50104         return panel;
50105     },
50106     
50107     /**
50108      * Returns true if the panel is in this region.
50109      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50110      * @return {Boolean}
50111      */
50112     hasPanel : function(panel){
50113         if(typeof panel == "object"){ // must be panel obj
50114             panel = panel.getId();
50115         }
50116         return this.getPanel(panel) ? true : false;
50117     },
50118     
50119     /**
50120      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50121      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50122      * @param {Boolean} preservePanel Overrides the config preservePanel option
50123      * @return {Roo.ContentPanel} The panel that was removed
50124      */
50125     remove : function(panel, preservePanel){
50126         panel = this.getPanel(panel);
50127         if(!panel){
50128             return null;
50129         }
50130         var e = {};
50131         this.fireEvent("beforeremove", this, panel, e);
50132         if(e.cancel === true){
50133             return null;
50134         }
50135         var panelId = panel.getId();
50136         this.panels.removeKey(panelId);
50137         return panel;
50138     },
50139     
50140     /**
50141      * Returns the panel specified or null if it's not in this region.
50142      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50143      * @return {Roo.ContentPanel}
50144      */
50145     getPanel : function(id){
50146         if(typeof id == "object"){ // must be panel obj
50147             return id;
50148         }
50149         return this.panels.get(id);
50150     },
50151     
50152     /**
50153      * Returns this regions position (north/south/east/west/center).
50154      * @return {String} 
50155      */
50156     getPosition: function(){
50157         return this.position;    
50158     }
50159 });/*
50160  * Based on:
50161  * Ext JS Library 1.1.1
50162  * Copyright(c) 2006-2007, Ext JS, LLC.
50163  *
50164  * Originally Released Under LGPL - original licence link has changed is not relivant.
50165  *
50166  * Fork - LGPL
50167  * <script type="text/javascript">
50168  */
50169  
50170 /**
50171  * @class Roo.LayoutRegion
50172  * @extends Roo.BasicLayoutRegion
50173  * This class represents a region in a layout manager.
50174  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50175  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50176  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50177  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50178  * @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})
50179  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50180  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50181  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50182  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50183  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50184  * @cfg {String}    title           The title for the region (overrides panel titles)
50185  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50186  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50187  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50188  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50189  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50190  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50191  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50192  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50193  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50194  * @cfg {Boolean}   showPin         True to show a pin button
50195  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50196  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50197  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50198  * @cfg {Number}    width           For East/West panels
50199  * @cfg {Number}    height          For North/South panels
50200  * @cfg {Boolean}   split           To show the splitter
50201  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50202  */
50203 Roo.LayoutRegion = function(mgr, config, pos){
50204     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50205     var dh = Roo.DomHelper;
50206     /** This region's container element 
50207     * @type Roo.Element */
50208     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50209     /** This region's title element 
50210     * @type Roo.Element */
50211
50212     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50213         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50214         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50215     ]}, true);
50216     this.titleEl.enableDisplayMode();
50217     /** This region's title text element 
50218     * @type HTMLElement */
50219     this.titleTextEl = this.titleEl.dom.firstChild;
50220     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50221     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50222     this.closeBtn.enableDisplayMode();
50223     this.closeBtn.on("click", this.closeClicked, this);
50224     this.closeBtn.hide();
50225
50226     this.createBody(config);
50227     this.visible = true;
50228     this.collapsed = false;
50229
50230     if(config.hideWhenEmpty){
50231         this.hide();
50232         this.on("paneladded", this.validateVisibility, this);
50233         this.on("panelremoved", this.validateVisibility, this);
50234     }
50235     this.applyConfig(config);
50236 };
50237
50238 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50239
50240     createBody : function(){
50241         /** This region's body element 
50242         * @type Roo.Element */
50243         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50244     },
50245
50246     applyConfig : function(c){
50247         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50248             var dh = Roo.DomHelper;
50249             if(c.titlebar !== false){
50250                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50251                 this.collapseBtn.on("click", this.collapse, this);
50252                 this.collapseBtn.enableDisplayMode();
50253
50254                 if(c.showPin === true || this.showPin){
50255                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50256                     this.stickBtn.enableDisplayMode();
50257                     this.stickBtn.on("click", this.expand, this);
50258                     this.stickBtn.hide();
50259                 }
50260             }
50261             /** This region's collapsed element
50262             * @type Roo.Element */
50263             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50264                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50265             ]}, true);
50266             if(c.floatable !== false){
50267                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50268                this.collapsedEl.on("click", this.collapseClick, this);
50269             }
50270
50271             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50272                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50273                    id: "message", unselectable: "on", style:{"float":"left"}});
50274                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50275              }
50276             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50277             this.expandBtn.on("click", this.expand, this);
50278         }
50279         if(this.collapseBtn){
50280             this.collapseBtn.setVisible(c.collapsible == true);
50281         }
50282         this.cmargins = c.cmargins || this.cmargins ||
50283                          (this.position == "west" || this.position == "east" ?
50284                              {top: 0, left: 2, right:2, bottom: 0} :
50285                              {top: 2, left: 0, right:0, bottom: 2});
50286         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50287         this.bottomTabs = c.tabPosition != "top";
50288         this.autoScroll = c.autoScroll || false;
50289         if(this.autoScroll){
50290             this.bodyEl.setStyle("overflow", "auto");
50291         }else{
50292             this.bodyEl.setStyle("overflow", "hidden");
50293         }
50294         //if(c.titlebar !== false){
50295             if((!c.titlebar && !c.title) || c.titlebar === false){
50296                 this.titleEl.hide();
50297             }else{
50298                 this.titleEl.show();
50299                 if(c.title){
50300                     this.titleTextEl.innerHTML = c.title;
50301                 }
50302             }
50303         //}
50304         this.duration = c.duration || .30;
50305         this.slideDuration = c.slideDuration || .45;
50306         this.config = c;
50307         if(c.collapsed){
50308             this.collapse(true);
50309         }
50310         if(c.hidden){
50311             this.hide();
50312         }
50313     },
50314     /**
50315      * Returns true if this region is currently visible.
50316      * @return {Boolean}
50317      */
50318     isVisible : function(){
50319         return this.visible;
50320     },
50321
50322     /**
50323      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50324      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50325      */
50326     setCollapsedTitle : function(title){
50327         title = title || "&#160;";
50328         if(this.collapsedTitleTextEl){
50329             this.collapsedTitleTextEl.innerHTML = title;
50330         }
50331     },
50332
50333     getBox : function(){
50334         var b;
50335         if(!this.collapsed){
50336             b = this.el.getBox(false, true);
50337         }else{
50338             b = this.collapsedEl.getBox(false, true);
50339         }
50340         return b;
50341     },
50342
50343     getMargins : function(){
50344         return this.collapsed ? this.cmargins : this.margins;
50345     },
50346
50347     highlight : function(){
50348         this.el.addClass("x-layout-panel-dragover");
50349     },
50350
50351     unhighlight : function(){
50352         this.el.removeClass("x-layout-panel-dragover");
50353     },
50354
50355     updateBox : function(box){
50356         this.box = box;
50357         if(!this.collapsed){
50358             this.el.dom.style.left = box.x + "px";
50359             this.el.dom.style.top = box.y + "px";
50360             this.updateBody(box.width, box.height);
50361         }else{
50362             this.collapsedEl.dom.style.left = box.x + "px";
50363             this.collapsedEl.dom.style.top = box.y + "px";
50364             this.collapsedEl.setSize(box.width, box.height);
50365         }
50366         if(this.tabs){
50367             this.tabs.autoSizeTabs();
50368         }
50369     },
50370
50371     updateBody : function(w, h){
50372         if(w !== null){
50373             this.el.setWidth(w);
50374             w -= this.el.getBorderWidth("rl");
50375             if(this.config.adjustments){
50376                 w += this.config.adjustments[0];
50377             }
50378         }
50379         if(h !== null){
50380             this.el.setHeight(h);
50381             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50382             h -= this.el.getBorderWidth("tb");
50383             if(this.config.adjustments){
50384                 h += this.config.adjustments[1];
50385             }
50386             this.bodyEl.setHeight(h);
50387             if(this.tabs){
50388                 h = this.tabs.syncHeight(h);
50389             }
50390         }
50391         if(this.panelSize){
50392             w = w !== null ? w : this.panelSize.width;
50393             h = h !== null ? h : this.panelSize.height;
50394         }
50395         if(this.activePanel){
50396             var el = this.activePanel.getEl();
50397             w = w !== null ? w : el.getWidth();
50398             h = h !== null ? h : el.getHeight();
50399             this.panelSize = {width: w, height: h};
50400             this.activePanel.setSize(w, h);
50401         }
50402         if(Roo.isIE && this.tabs){
50403             this.tabs.el.repaint();
50404         }
50405     },
50406
50407     /**
50408      * Returns the container element for this region.
50409      * @return {Roo.Element}
50410      */
50411     getEl : function(){
50412         return this.el;
50413     },
50414
50415     /**
50416      * Hides this region.
50417      */
50418     hide : function(){
50419         if(!this.collapsed){
50420             this.el.dom.style.left = "-2000px";
50421             this.el.hide();
50422         }else{
50423             this.collapsedEl.dom.style.left = "-2000px";
50424             this.collapsedEl.hide();
50425         }
50426         this.visible = false;
50427         this.fireEvent("visibilitychange", this, false);
50428     },
50429
50430     /**
50431      * Shows this region if it was previously hidden.
50432      */
50433     show : function(){
50434         if(!this.collapsed){
50435             this.el.show();
50436         }else{
50437             this.collapsedEl.show();
50438         }
50439         this.visible = true;
50440         this.fireEvent("visibilitychange", this, true);
50441     },
50442
50443     closeClicked : function(){
50444         if(this.activePanel){
50445             this.remove(this.activePanel);
50446         }
50447     },
50448
50449     collapseClick : function(e){
50450         if(this.isSlid){
50451            e.stopPropagation();
50452            this.slideIn();
50453         }else{
50454            e.stopPropagation();
50455            this.slideOut();
50456         }
50457     },
50458
50459     /**
50460      * Collapses this region.
50461      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50462      */
50463     collapse : function(skipAnim){
50464         if(this.collapsed) return;
50465         this.collapsed = true;
50466         if(this.split){
50467             this.split.el.hide();
50468         }
50469         if(this.config.animate && skipAnim !== true){
50470             this.fireEvent("invalidated", this);
50471             this.animateCollapse();
50472         }else{
50473             this.el.setLocation(-20000,-20000);
50474             this.el.hide();
50475             this.collapsedEl.show();
50476             this.fireEvent("collapsed", this);
50477             this.fireEvent("invalidated", this);
50478         }
50479     },
50480
50481     animateCollapse : function(){
50482         // overridden
50483     },
50484
50485     /**
50486      * Expands this region if it was previously collapsed.
50487      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50488      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50489      */
50490     expand : function(e, skipAnim){
50491         if(e) e.stopPropagation();
50492         if(!this.collapsed || this.el.hasActiveFx()) return;
50493         if(this.isSlid){
50494             this.afterSlideIn();
50495             skipAnim = true;
50496         }
50497         this.collapsed = false;
50498         if(this.config.animate && skipAnim !== true){
50499             this.animateExpand();
50500         }else{
50501             this.el.show();
50502             if(this.split){
50503                 this.split.el.show();
50504             }
50505             this.collapsedEl.setLocation(-2000,-2000);
50506             this.collapsedEl.hide();
50507             this.fireEvent("invalidated", this);
50508             this.fireEvent("expanded", this);
50509         }
50510     },
50511
50512     animateExpand : function(){
50513         // overridden
50514     },
50515
50516     initTabs : function()
50517     {
50518         this.bodyEl.setStyle("overflow", "hidden");
50519         var ts = new Roo.TabPanel(
50520                 this.bodyEl.dom,
50521                 {
50522                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50523                     disableTooltips: this.config.disableTabTips,
50524                     toolbar : this.config.toolbar
50525                 }
50526         );
50527         if(this.config.hideTabs){
50528             ts.stripWrap.setDisplayed(false);
50529         }
50530         this.tabs = ts;
50531         ts.resizeTabs = this.config.resizeTabs === true;
50532         ts.minTabWidth = this.config.minTabWidth || 40;
50533         ts.maxTabWidth = this.config.maxTabWidth || 250;
50534         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50535         ts.monitorResize = false;
50536         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50537         ts.bodyEl.addClass('x-layout-tabs-body');
50538         this.panels.each(this.initPanelAsTab, this);
50539     },
50540
50541     initPanelAsTab : function(panel){
50542         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50543                     this.config.closeOnTab && panel.isClosable());
50544         if(panel.tabTip !== undefined){
50545             ti.setTooltip(panel.tabTip);
50546         }
50547         ti.on("activate", function(){
50548               this.setActivePanel(panel);
50549         }, this);
50550         if(this.config.closeOnTab){
50551             ti.on("beforeclose", function(t, e){
50552                 e.cancel = true;
50553                 this.remove(panel);
50554             }, this);
50555         }
50556         return ti;
50557     },
50558
50559     updatePanelTitle : function(panel, title){
50560         if(this.activePanel == panel){
50561             this.updateTitle(title);
50562         }
50563         if(this.tabs){
50564             var ti = this.tabs.getTab(panel.getEl().id);
50565             ti.setText(title);
50566             if(panel.tabTip !== undefined){
50567                 ti.setTooltip(panel.tabTip);
50568             }
50569         }
50570     },
50571
50572     updateTitle : function(title){
50573         if(this.titleTextEl && !this.config.title){
50574             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50575         }
50576     },
50577
50578     setActivePanel : function(panel){
50579         panel = this.getPanel(panel);
50580         if(this.activePanel && this.activePanel != panel){
50581             this.activePanel.setActiveState(false);
50582         }
50583         this.activePanel = panel;
50584         panel.setActiveState(true);
50585         if(this.panelSize){
50586             panel.setSize(this.panelSize.width, this.panelSize.height);
50587         }
50588         if(this.closeBtn){
50589             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50590         }
50591         this.updateTitle(panel.getTitle());
50592         if(this.tabs){
50593             this.fireEvent("invalidated", this);
50594         }
50595         this.fireEvent("panelactivated", this, panel);
50596     },
50597
50598     /**
50599      * Shows the specified panel.
50600      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50601      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50602      */
50603     showPanel : function(panel)
50604     {
50605         panel = this.getPanel(panel);
50606         if(panel){
50607             if(this.tabs){
50608                 var tab = this.tabs.getTab(panel.getEl().id);
50609                 if(tab.isHidden()){
50610                     this.tabs.unhideTab(tab.id);
50611                 }
50612                 tab.activate();
50613             }else{
50614                 this.setActivePanel(panel);
50615             }
50616         }
50617         return panel;
50618     },
50619
50620     /**
50621      * Get the active panel for this region.
50622      * @return {Roo.ContentPanel} The active panel or null
50623      */
50624     getActivePanel : function(){
50625         return this.activePanel;
50626     },
50627
50628     validateVisibility : function(){
50629         if(this.panels.getCount() < 1){
50630             this.updateTitle("&#160;");
50631             this.closeBtn.hide();
50632             this.hide();
50633         }else{
50634             if(!this.isVisible()){
50635                 this.show();
50636             }
50637         }
50638     },
50639
50640     /**
50641      * Adds the passed ContentPanel(s) to this region.
50642      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50643      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50644      */
50645     add : function(panel){
50646         if(arguments.length > 1){
50647             for(var i = 0, len = arguments.length; i < len; i++) {
50648                 this.add(arguments[i]);
50649             }
50650             return null;
50651         }
50652         if(this.hasPanel(panel)){
50653             this.showPanel(panel);
50654             return panel;
50655         }
50656         panel.setRegion(this);
50657         this.panels.add(panel);
50658         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50659             this.bodyEl.dom.appendChild(panel.getEl().dom);
50660             if(panel.background !== true){
50661                 this.setActivePanel(panel);
50662             }
50663             this.fireEvent("paneladded", this, panel);
50664             return panel;
50665         }
50666         if(!this.tabs){
50667             this.initTabs();
50668         }else{
50669             this.initPanelAsTab(panel);
50670         }
50671         if(panel.background !== true){
50672             this.tabs.activate(panel.getEl().id);
50673         }
50674         this.fireEvent("paneladded", this, panel);
50675         return panel;
50676     },
50677
50678     /**
50679      * Hides the tab for the specified panel.
50680      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50681      */
50682     hidePanel : function(panel){
50683         if(this.tabs && (panel = this.getPanel(panel))){
50684             this.tabs.hideTab(panel.getEl().id);
50685         }
50686     },
50687
50688     /**
50689      * Unhides the tab for a previously hidden panel.
50690      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50691      */
50692     unhidePanel : function(panel){
50693         if(this.tabs && (panel = this.getPanel(panel))){
50694             this.tabs.unhideTab(panel.getEl().id);
50695         }
50696     },
50697
50698     clearPanels : function(){
50699         while(this.panels.getCount() > 0){
50700              this.remove(this.panels.first());
50701         }
50702     },
50703
50704     /**
50705      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50706      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50707      * @param {Boolean} preservePanel Overrides the config preservePanel option
50708      * @return {Roo.ContentPanel} The panel that was removed
50709      */
50710     remove : function(panel, preservePanel){
50711         panel = this.getPanel(panel);
50712         if(!panel){
50713             return null;
50714         }
50715         var e = {};
50716         this.fireEvent("beforeremove", this, panel, e);
50717         if(e.cancel === true){
50718             return null;
50719         }
50720         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50721         var panelId = panel.getId();
50722         this.panels.removeKey(panelId);
50723         if(preservePanel){
50724             document.body.appendChild(panel.getEl().dom);
50725         }
50726         if(this.tabs){
50727             this.tabs.removeTab(panel.getEl().id);
50728         }else if (!preservePanel){
50729             this.bodyEl.dom.removeChild(panel.getEl().dom);
50730         }
50731         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50732             var p = this.panels.first();
50733             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50734             tempEl.appendChild(p.getEl().dom);
50735             this.bodyEl.update("");
50736             this.bodyEl.dom.appendChild(p.getEl().dom);
50737             tempEl = null;
50738             this.updateTitle(p.getTitle());
50739             this.tabs = null;
50740             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50741             this.setActivePanel(p);
50742         }
50743         panel.setRegion(null);
50744         if(this.activePanel == panel){
50745             this.activePanel = null;
50746         }
50747         if(this.config.autoDestroy !== false && preservePanel !== true){
50748             try{panel.destroy();}catch(e){}
50749         }
50750         this.fireEvent("panelremoved", this, panel);
50751         return panel;
50752     },
50753
50754     /**
50755      * Returns the TabPanel component used by this region
50756      * @return {Roo.TabPanel}
50757      */
50758     getTabs : function(){
50759         return this.tabs;
50760     },
50761
50762     createTool : function(parentEl, className){
50763         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50764             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50765         btn.addClassOnOver("x-layout-tools-button-over");
50766         return btn;
50767     }
50768 });/*
50769  * Based on:
50770  * Ext JS Library 1.1.1
50771  * Copyright(c) 2006-2007, Ext JS, LLC.
50772  *
50773  * Originally Released Under LGPL - original licence link has changed is not relivant.
50774  *
50775  * Fork - LGPL
50776  * <script type="text/javascript">
50777  */
50778  
50779
50780
50781 /**
50782  * @class Roo.SplitLayoutRegion
50783  * @extends Roo.LayoutRegion
50784  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50785  */
50786 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50787     this.cursor = cursor;
50788     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50789 };
50790
50791 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50792     splitTip : "Drag to resize.",
50793     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50794     useSplitTips : false,
50795
50796     applyConfig : function(config){
50797         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50798         if(config.split){
50799             if(!this.split){
50800                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50801                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50802                 /** The SplitBar for this region 
50803                 * @type Roo.SplitBar */
50804                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50805                 this.split.on("moved", this.onSplitMove, this);
50806                 this.split.useShim = config.useShim === true;
50807                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50808                 if(this.useSplitTips){
50809                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50810                 }
50811                 if(config.collapsible){
50812                     this.split.el.on("dblclick", this.collapse,  this);
50813                 }
50814             }
50815             if(typeof config.minSize != "undefined"){
50816                 this.split.minSize = config.minSize;
50817             }
50818             if(typeof config.maxSize != "undefined"){
50819                 this.split.maxSize = config.maxSize;
50820             }
50821             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50822                 this.hideSplitter();
50823             }
50824         }
50825     },
50826
50827     getHMaxSize : function(){
50828          var cmax = this.config.maxSize || 10000;
50829          var center = this.mgr.getRegion("center");
50830          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50831     },
50832
50833     getVMaxSize : function(){
50834          var cmax = this.config.maxSize || 10000;
50835          var center = this.mgr.getRegion("center");
50836          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50837     },
50838
50839     onSplitMove : function(split, newSize){
50840         this.fireEvent("resized", this, newSize);
50841     },
50842     
50843     /** 
50844      * Returns the {@link Roo.SplitBar} for this region.
50845      * @return {Roo.SplitBar}
50846      */
50847     getSplitBar : function(){
50848         return this.split;
50849     },
50850     
50851     hide : function(){
50852         this.hideSplitter();
50853         Roo.SplitLayoutRegion.superclass.hide.call(this);
50854     },
50855
50856     hideSplitter : function(){
50857         if(this.split){
50858             this.split.el.setLocation(-2000,-2000);
50859             this.split.el.hide();
50860         }
50861     },
50862
50863     show : function(){
50864         if(this.split){
50865             this.split.el.show();
50866         }
50867         Roo.SplitLayoutRegion.superclass.show.call(this);
50868     },
50869     
50870     beforeSlide: function(){
50871         if(Roo.isGecko){// firefox overflow auto bug workaround
50872             this.bodyEl.clip();
50873             if(this.tabs) this.tabs.bodyEl.clip();
50874             if(this.activePanel){
50875                 this.activePanel.getEl().clip();
50876                 
50877                 if(this.activePanel.beforeSlide){
50878                     this.activePanel.beforeSlide();
50879                 }
50880             }
50881         }
50882     },
50883     
50884     afterSlide : function(){
50885         if(Roo.isGecko){// firefox overflow auto bug workaround
50886             this.bodyEl.unclip();
50887             if(this.tabs) this.tabs.bodyEl.unclip();
50888             if(this.activePanel){
50889                 this.activePanel.getEl().unclip();
50890                 if(this.activePanel.afterSlide){
50891                     this.activePanel.afterSlide();
50892                 }
50893             }
50894         }
50895     },
50896
50897     initAutoHide : function(){
50898         if(this.autoHide !== false){
50899             if(!this.autoHideHd){
50900                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50901                 this.autoHideHd = {
50902                     "mouseout": function(e){
50903                         if(!e.within(this.el, true)){
50904                             st.delay(500);
50905                         }
50906                     },
50907                     "mouseover" : function(e){
50908                         st.cancel();
50909                     },
50910                     scope : this
50911                 };
50912             }
50913             this.el.on(this.autoHideHd);
50914         }
50915     },
50916
50917     clearAutoHide : function(){
50918         if(this.autoHide !== false){
50919             this.el.un("mouseout", this.autoHideHd.mouseout);
50920             this.el.un("mouseover", this.autoHideHd.mouseover);
50921         }
50922     },
50923
50924     clearMonitor : function(){
50925         Roo.get(document).un("click", this.slideInIf, this);
50926     },
50927
50928     // these names are backwards but not changed for compat
50929     slideOut : function(){
50930         if(this.isSlid || this.el.hasActiveFx()){
50931             return;
50932         }
50933         this.isSlid = true;
50934         if(this.collapseBtn){
50935             this.collapseBtn.hide();
50936         }
50937         this.closeBtnState = this.closeBtn.getStyle('display');
50938         this.closeBtn.hide();
50939         if(this.stickBtn){
50940             this.stickBtn.show();
50941         }
50942         this.el.show();
50943         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50944         this.beforeSlide();
50945         this.el.setStyle("z-index", 10001);
50946         this.el.slideIn(this.getSlideAnchor(), {
50947             callback: function(){
50948                 this.afterSlide();
50949                 this.initAutoHide();
50950                 Roo.get(document).on("click", this.slideInIf, this);
50951                 this.fireEvent("slideshow", this);
50952             },
50953             scope: this,
50954             block: true
50955         });
50956     },
50957
50958     afterSlideIn : function(){
50959         this.clearAutoHide();
50960         this.isSlid = false;
50961         this.clearMonitor();
50962         this.el.setStyle("z-index", "");
50963         if(this.collapseBtn){
50964             this.collapseBtn.show();
50965         }
50966         this.closeBtn.setStyle('display', this.closeBtnState);
50967         if(this.stickBtn){
50968             this.stickBtn.hide();
50969         }
50970         this.fireEvent("slidehide", this);
50971     },
50972
50973     slideIn : function(cb){
50974         if(!this.isSlid || this.el.hasActiveFx()){
50975             Roo.callback(cb);
50976             return;
50977         }
50978         this.isSlid = false;
50979         this.beforeSlide();
50980         this.el.slideOut(this.getSlideAnchor(), {
50981             callback: function(){
50982                 this.el.setLeftTop(-10000, -10000);
50983                 this.afterSlide();
50984                 this.afterSlideIn();
50985                 Roo.callback(cb);
50986             },
50987             scope: this,
50988             block: true
50989         });
50990     },
50991     
50992     slideInIf : function(e){
50993         if(!e.within(this.el)){
50994             this.slideIn();
50995         }
50996     },
50997
50998     animateCollapse : function(){
50999         this.beforeSlide();
51000         this.el.setStyle("z-index", 20000);
51001         var anchor = this.getSlideAnchor();
51002         this.el.slideOut(anchor, {
51003             callback : function(){
51004                 this.el.setStyle("z-index", "");
51005                 this.collapsedEl.slideIn(anchor, {duration:.3});
51006                 this.afterSlide();
51007                 this.el.setLocation(-10000,-10000);
51008                 this.el.hide();
51009                 this.fireEvent("collapsed", this);
51010             },
51011             scope: this,
51012             block: true
51013         });
51014     },
51015
51016     animateExpand : function(){
51017         this.beforeSlide();
51018         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51019         this.el.setStyle("z-index", 20000);
51020         this.collapsedEl.hide({
51021             duration:.1
51022         });
51023         this.el.slideIn(this.getSlideAnchor(), {
51024             callback : function(){
51025                 this.el.setStyle("z-index", "");
51026                 this.afterSlide();
51027                 if(this.split){
51028                     this.split.el.show();
51029                 }
51030                 this.fireEvent("invalidated", this);
51031                 this.fireEvent("expanded", this);
51032             },
51033             scope: this,
51034             block: true
51035         });
51036     },
51037
51038     anchors : {
51039         "west" : "left",
51040         "east" : "right",
51041         "north" : "top",
51042         "south" : "bottom"
51043     },
51044
51045     sanchors : {
51046         "west" : "l",
51047         "east" : "r",
51048         "north" : "t",
51049         "south" : "b"
51050     },
51051
51052     canchors : {
51053         "west" : "tl-tr",
51054         "east" : "tr-tl",
51055         "north" : "tl-bl",
51056         "south" : "bl-tl"
51057     },
51058
51059     getAnchor : function(){
51060         return this.anchors[this.position];
51061     },
51062
51063     getCollapseAnchor : function(){
51064         return this.canchors[this.position];
51065     },
51066
51067     getSlideAnchor : function(){
51068         return this.sanchors[this.position];
51069     },
51070
51071     getAlignAdj : function(){
51072         var cm = this.cmargins;
51073         switch(this.position){
51074             case "west":
51075                 return [0, 0];
51076             break;
51077             case "east":
51078                 return [0, 0];
51079             break;
51080             case "north":
51081                 return [0, 0];
51082             break;
51083             case "south":
51084                 return [0, 0];
51085             break;
51086         }
51087     },
51088
51089     getExpandAdj : function(){
51090         var c = this.collapsedEl, cm = this.cmargins;
51091         switch(this.position){
51092             case "west":
51093                 return [-(cm.right+c.getWidth()+cm.left), 0];
51094             break;
51095             case "east":
51096                 return [cm.right+c.getWidth()+cm.left, 0];
51097             break;
51098             case "north":
51099                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51100             break;
51101             case "south":
51102                 return [0, cm.top+cm.bottom+c.getHeight()];
51103             break;
51104         }
51105     }
51106 });/*
51107  * Based on:
51108  * Ext JS Library 1.1.1
51109  * Copyright(c) 2006-2007, Ext JS, LLC.
51110  *
51111  * Originally Released Under LGPL - original licence link has changed is not relivant.
51112  *
51113  * Fork - LGPL
51114  * <script type="text/javascript">
51115  */
51116 /*
51117  * These classes are private internal classes
51118  */
51119 Roo.CenterLayoutRegion = function(mgr, config){
51120     Roo.LayoutRegion.call(this, mgr, config, "center");
51121     this.visible = true;
51122     this.minWidth = config.minWidth || 20;
51123     this.minHeight = config.minHeight || 20;
51124 };
51125
51126 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51127     hide : function(){
51128         // center panel can't be hidden
51129     },
51130     
51131     show : function(){
51132         // center panel can't be hidden
51133     },
51134     
51135     getMinWidth: function(){
51136         return this.minWidth;
51137     },
51138     
51139     getMinHeight: function(){
51140         return this.minHeight;
51141     }
51142 });
51143
51144
51145 Roo.NorthLayoutRegion = function(mgr, config){
51146     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51147     if(this.split){
51148         this.split.placement = Roo.SplitBar.TOP;
51149         this.split.orientation = Roo.SplitBar.VERTICAL;
51150         this.split.el.addClass("x-layout-split-v");
51151     }
51152     var size = config.initialSize || config.height;
51153     if(typeof size != "undefined"){
51154         this.el.setHeight(size);
51155     }
51156 };
51157 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51158     orientation: Roo.SplitBar.VERTICAL,
51159     getBox : function(){
51160         if(this.collapsed){
51161             return this.collapsedEl.getBox();
51162         }
51163         var box = this.el.getBox();
51164         if(this.split){
51165             box.height += this.split.el.getHeight();
51166         }
51167         return box;
51168     },
51169     
51170     updateBox : function(box){
51171         if(this.split && !this.collapsed){
51172             box.height -= this.split.el.getHeight();
51173             this.split.el.setLeft(box.x);
51174             this.split.el.setTop(box.y+box.height);
51175             this.split.el.setWidth(box.width);
51176         }
51177         if(this.collapsed){
51178             this.updateBody(box.width, null);
51179         }
51180         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51181     }
51182 });
51183
51184 Roo.SouthLayoutRegion = function(mgr, config){
51185     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51186     if(this.split){
51187         this.split.placement = Roo.SplitBar.BOTTOM;
51188         this.split.orientation = Roo.SplitBar.VERTICAL;
51189         this.split.el.addClass("x-layout-split-v");
51190     }
51191     var size = config.initialSize || config.height;
51192     if(typeof size != "undefined"){
51193         this.el.setHeight(size);
51194     }
51195 };
51196 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51197     orientation: Roo.SplitBar.VERTICAL,
51198     getBox : function(){
51199         if(this.collapsed){
51200             return this.collapsedEl.getBox();
51201         }
51202         var box = this.el.getBox();
51203         if(this.split){
51204             var sh = this.split.el.getHeight();
51205             box.height += sh;
51206             box.y -= sh;
51207         }
51208         return box;
51209     },
51210     
51211     updateBox : function(box){
51212         if(this.split && !this.collapsed){
51213             var sh = this.split.el.getHeight();
51214             box.height -= sh;
51215             box.y += sh;
51216             this.split.el.setLeft(box.x);
51217             this.split.el.setTop(box.y-sh);
51218             this.split.el.setWidth(box.width);
51219         }
51220         if(this.collapsed){
51221             this.updateBody(box.width, null);
51222         }
51223         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51224     }
51225 });
51226
51227 Roo.EastLayoutRegion = function(mgr, config){
51228     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51229     if(this.split){
51230         this.split.placement = Roo.SplitBar.RIGHT;
51231         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51232         this.split.el.addClass("x-layout-split-h");
51233     }
51234     var size = config.initialSize || config.width;
51235     if(typeof size != "undefined"){
51236         this.el.setWidth(size);
51237     }
51238 };
51239 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51240     orientation: Roo.SplitBar.HORIZONTAL,
51241     getBox : function(){
51242         if(this.collapsed){
51243             return this.collapsedEl.getBox();
51244         }
51245         var box = this.el.getBox();
51246         if(this.split){
51247             var sw = this.split.el.getWidth();
51248             box.width += sw;
51249             box.x -= sw;
51250         }
51251         return box;
51252     },
51253
51254     updateBox : function(box){
51255         if(this.split && !this.collapsed){
51256             var sw = this.split.el.getWidth();
51257             box.width -= sw;
51258             this.split.el.setLeft(box.x);
51259             this.split.el.setTop(box.y);
51260             this.split.el.setHeight(box.height);
51261             box.x += sw;
51262         }
51263         if(this.collapsed){
51264             this.updateBody(null, box.height);
51265         }
51266         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51267     }
51268 });
51269
51270 Roo.WestLayoutRegion = function(mgr, config){
51271     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51272     if(this.split){
51273         this.split.placement = Roo.SplitBar.LEFT;
51274         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51275         this.split.el.addClass("x-layout-split-h");
51276     }
51277     var size = config.initialSize || config.width;
51278     if(typeof size != "undefined"){
51279         this.el.setWidth(size);
51280     }
51281 };
51282 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51283     orientation: Roo.SplitBar.HORIZONTAL,
51284     getBox : function(){
51285         if(this.collapsed){
51286             return this.collapsedEl.getBox();
51287         }
51288         var box = this.el.getBox();
51289         if(this.split){
51290             box.width += this.split.el.getWidth();
51291         }
51292         return box;
51293     },
51294     
51295     updateBox : function(box){
51296         if(this.split && !this.collapsed){
51297             var sw = this.split.el.getWidth();
51298             box.width -= sw;
51299             this.split.el.setLeft(box.x+box.width);
51300             this.split.el.setTop(box.y);
51301             this.split.el.setHeight(box.height);
51302         }
51303         if(this.collapsed){
51304             this.updateBody(null, box.height);
51305         }
51306         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51307     }
51308 });
51309 /*
51310  * Based on:
51311  * Ext JS Library 1.1.1
51312  * Copyright(c) 2006-2007, Ext JS, LLC.
51313  *
51314  * Originally Released Under LGPL - original licence link has changed is not relivant.
51315  *
51316  * Fork - LGPL
51317  * <script type="text/javascript">
51318  */
51319  
51320  
51321 /*
51322  * Private internal class for reading and applying state
51323  */
51324 Roo.LayoutStateManager = function(layout){
51325      // default empty state
51326      this.state = {
51327         north: {},
51328         south: {},
51329         east: {},
51330         west: {}       
51331     };
51332 };
51333
51334 Roo.LayoutStateManager.prototype = {
51335     init : function(layout, provider){
51336         this.provider = provider;
51337         var state = provider.get(layout.id+"-layout-state");
51338         if(state){
51339             var wasUpdating = layout.isUpdating();
51340             if(!wasUpdating){
51341                 layout.beginUpdate();
51342             }
51343             for(var key in state){
51344                 if(typeof state[key] != "function"){
51345                     var rstate = state[key];
51346                     var r = layout.getRegion(key);
51347                     if(r && rstate){
51348                         if(rstate.size){
51349                             r.resizeTo(rstate.size);
51350                         }
51351                         if(rstate.collapsed == true){
51352                             r.collapse(true);
51353                         }else{
51354                             r.expand(null, true);
51355                         }
51356                     }
51357                 }
51358             }
51359             if(!wasUpdating){
51360                 layout.endUpdate();
51361             }
51362             this.state = state; 
51363         }
51364         this.layout = layout;
51365         layout.on("regionresized", this.onRegionResized, this);
51366         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51367         layout.on("regionexpanded", this.onRegionExpanded, this);
51368     },
51369     
51370     storeState : function(){
51371         this.provider.set(this.layout.id+"-layout-state", this.state);
51372     },
51373     
51374     onRegionResized : function(region, newSize){
51375         this.state[region.getPosition()].size = newSize;
51376         this.storeState();
51377     },
51378     
51379     onRegionCollapsed : function(region){
51380         this.state[region.getPosition()].collapsed = true;
51381         this.storeState();
51382     },
51383     
51384     onRegionExpanded : function(region){
51385         this.state[region.getPosition()].collapsed = false;
51386         this.storeState();
51387     }
51388 };/*
51389  * Based on:
51390  * Ext JS Library 1.1.1
51391  * Copyright(c) 2006-2007, Ext JS, LLC.
51392  *
51393  * Originally Released Under LGPL - original licence link has changed is not relivant.
51394  *
51395  * Fork - LGPL
51396  * <script type="text/javascript">
51397  */
51398 /**
51399  * @class Roo.ContentPanel
51400  * @extends Roo.util.Observable
51401  * A basic ContentPanel element.
51402  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51403  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51404  * @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
51405  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51406  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51407  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51408  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51409  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51410  * @cfg {String} title          The title for this panel
51411  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51412  * @cfg {String} url            Calls {@link #setUrl} with this value
51413  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51414  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51415  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51416  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51417
51418  * @constructor
51419  * Create a new ContentPanel.
51420  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51421  * @param {String/Object} config A string to set only the title or a config object
51422  * @param {String} content (optional) Set the HTML content for this panel
51423  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51424  */
51425 Roo.ContentPanel = function(el, config, content){
51426     
51427      
51428     /*
51429     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51430         config = el;
51431         el = Roo.id();
51432     }
51433     if (config && config.parentLayout) { 
51434         el = config.parentLayout.el.createChild(); 
51435     }
51436     */
51437     if(el.autoCreate){ // xtype is available if this is called from factory
51438         config = el;
51439         el = Roo.id();
51440     }
51441     this.el = Roo.get(el);
51442     if(!this.el && config && config.autoCreate){
51443         if(typeof config.autoCreate == "object"){
51444             if(!config.autoCreate.id){
51445                 config.autoCreate.id = config.id||el;
51446             }
51447             this.el = Roo.DomHelper.append(document.body,
51448                         config.autoCreate, true);
51449         }else{
51450             this.el = Roo.DomHelper.append(document.body,
51451                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51452         }
51453     }
51454     this.closable = false;
51455     this.loaded = false;
51456     this.active = false;
51457     if(typeof config == "string"){
51458         this.title = config;
51459     }else{
51460         Roo.apply(this, config);
51461     }
51462     
51463     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51464         this.wrapEl = this.el.wrap();
51465         this.toolbar.container = this.el.insertSibling(false, 'before');
51466         this.toolbar = new Roo.Toolbar(this.toolbar);
51467     }
51468     
51469     // xtype created footer. - not sure if will work as we normally have to render first..
51470     if (this.footer && !this.footer.el && this.footer.xtype) {
51471         if (!this.wrapEl) {
51472             this.wrapEl = this.el.wrap();
51473         }
51474     
51475         this.footer.container = this.wrapEl.createChild();
51476          
51477         this.footer = Roo.factory(this.footer, Roo);
51478         
51479     }
51480     
51481     if(this.resizeEl){
51482         this.resizeEl = Roo.get(this.resizeEl, true);
51483     }else{
51484         this.resizeEl = this.el;
51485     }
51486     // handle view.xtype
51487     
51488  
51489     
51490     
51491     this.addEvents({
51492         /**
51493          * @event activate
51494          * Fires when this panel is activated. 
51495          * @param {Roo.ContentPanel} this
51496          */
51497         "activate" : true,
51498         /**
51499          * @event deactivate
51500          * Fires when this panel is activated. 
51501          * @param {Roo.ContentPanel} this
51502          */
51503         "deactivate" : true,
51504
51505         /**
51506          * @event resize
51507          * Fires when this panel is resized if fitToFrame is true.
51508          * @param {Roo.ContentPanel} this
51509          * @param {Number} width The width after any component adjustments
51510          * @param {Number} height The height after any component adjustments
51511          */
51512         "resize" : true,
51513         
51514          /**
51515          * @event render
51516          * Fires when this tab is created
51517          * @param {Roo.ContentPanel} this
51518          */
51519         "render" : true
51520         
51521         
51522         
51523     });
51524     
51525
51526     
51527     
51528     if(this.autoScroll){
51529         this.resizeEl.setStyle("overflow", "auto");
51530     } else {
51531         // fix randome scrolling
51532         this.el.on('scroll', function() {
51533             Roo.log('fix random scolling');
51534             this.scrollTo('top',0); 
51535         });
51536     }
51537     content = content || this.content;
51538     if(content){
51539         this.setContent(content);
51540     }
51541     if(config && config.url){
51542         this.setUrl(this.url, this.params, this.loadOnce);
51543     }
51544     
51545     
51546     
51547     Roo.ContentPanel.superclass.constructor.call(this);
51548     
51549     if (this.view && typeof(this.view.xtype) != 'undefined') {
51550         this.view.el = this.el.appendChild(document.createElement("div"));
51551         this.view = Roo.factory(this.view); 
51552         this.view.render  &&  this.view.render(false, '');  
51553     }
51554     
51555     
51556     this.fireEvent('render', this);
51557 };
51558
51559 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51560     tabTip:'',
51561     setRegion : function(region){
51562         this.region = region;
51563         if(region){
51564            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51565         }else{
51566            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51567         } 
51568     },
51569     
51570     /**
51571      * Returns the toolbar for this Panel if one was configured. 
51572      * @return {Roo.Toolbar} 
51573      */
51574     getToolbar : function(){
51575         return this.toolbar;
51576     },
51577     
51578     setActiveState : function(active){
51579         this.active = active;
51580         if(!active){
51581             this.fireEvent("deactivate", this);
51582         }else{
51583             this.fireEvent("activate", this);
51584         }
51585     },
51586     /**
51587      * Updates this panel's element
51588      * @param {String} content The new content
51589      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51590     */
51591     setContent : function(content, loadScripts){
51592         this.el.update(content, loadScripts);
51593     },
51594
51595     ignoreResize : function(w, h){
51596         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51597             return true;
51598         }else{
51599             this.lastSize = {width: w, height: h};
51600             return false;
51601         }
51602     },
51603     /**
51604      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51605      * @return {Roo.UpdateManager} The UpdateManager
51606      */
51607     getUpdateManager : function(){
51608         return this.el.getUpdateManager();
51609     },
51610      /**
51611      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51612      * @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:
51613 <pre><code>
51614 panel.load({
51615     url: "your-url.php",
51616     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51617     callback: yourFunction,
51618     scope: yourObject, //(optional scope)
51619     discardUrl: false,
51620     nocache: false,
51621     text: "Loading...",
51622     timeout: 30,
51623     scripts: false
51624 });
51625 </code></pre>
51626      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51627      * 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.
51628      * @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}
51629      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51630      * @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.
51631      * @return {Roo.ContentPanel} this
51632      */
51633     load : function(){
51634         var um = this.el.getUpdateManager();
51635         um.update.apply(um, arguments);
51636         return this;
51637     },
51638
51639
51640     /**
51641      * 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.
51642      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51643      * @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)
51644      * @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)
51645      * @return {Roo.UpdateManager} The UpdateManager
51646      */
51647     setUrl : function(url, params, loadOnce){
51648         if(this.refreshDelegate){
51649             this.removeListener("activate", this.refreshDelegate);
51650         }
51651         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51652         this.on("activate", this.refreshDelegate);
51653         return this.el.getUpdateManager();
51654     },
51655     
51656     _handleRefresh : function(url, params, loadOnce){
51657         if(!loadOnce || !this.loaded){
51658             var updater = this.el.getUpdateManager();
51659             updater.update(url, params, this._setLoaded.createDelegate(this));
51660         }
51661     },
51662     
51663     _setLoaded : function(){
51664         this.loaded = true;
51665     }, 
51666     
51667     /**
51668      * Returns this panel's id
51669      * @return {String} 
51670      */
51671     getId : function(){
51672         return this.el.id;
51673     },
51674     
51675     /** 
51676      * Returns this panel's element - used by regiosn to add.
51677      * @return {Roo.Element} 
51678      */
51679     getEl : function(){
51680         return this.wrapEl || this.el;
51681     },
51682     
51683     adjustForComponents : function(width, height)
51684     {
51685         //Roo.log('adjustForComponents ');
51686         if(this.resizeEl != this.el){
51687             width -= this.el.getFrameWidth('lr');
51688             height -= this.el.getFrameWidth('tb');
51689         }
51690         if(this.toolbar){
51691             var te = this.toolbar.getEl();
51692             height -= te.getHeight();
51693             te.setWidth(width);
51694         }
51695         if(this.footer){
51696             var te = this.footer.getEl();
51697             Roo.log("footer:" + te.getHeight());
51698             
51699             height -= te.getHeight();
51700             te.setWidth(width);
51701         }
51702         
51703         
51704         if(this.adjustments){
51705             width += this.adjustments[0];
51706             height += this.adjustments[1];
51707         }
51708         return {"width": width, "height": height};
51709     },
51710     
51711     setSize : function(width, height){
51712         if(this.fitToFrame && !this.ignoreResize(width, height)){
51713             if(this.fitContainer && this.resizeEl != this.el){
51714                 this.el.setSize(width, height);
51715             }
51716             var size = this.adjustForComponents(width, height);
51717             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51718             this.fireEvent('resize', this, size.width, size.height);
51719         }
51720     },
51721     
51722     /**
51723      * Returns this panel's title
51724      * @return {String} 
51725      */
51726     getTitle : function(){
51727         return this.title;
51728     },
51729     
51730     /**
51731      * Set this panel's title
51732      * @param {String} title
51733      */
51734     setTitle : function(title){
51735         this.title = title;
51736         if(this.region){
51737             this.region.updatePanelTitle(this, title);
51738         }
51739     },
51740     
51741     /**
51742      * Returns true is this panel was configured to be closable
51743      * @return {Boolean} 
51744      */
51745     isClosable : function(){
51746         return this.closable;
51747     },
51748     
51749     beforeSlide : function(){
51750         this.el.clip();
51751         this.resizeEl.clip();
51752     },
51753     
51754     afterSlide : function(){
51755         this.el.unclip();
51756         this.resizeEl.unclip();
51757     },
51758     
51759     /**
51760      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51761      *   Will fail silently if the {@link #setUrl} method has not been called.
51762      *   This does not activate the panel, just updates its content.
51763      */
51764     refresh : function(){
51765         if(this.refreshDelegate){
51766            this.loaded = false;
51767            this.refreshDelegate();
51768         }
51769     },
51770     
51771     /**
51772      * Destroys this panel
51773      */
51774     destroy : function(){
51775         this.el.removeAllListeners();
51776         var tempEl = document.createElement("span");
51777         tempEl.appendChild(this.el.dom);
51778         tempEl.innerHTML = "";
51779         this.el.remove();
51780         this.el = null;
51781     },
51782     
51783     /**
51784      * form - if the content panel contains a form - this is a reference to it.
51785      * @type {Roo.form.Form}
51786      */
51787     form : false,
51788     /**
51789      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51790      *    This contains a reference to it.
51791      * @type {Roo.View}
51792      */
51793     view : false,
51794     
51795       /**
51796      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51797      * <pre><code>
51798
51799 layout.addxtype({
51800        xtype : 'Form',
51801        items: [ .... ]
51802    }
51803 );
51804
51805 </code></pre>
51806      * @param {Object} cfg Xtype definition of item to add.
51807      */
51808     
51809     addxtype : function(cfg) {
51810         // add form..
51811         if (cfg.xtype.match(/^Form$/)) {
51812             
51813             var el;
51814             //if (this.footer) {
51815             //    el = this.footer.container.insertSibling(false, 'before');
51816             //} else {
51817                 el = this.el.createChild();
51818             //}
51819
51820             this.form = new  Roo.form.Form(cfg);
51821             
51822             
51823             if ( this.form.allItems.length) this.form.render(el.dom);
51824             return this.form;
51825         }
51826         // should only have one of theses..
51827         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51828             // views.. should not be just added - used named prop 'view''
51829             
51830             cfg.el = this.el.appendChild(document.createElement("div"));
51831             // factory?
51832             
51833             var ret = new Roo.factory(cfg);
51834              
51835              ret.render && ret.render(false, ''); // render blank..
51836             this.view = ret;
51837             return ret;
51838         }
51839         return false;
51840     }
51841 });
51842
51843 /**
51844  * @class Roo.GridPanel
51845  * @extends Roo.ContentPanel
51846  * @constructor
51847  * Create a new GridPanel.
51848  * @param {Roo.grid.Grid} grid The grid for this panel
51849  * @param {String/Object} config A string to set only the panel's title, or a config object
51850  */
51851 Roo.GridPanel = function(grid, config){
51852     
51853   
51854     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51855         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51856         
51857     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51858     
51859     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51860     
51861     if(this.toolbar){
51862         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51863     }
51864     // xtype created footer. - not sure if will work as we normally have to render first..
51865     if (this.footer && !this.footer.el && this.footer.xtype) {
51866         
51867         this.footer.container = this.grid.getView().getFooterPanel(true);
51868         this.footer.dataSource = this.grid.dataSource;
51869         this.footer = Roo.factory(this.footer, Roo);
51870         
51871     }
51872     
51873     grid.monitorWindowResize = false; // turn off autosizing
51874     grid.autoHeight = false;
51875     grid.autoWidth = false;
51876     this.grid = grid;
51877     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51878 };
51879
51880 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51881     getId : function(){
51882         return this.grid.id;
51883     },
51884     
51885     /**
51886      * Returns the grid for this panel
51887      * @return {Roo.grid.Grid} 
51888      */
51889     getGrid : function(){
51890         return this.grid;    
51891     },
51892     
51893     setSize : function(width, height){
51894         if(!this.ignoreResize(width, height)){
51895             var grid = this.grid;
51896             var size = this.adjustForComponents(width, height);
51897             grid.getGridEl().setSize(size.width, size.height);
51898             grid.autoSize();
51899         }
51900     },
51901     
51902     beforeSlide : function(){
51903         this.grid.getView().scroller.clip();
51904     },
51905     
51906     afterSlide : function(){
51907         this.grid.getView().scroller.unclip();
51908     },
51909     
51910     destroy : function(){
51911         this.grid.destroy();
51912         delete this.grid;
51913         Roo.GridPanel.superclass.destroy.call(this); 
51914     }
51915 });
51916
51917
51918 /**
51919  * @class Roo.NestedLayoutPanel
51920  * @extends Roo.ContentPanel
51921  * @constructor
51922  * Create a new NestedLayoutPanel.
51923  * 
51924  * 
51925  * @param {Roo.BorderLayout} layout The layout for this panel
51926  * @param {String/Object} config A string to set only the title or a config object
51927  */
51928 Roo.NestedLayoutPanel = function(layout, config)
51929 {
51930     // construct with only one argument..
51931     /* FIXME - implement nicer consturctors
51932     if (layout.layout) {
51933         config = layout;
51934         layout = config.layout;
51935         delete config.layout;
51936     }
51937     if (layout.xtype && !layout.getEl) {
51938         // then layout needs constructing..
51939         layout = Roo.factory(layout, Roo);
51940     }
51941     */
51942     
51943     
51944     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51945     
51946     layout.monitorWindowResize = false; // turn off autosizing
51947     this.layout = layout;
51948     this.layout.getEl().addClass("x-layout-nested-layout");
51949     
51950     
51951     
51952     
51953 };
51954
51955 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51956
51957     setSize : function(width, height){
51958         if(!this.ignoreResize(width, height)){
51959             var size = this.adjustForComponents(width, height);
51960             var el = this.layout.getEl();
51961             el.setSize(size.width, size.height);
51962             var touch = el.dom.offsetWidth;
51963             this.layout.layout();
51964             // ie requires a double layout on the first pass
51965             if(Roo.isIE && !this.initialized){
51966                 this.initialized = true;
51967                 this.layout.layout();
51968             }
51969         }
51970     },
51971     
51972     // activate all subpanels if not currently active..
51973     
51974     setActiveState : function(active){
51975         this.active = active;
51976         if(!active){
51977             this.fireEvent("deactivate", this);
51978             return;
51979         }
51980         
51981         this.fireEvent("activate", this);
51982         // not sure if this should happen before or after..
51983         if (!this.layout) {
51984             return; // should not happen..
51985         }
51986         var reg = false;
51987         for (var r in this.layout.regions) {
51988             reg = this.layout.getRegion(r);
51989             if (reg.getActivePanel()) {
51990                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51991                 reg.setActivePanel(reg.getActivePanel());
51992                 continue;
51993             }
51994             if (!reg.panels.length) {
51995                 continue;
51996             }
51997             reg.showPanel(reg.getPanel(0));
51998         }
51999         
52000         
52001         
52002         
52003     },
52004     
52005     /**
52006      * Returns the nested BorderLayout for this panel
52007      * @return {Roo.BorderLayout} 
52008      */
52009     getLayout : function(){
52010         return this.layout;
52011     },
52012     
52013      /**
52014      * Adds a xtype elements to the layout of the nested panel
52015      * <pre><code>
52016
52017 panel.addxtype({
52018        xtype : 'ContentPanel',
52019        region: 'west',
52020        items: [ .... ]
52021    }
52022 );
52023
52024 panel.addxtype({
52025         xtype : 'NestedLayoutPanel',
52026         region: 'west',
52027         layout: {
52028            center: { },
52029            west: { }   
52030         },
52031         items : [ ... list of content panels or nested layout panels.. ]
52032    }
52033 );
52034 </code></pre>
52035      * @param {Object} cfg Xtype definition of item to add.
52036      */
52037     addxtype : function(cfg) {
52038         return this.layout.addxtype(cfg);
52039     
52040     }
52041 });
52042
52043 Roo.ScrollPanel = function(el, config, content){
52044     config = config || {};
52045     config.fitToFrame = true;
52046     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52047     
52048     this.el.dom.style.overflow = "hidden";
52049     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52050     this.el.removeClass("x-layout-inactive-content");
52051     this.el.on("mousewheel", this.onWheel, this);
52052
52053     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52054     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52055     up.unselectable(); down.unselectable();
52056     up.on("click", this.scrollUp, this);
52057     down.on("click", this.scrollDown, this);
52058     up.addClassOnOver("x-scroller-btn-over");
52059     down.addClassOnOver("x-scroller-btn-over");
52060     up.addClassOnClick("x-scroller-btn-click");
52061     down.addClassOnClick("x-scroller-btn-click");
52062     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52063
52064     this.resizeEl = this.el;
52065     this.el = wrap; this.up = up; this.down = down;
52066 };
52067
52068 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52069     increment : 100,
52070     wheelIncrement : 5,
52071     scrollUp : function(){
52072         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52073     },
52074
52075     scrollDown : function(){
52076         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52077     },
52078
52079     afterScroll : function(){
52080         var el = this.resizeEl;
52081         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52082         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52083         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52084     },
52085
52086     setSize : function(){
52087         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52088         this.afterScroll();
52089     },
52090
52091     onWheel : function(e){
52092         var d = e.getWheelDelta();
52093         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52094         this.afterScroll();
52095         e.stopEvent();
52096     },
52097
52098     setContent : function(content, loadScripts){
52099         this.resizeEl.update(content, loadScripts);
52100     }
52101
52102 });
52103
52104
52105
52106
52107
52108
52109
52110
52111
52112 /**
52113  * @class Roo.TreePanel
52114  * @extends Roo.ContentPanel
52115  * @constructor
52116  * Create a new TreePanel. - defaults to fit/scoll contents.
52117  * @param {String/Object} config A string to set only the panel's title, or a config object
52118  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52119  */
52120 Roo.TreePanel = function(config){
52121     var el = config.el;
52122     var tree = config.tree;
52123     delete config.tree; 
52124     delete config.el; // hopefull!
52125     
52126     // wrapper for IE7 strict & safari scroll issue
52127     
52128     var treeEl = el.createChild();
52129     config.resizeEl = treeEl;
52130     
52131     
52132     
52133     Roo.TreePanel.superclass.constructor.call(this, el, config);
52134  
52135  
52136     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52137     //console.log(tree);
52138     this.on('activate', function()
52139     {
52140         if (this.tree.rendered) {
52141             return;
52142         }
52143         //console.log('render tree');
52144         this.tree.render();
52145     });
52146     // this should not be needed.. - it's actually the 'el' that resizes?
52147     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52148     
52149     //this.on('resize',  function (cp, w, h) {
52150     //        this.tree.innerCt.setWidth(w);
52151     //        this.tree.innerCt.setHeight(h);
52152     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52153     //});
52154
52155         
52156     
52157 };
52158
52159 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52160     fitToFrame : true,
52161     autoScroll : true
52162 });
52163
52164
52165
52166
52167
52168
52169
52170
52171
52172
52173
52174 /*
52175  * Based on:
52176  * Ext JS Library 1.1.1
52177  * Copyright(c) 2006-2007, Ext JS, LLC.
52178  *
52179  * Originally Released Under LGPL - original licence link has changed is not relivant.
52180  *
52181  * Fork - LGPL
52182  * <script type="text/javascript">
52183  */
52184  
52185
52186 /**
52187  * @class Roo.ReaderLayout
52188  * @extends Roo.BorderLayout
52189  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52190  * center region containing two nested regions (a top one for a list view and one for item preview below),
52191  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52192  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52193  * expedites the setup of the overall layout and regions for this common application style.
52194  * Example:
52195  <pre><code>
52196 var reader = new Roo.ReaderLayout();
52197 var CP = Roo.ContentPanel;  // shortcut for adding
52198
52199 reader.beginUpdate();
52200 reader.add("north", new CP("north", "North"));
52201 reader.add("west", new CP("west", {title: "West"}));
52202 reader.add("east", new CP("east", {title: "East"}));
52203
52204 reader.regions.listView.add(new CP("listView", "List"));
52205 reader.regions.preview.add(new CP("preview", "Preview"));
52206 reader.endUpdate();
52207 </code></pre>
52208 * @constructor
52209 * Create a new ReaderLayout
52210 * @param {Object} config Configuration options
52211 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52212 * document.body if omitted)
52213 */
52214 Roo.ReaderLayout = function(config, renderTo){
52215     var c = config || {size:{}};
52216     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52217         north: c.north !== false ? Roo.apply({
52218             split:false,
52219             initialSize: 32,
52220             titlebar: false
52221         }, c.north) : false,
52222         west: c.west !== false ? Roo.apply({
52223             split:true,
52224             initialSize: 200,
52225             minSize: 175,
52226             maxSize: 400,
52227             titlebar: true,
52228             collapsible: true,
52229             animate: true,
52230             margins:{left:5,right:0,bottom:5,top:5},
52231             cmargins:{left:5,right:5,bottom:5,top:5}
52232         }, c.west) : false,
52233         east: c.east !== false ? Roo.apply({
52234             split:true,
52235             initialSize: 200,
52236             minSize: 175,
52237             maxSize: 400,
52238             titlebar: true,
52239             collapsible: true,
52240             animate: true,
52241             margins:{left:0,right:5,bottom:5,top:5},
52242             cmargins:{left:5,right:5,bottom:5,top:5}
52243         }, c.east) : false,
52244         center: Roo.apply({
52245             tabPosition: 'top',
52246             autoScroll:false,
52247             closeOnTab: true,
52248             titlebar:false,
52249             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52250         }, c.center)
52251     });
52252
52253     this.el.addClass('x-reader');
52254
52255     this.beginUpdate();
52256
52257     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52258         south: c.preview !== false ? Roo.apply({
52259             split:true,
52260             initialSize: 200,
52261             minSize: 100,
52262             autoScroll:true,
52263             collapsible:true,
52264             titlebar: true,
52265             cmargins:{top:5,left:0, right:0, bottom:0}
52266         }, c.preview) : false,
52267         center: Roo.apply({
52268             autoScroll:false,
52269             titlebar:false,
52270             minHeight:200
52271         }, c.listView)
52272     });
52273     this.add('center', new Roo.NestedLayoutPanel(inner,
52274             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52275
52276     this.endUpdate();
52277
52278     this.regions.preview = inner.getRegion('south');
52279     this.regions.listView = inner.getRegion('center');
52280 };
52281
52282 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52283  * Based on:
52284  * Ext JS Library 1.1.1
52285  * Copyright(c) 2006-2007, Ext JS, LLC.
52286  *
52287  * Originally Released Under LGPL - original licence link has changed is not relivant.
52288  *
52289  * Fork - LGPL
52290  * <script type="text/javascript">
52291  */
52292  
52293 /**
52294  * @class Roo.grid.Grid
52295  * @extends Roo.util.Observable
52296  * This class represents the primary interface of a component based grid control.
52297  * <br><br>Usage:<pre><code>
52298  var grid = new Roo.grid.Grid("my-container-id", {
52299      ds: myDataStore,
52300      cm: myColModel,
52301      selModel: mySelectionModel,
52302      autoSizeColumns: true,
52303      monitorWindowResize: false,
52304      trackMouseOver: true
52305  });
52306  // set any options
52307  grid.render();
52308  * </code></pre>
52309  * <b>Common Problems:</b><br/>
52310  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52311  * element will correct this<br/>
52312  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52313  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52314  * are unpredictable.<br/>
52315  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52316  * grid to calculate dimensions/offsets.<br/>
52317   * @constructor
52318  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52319  * The container MUST have some type of size defined for the grid to fill. The container will be
52320  * automatically set to position relative if it isn't already.
52321  * @param {Object} config A config object that sets properties on this grid.
52322  */
52323 Roo.grid.Grid = function(container, config){
52324         // initialize the container
52325         this.container = Roo.get(container);
52326         this.container.update("");
52327         this.container.setStyle("overflow", "hidden");
52328     this.container.addClass('x-grid-container');
52329
52330     this.id = this.container.id;
52331
52332     Roo.apply(this, config);
52333     // check and correct shorthanded configs
52334     if(this.ds){
52335         this.dataSource = this.ds;
52336         delete this.ds;
52337     }
52338     if(this.cm){
52339         this.colModel = this.cm;
52340         delete this.cm;
52341     }
52342     if(this.sm){
52343         this.selModel = this.sm;
52344         delete this.sm;
52345     }
52346
52347     if (this.selModel) {
52348         this.selModel = Roo.factory(this.selModel, Roo.grid);
52349         this.sm = this.selModel;
52350         this.sm.xmodule = this.xmodule || false;
52351     }
52352     if (typeof(this.colModel.config) == 'undefined') {
52353         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52354         this.cm = this.colModel;
52355         this.cm.xmodule = this.xmodule || false;
52356     }
52357     if (this.dataSource) {
52358         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52359         this.ds = this.dataSource;
52360         this.ds.xmodule = this.xmodule || false;
52361          
52362     }
52363     
52364     
52365     
52366     if(this.width){
52367         this.container.setWidth(this.width);
52368     }
52369
52370     if(this.height){
52371         this.container.setHeight(this.height);
52372     }
52373     /** @private */
52374         this.addEvents({
52375         // raw events
52376         /**
52377          * @event click
52378          * The raw click event for the entire grid.
52379          * @param {Roo.EventObject} e
52380          */
52381         "click" : true,
52382         /**
52383          * @event dblclick
52384          * The raw dblclick event for the entire grid.
52385          * @param {Roo.EventObject} e
52386          */
52387         "dblclick" : true,
52388         /**
52389          * @event contextmenu
52390          * The raw contextmenu event for the entire grid.
52391          * @param {Roo.EventObject} e
52392          */
52393         "contextmenu" : true,
52394         /**
52395          * @event mousedown
52396          * The raw mousedown event for the entire grid.
52397          * @param {Roo.EventObject} e
52398          */
52399         "mousedown" : true,
52400         /**
52401          * @event mouseup
52402          * The raw mouseup event for the entire grid.
52403          * @param {Roo.EventObject} e
52404          */
52405         "mouseup" : true,
52406         /**
52407          * @event mouseover
52408          * The raw mouseover event for the entire grid.
52409          * @param {Roo.EventObject} e
52410          */
52411         "mouseover" : true,
52412         /**
52413          * @event mouseout
52414          * The raw mouseout event for the entire grid.
52415          * @param {Roo.EventObject} e
52416          */
52417         "mouseout" : true,
52418         /**
52419          * @event keypress
52420          * The raw keypress event for the entire grid.
52421          * @param {Roo.EventObject} e
52422          */
52423         "keypress" : true,
52424         /**
52425          * @event keydown
52426          * The raw keydown event for the entire grid.
52427          * @param {Roo.EventObject} e
52428          */
52429         "keydown" : true,
52430
52431         // custom events
52432
52433         /**
52434          * @event cellclick
52435          * Fires when a cell is clicked
52436          * @param {Grid} this
52437          * @param {Number} rowIndex
52438          * @param {Number} columnIndex
52439          * @param {Roo.EventObject} e
52440          */
52441         "cellclick" : true,
52442         /**
52443          * @event celldblclick
52444          * Fires when a cell is double clicked
52445          * @param {Grid} this
52446          * @param {Number} rowIndex
52447          * @param {Number} columnIndex
52448          * @param {Roo.EventObject} e
52449          */
52450         "celldblclick" : true,
52451         /**
52452          * @event rowclick
52453          * Fires when a row is clicked
52454          * @param {Grid} this
52455          * @param {Number} rowIndex
52456          * @param {Roo.EventObject} e
52457          */
52458         "rowclick" : true,
52459         /**
52460          * @event rowdblclick
52461          * Fires when a row is double clicked
52462          * @param {Grid} this
52463          * @param {Number} rowIndex
52464          * @param {Roo.EventObject} e
52465          */
52466         "rowdblclick" : true,
52467         /**
52468          * @event headerclick
52469          * Fires when a header is clicked
52470          * @param {Grid} this
52471          * @param {Number} columnIndex
52472          * @param {Roo.EventObject} e
52473          */
52474         "headerclick" : true,
52475         /**
52476          * @event headerdblclick
52477          * Fires when a header cell is double clicked
52478          * @param {Grid} this
52479          * @param {Number} columnIndex
52480          * @param {Roo.EventObject} e
52481          */
52482         "headerdblclick" : true,
52483         /**
52484          * @event rowcontextmenu
52485          * Fires when a row is right clicked
52486          * @param {Grid} this
52487          * @param {Number} rowIndex
52488          * @param {Roo.EventObject} e
52489          */
52490         "rowcontextmenu" : true,
52491         /**
52492          * @event cellcontextmenu
52493          * Fires when a cell is right clicked
52494          * @param {Grid} this
52495          * @param {Number} rowIndex
52496          * @param {Number} cellIndex
52497          * @param {Roo.EventObject} e
52498          */
52499          "cellcontextmenu" : true,
52500         /**
52501          * @event headercontextmenu
52502          * Fires when a header is right clicked
52503          * @param {Grid} this
52504          * @param {Number} columnIndex
52505          * @param {Roo.EventObject} e
52506          */
52507         "headercontextmenu" : true,
52508         /**
52509          * @event bodyscroll
52510          * Fires when the body element is scrolled
52511          * @param {Number} scrollLeft
52512          * @param {Number} scrollTop
52513          */
52514         "bodyscroll" : true,
52515         /**
52516          * @event columnresize
52517          * Fires when the user resizes a column
52518          * @param {Number} columnIndex
52519          * @param {Number} newSize
52520          */
52521         "columnresize" : true,
52522         /**
52523          * @event columnmove
52524          * Fires when the user moves a column
52525          * @param {Number} oldIndex
52526          * @param {Number} newIndex
52527          */
52528         "columnmove" : true,
52529         /**
52530          * @event startdrag
52531          * Fires when row(s) start being dragged
52532          * @param {Grid} this
52533          * @param {Roo.GridDD} dd The drag drop object
52534          * @param {event} e The raw browser event
52535          */
52536         "startdrag" : true,
52537         /**
52538          * @event enddrag
52539          * Fires when a drag operation is complete
52540          * @param {Grid} this
52541          * @param {Roo.GridDD} dd The drag drop object
52542          * @param {event} e The raw browser event
52543          */
52544         "enddrag" : true,
52545         /**
52546          * @event dragdrop
52547          * Fires when dragged row(s) are dropped on a valid DD target
52548          * @param {Grid} this
52549          * @param {Roo.GridDD} dd The drag drop object
52550          * @param {String} targetId The target drag drop object
52551          * @param {event} e The raw browser event
52552          */
52553         "dragdrop" : true,
52554         /**
52555          * @event dragover
52556          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52557          * @param {Grid} this
52558          * @param {Roo.GridDD} dd The drag drop object
52559          * @param {String} targetId The target drag drop object
52560          * @param {event} e The raw browser event
52561          */
52562         "dragover" : true,
52563         /**
52564          * @event dragenter
52565          *  Fires when the dragged row(s) first cross another DD target while being dragged
52566          * @param {Grid} this
52567          * @param {Roo.GridDD} dd The drag drop object
52568          * @param {String} targetId The target drag drop object
52569          * @param {event} e The raw browser event
52570          */
52571         "dragenter" : true,
52572         /**
52573          * @event dragout
52574          * Fires when the dragged row(s) leave another DD target while being dragged
52575          * @param {Grid} this
52576          * @param {Roo.GridDD} dd The drag drop object
52577          * @param {String} targetId The target drag drop object
52578          * @param {event} e The raw browser event
52579          */
52580         "dragout" : true,
52581         /**
52582          * @event rowclass
52583          * Fires when a row is rendered, so you can change add a style to it.
52584          * @param {GridView} gridview   The grid view
52585          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52586          */
52587         'rowclass' : true,
52588
52589         /**
52590          * @event render
52591          * Fires when the grid is rendered
52592          * @param {Grid} grid
52593          */
52594         'render' : true
52595     });
52596
52597     Roo.grid.Grid.superclass.constructor.call(this);
52598 };
52599 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52600     
52601     /**
52602      * @cfg {String} ddGroup - drag drop group.
52603      */
52604
52605     /**
52606      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52607      */
52608     minColumnWidth : 25,
52609
52610     /**
52611      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52612      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52613      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52614      */
52615     autoSizeColumns : false,
52616
52617     /**
52618      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52619      */
52620     autoSizeHeaders : true,
52621
52622     /**
52623      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52624      */
52625     monitorWindowResize : true,
52626
52627     /**
52628      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52629      * rows measured to get a columns size. Default is 0 (all rows).
52630      */
52631     maxRowsToMeasure : 0,
52632
52633     /**
52634      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52635      */
52636     trackMouseOver : true,
52637
52638     /**
52639     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52640     */
52641     
52642     /**
52643     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52644     */
52645     enableDragDrop : false,
52646     
52647     /**
52648     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52649     */
52650     enableColumnMove : true,
52651     
52652     /**
52653     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52654     */
52655     enableColumnHide : true,
52656     
52657     /**
52658     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52659     */
52660     enableRowHeightSync : false,
52661     
52662     /**
52663     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52664     */
52665     stripeRows : true,
52666     
52667     /**
52668     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52669     */
52670     autoHeight : false,
52671
52672     /**
52673      * @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.
52674      */
52675     autoExpandColumn : false,
52676
52677     /**
52678     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52679     * Default is 50.
52680     */
52681     autoExpandMin : 50,
52682
52683     /**
52684     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52685     */
52686     autoExpandMax : 1000,
52687
52688     /**
52689     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52690     */
52691     view : null,
52692
52693     /**
52694     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52695     */
52696     loadMask : false,
52697     /**
52698     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52699     */
52700     dropTarget: false,
52701     
52702    
52703     
52704     // private
52705     rendered : false,
52706
52707     /**
52708     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52709     * of a fixed width. Default is false.
52710     */
52711     /**
52712     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52713     */
52714     /**
52715      * Called once after all setup has been completed and the grid is ready to be rendered.
52716      * @return {Roo.grid.Grid} this
52717      */
52718     render : function()
52719     {
52720         var c = this.container;
52721         // try to detect autoHeight/width mode
52722         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52723             this.autoHeight = true;
52724         }
52725         var view = this.getView();
52726         view.init(this);
52727
52728         c.on("click", this.onClick, this);
52729         c.on("dblclick", this.onDblClick, this);
52730         c.on("contextmenu", this.onContextMenu, this);
52731         c.on("keydown", this.onKeyDown, this);
52732         if (Roo.isTouch) {
52733             c.on("touchstart", this.onTouchStart, this);
52734         }
52735
52736         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52737
52738         this.getSelectionModel().init(this);
52739
52740         view.render();
52741
52742         if(this.loadMask){
52743             this.loadMask = new Roo.LoadMask(this.container,
52744                     Roo.apply({store:this.dataSource}, this.loadMask));
52745         }
52746         
52747         
52748         if (this.toolbar && this.toolbar.xtype) {
52749             this.toolbar.container = this.getView().getHeaderPanel(true);
52750             this.toolbar = new Roo.Toolbar(this.toolbar);
52751         }
52752         if (this.footer && this.footer.xtype) {
52753             this.footer.dataSource = this.getDataSource();
52754             this.footer.container = this.getView().getFooterPanel(true);
52755             this.footer = Roo.factory(this.footer, Roo);
52756         }
52757         if (this.dropTarget && this.dropTarget.xtype) {
52758             delete this.dropTarget.xtype;
52759             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52760         }
52761         
52762         
52763         this.rendered = true;
52764         this.fireEvent('render', this);
52765         return this;
52766     },
52767
52768         /**
52769          * Reconfigures the grid to use a different Store and Column Model.
52770          * The View will be bound to the new objects and refreshed.
52771          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52772          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52773          */
52774     reconfigure : function(dataSource, colModel){
52775         if(this.loadMask){
52776             this.loadMask.destroy();
52777             this.loadMask = new Roo.LoadMask(this.container,
52778                     Roo.apply({store:dataSource}, this.loadMask));
52779         }
52780         this.view.bind(dataSource, colModel);
52781         this.dataSource = dataSource;
52782         this.colModel = colModel;
52783         this.view.refresh(true);
52784     },
52785
52786     // private
52787     onKeyDown : function(e){
52788         this.fireEvent("keydown", e);
52789     },
52790
52791     /**
52792      * Destroy this grid.
52793      * @param {Boolean} removeEl True to remove the element
52794      */
52795     destroy : function(removeEl, keepListeners){
52796         if(this.loadMask){
52797             this.loadMask.destroy();
52798         }
52799         var c = this.container;
52800         c.removeAllListeners();
52801         this.view.destroy();
52802         this.colModel.purgeListeners();
52803         if(!keepListeners){
52804             this.purgeListeners();
52805         }
52806         c.update("");
52807         if(removeEl === true){
52808             c.remove();
52809         }
52810     },
52811
52812     // private
52813     processEvent : function(name, e){
52814         // does this fire select???
52815         //Roo.log('grid:processEvent '  + name);
52816         
52817         if (name != 'touchstart' ) {
52818             this.fireEvent(name, e);    
52819         }
52820         
52821         var t = e.getTarget();
52822         var v = this.view;
52823         var header = v.findHeaderIndex(t);
52824         if(header !== false){
52825             var ename = name == 'touchstart' ? 'click' : name;
52826              
52827             this.fireEvent("header" + ename, this, header, e);
52828         }else{
52829             var row = v.findRowIndex(t);
52830             var cell = v.findCellIndex(t);
52831             if (name == 'touchstart') {
52832                 // first touch is always a click.
52833                 // hopefull this happens after selection is updated.?
52834                 name = false;
52835                 
52836                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52837                     var cs = this.selModel.getSelectedCell();
52838                     if (row == cs[0] && cell == cs[1]){
52839                         name = 'dblclick';
52840                     }
52841                 }
52842                 if (typeof(this.selModel.getSelections) != 'undefined') {
52843                     var cs = this.selModel.getSelections();
52844                     var ds = this.dataSource;
52845                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52846                         name = 'dblclick';
52847                     }
52848                 }
52849                 if (!name) {
52850                     return;
52851                 }
52852             }
52853             
52854             
52855             if(row !== false){
52856                 this.fireEvent("row" + name, this, row, e);
52857                 if(cell !== false){
52858                     this.fireEvent("cell" + name, this, row, cell, e);
52859                 }
52860             }
52861         }
52862     },
52863
52864     // private
52865     onClick : function(e){
52866         this.processEvent("click", e);
52867     },
52868    // private
52869     onTouchStart : function(e){
52870         this.processEvent("touchstart", e);
52871     },
52872
52873     // private
52874     onContextMenu : function(e, t){
52875         this.processEvent("contextmenu", e);
52876     },
52877
52878     // private
52879     onDblClick : function(e){
52880         this.processEvent("dblclick", e);
52881     },
52882
52883     // private
52884     walkCells : function(row, col, step, fn, scope){
52885         var cm = this.colModel, clen = cm.getColumnCount();
52886         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52887         if(step < 0){
52888             if(col < 0){
52889                 row--;
52890                 first = false;
52891             }
52892             while(row >= 0){
52893                 if(!first){
52894                     col = clen-1;
52895                 }
52896                 first = false;
52897                 while(col >= 0){
52898                     if(fn.call(scope || this, row, col, cm) === true){
52899                         return [row, col];
52900                     }
52901                     col--;
52902                 }
52903                 row--;
52904             }
52905         } else {
52906             if(col >= clen){
52907                 row++;
52908                 first = false;
52909             }
52910             while(row < rlen){
52911                 if(!first){
52912                     col = 0;
52913                 }
52914                 first = false;
52915                 while(col < clen){
52916                     if(fn.call(scope || this, row, col, cm) === true){
52917                         return [row, col];
52918                     }
52919                     col++;
52920                 }
52921                 row++;
52922             }
52923         }
52924         return null;
52925     },
52926
52927     // private
52928     getSelections : function(){
52929         return this.selModel.getSelections();
52930     },
52931
52932     /**
52933      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52934      * but if manual update is required this method will initiate it.
52935      */
52936     autoSize : function(){
52937         if(this.rendered){
52938             this.view.layout();
52939             if(this.view.adjustForScroll){
52940                 this.view.adjustForScroll();
52941             }
52942         }
52943     },
52944
52945     /**
52946      * Returns the grid's underlying element.
52947      * @return {Element} The element
52948      */
52949     getGridEl : function(){
52950         return this.container;
52951     },
52952
52953     // private for compatibility, overridden by editor grid
52954     stopEditing : function(){},
52955
52956     /**
52957      * Returns the grid's SelectionModel.
52958      * @return {SelectionModel}
52959      */
52960     getSelectionModel : function(){
52961         if(!this.selModel){
52962             this.selModel = new Roo.grid.RowSelectionModel();
52963         }
52964         return this.selModel;
52965     },
52966
52967     /**
52968      * Returns the grid's DataSource.
52969      * @return {DataSource}
52970      */
52971     getDataSource : function(){
52972         return this.dataSource;
52973     },
52974
52975     /**
52976      * Returns the grid's ColumnModel.
52977      * @return {ColumnModel}
52978      */
52979     getColumnModel : function(){
52980         return this.colModel;
52981     },
52982
52983     /**
52984      * Returns the grid's GridView object.
52985      * @return {GridView}
52986      */
52987     getView : function(){
52988         if(!this.view){
52989             this.view = new Roo.grid.GridView(this.viewConfig);
52990         }
52991         return this.view;
52992     },
52993     /**
52994      * Called to get grid's drag proxy text, by default returns this.ddText.
52995      * @return {String}
52996      */
52997     getDragDropText : function(){
52998         var count = this.selModel.getCount();
52999         return String.format(this.ddText, count, count == 1 ? '' : 's');
53000     }
53001 });
53002 /**
53003  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53004  * %0 is replaced with the number of selected rows.
53005  * @type String
53006  */
53007 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53008  * Based on:
53009  * Ext JS Library 1.1.1
53010  * Copyright(c) 2006-2007, Ext JS, LLC.
53011  *
53012  * Originally Released Under LGPL - original licence link has changed is not relivant.
53013  *
53014  * Fork - LGPL
53015  * <script type="text/javascript">
53016  */
53017  
53018 Roo.grid.AbstractGridView = function(){
53019         this.grid = null;
53020         
53021         this.events = {
53022             "beforerowremoved" : true,
53023             "beforerowsinserted" : true,
53024             "beforerefresh" : true,
53025             "rowremoved" : true,
53026             "rowsinserted" : true,
53027             "rowupdated" : true,
53028             "refresh" : true
53029         };
53030     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53031 };
53032
53033 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53034     rowClass : "x-grid-row",
53035     cellClass : "x-grid-cell",
53036     tdClass : "x-grid-td",
53037     hdClass : "x-grid-hd",
53038     splitClass : "x-grid-hd-split",
53039     
53040     init: function(grid){
53041         this.grid = grid;
53042                 var cid = this.grid.getGridEl().id;
53043         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53044         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53045         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53046         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53047         },
53048         
53049     getColumnRenderers : function(){
53050         var renderers = [];
53051         var cm = this.grid.colModel;
53052         var colCount = cm.getColumnCount();
53053         for(var i = 0; i < colCount; i++){
53054             renderers[i] = cm.getRenderer(i);
53055         }
53056         return renderers;
53057     },
53058     
53059     getColumnIds : function(){
53060         var ids = [];
53061         var cm = this.grid.colModel;
53062         var colCount = cm.getColumnCount();
53063         for(var i = 0; i < colCount; i++){
53064             ids[i] = cm.getColumnId(i);
53065         }
53066         return ids;
53067     },
53068     
53069     getDataIndexes : function(){
53070         if(!this.indexMap){
53071             this.indexMap = this.buildIndexMap();
53072         }
53073         return this.indexMap.colToData;
53074     },
53075     
53076     getColumnIndexByDataIndex : function(dataIndex){
53077         if(!this.indexMap){
53078             this.indexMap = this.buildIndexMap();
53079         }
53080         return this.indexMap.dataToCol[dataIndex];
53081     },
53082     
53083     /**
53084      * Set a css style for a column dynamically. 
53085      * @param {Number} colIndex The index of the column
53086      * @param {String} name The css property name
53087      * @param {String} value The css value
53088      */
53089     setCSSStyle : function(colIndex, name, value){
53090         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53091         Roo.util.CSS.updateRule(selector, name, value);
53092     },
53093     
53094     generateRules : function(cm){
53095         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53096         Roo.util.CSS.removeStyleSheet(rulesId);
53097         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53098             var cid = cm.getColumnId(i);
53099             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53100                          this.tdSelector, cid, " {\n}\n",
53101                          this.hdSelector, cid, " {\n}\n",
53102                          this.splitSelector, cid, " {\n}\n");
53103         }
53104         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53105     }
53106 });/*
53107  * Based on:
53108  * Ext JS Library 1.1.1
53109  * Copyright(c) 2006-2007, Ext JS, LLC.
53110  *
53111  * Originally Released Under LGPL - original licence link has changed is not relivant.
53112  *
53113  * Fork - LGPL
53114  * <script type="text/javascript">
53115  */
53116
53117 // private
53118 // This is a support class used internally by the Grid components
53119 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53120     this.grid = grid;
53121     this.view = grid.getView();
53122     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53123     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53124     if(hd2){
53125         this.setHandleElId(Roo.id(hd));
53126         this.setOuterHandleElId(Roo.id(hd2));
53127     }
53128     this.scroll = false;
53129 };
53130 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53131     maxDragWidth: 120,
53132     getDragData : function(e){
53133         var t = Roo.lib.Event.getTarget(e);
53134         var h = this.view.findHeaderCell(t);
53135         if(h){
53136             return {ddel: h.firstChild, header:h};
53137         }
53138         return false;
53139     },
53140
53141     onInitDrag : function(e){
53142         this.view.headersDisabled = true;
53143         var clone = this.dragData.ddel.cloneNode(true);
53144         clone.id = Roo.id();
53145         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53146         this.proxy.update(clone);
53147         return true;
53148     },
53149
53150     afterValidDrop : function(){
53151         var v = this.view;
53152         setTimeout(function(){
53153             v.headersDisabled = false;
53154         }, 50);
53155     },
53156
53157     afterInvalidDrop : function(){
53158         var v = this.view;
53159         setTimeout(function(){
53160             v.headersDisabled = false;
53161         }, 50);
53162     }
53163 });
53164 /*
53165  * Based on:
53166  * Ext JS Library 1.1.1
53167  * Copyright(c) 2006-2007, Ext JS, LLC.
53168  *
53169  * Originally Released Under LGPL - original licence link has changed is not relivant.
53170  *
53171  * Fork - LGPL
53172  * <script type="text/javascript">
53173  */
53174 // private
53175 // This is a support class used internally by the Grid components
53176 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53177     this.grid = grid;
53178     this.view = grid.getView();
53179     // split the proxies so they don't interfere with mouse events
53180     this.proxyTop = Roo.DomHelper.append(document.body, {
53181         cls:"col-move-top", html:"&#160;"
53182     }, true);
53183     this.proxyBottom = Roo.DomHelper.append(document.body, {
53184         cls:"col-move-bottom", html:"&#160;"
53185     }, true);
53186     this.proxyTop.hide = this.proxyBottom.hide = function(){
53187         this.setLeftTop(-100,-100);
53188         this.setStyle("visibility", "hidden");
53189     };
53190     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53191     // temporarily disabled
53192     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53193     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53194 };
53195 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53196     proxyOffsets : [-4, -9],
53197     fly: Roo.Element.fly,
53198
53199     getTargetFromEvent : function(e){
53200         var t = Roo.lib.Event.getTarget(e);
53201         var cindex = this.view.findCellIndex(t);
53202         if(cindex !== false){
53203             return this.view.getHeaderCell(cindex);
53204         }
53205         return null;
53206     },
53207
53208     nextVisible : function(h){
53209         var v = this.view, cm = this.grid.colModel;
53210         h = h.nextSibling;
53211         while(h){
53212             if(!cm.isHidden(v.getCellIndex(h))){
53213                 return h;
53214             }
53215             h = h.nextSibling;
53216         }
53217         return null;
53218     },
53219
53220     prevVisible : function(h){
53221         var v = this.view, cm = this.grid.colModel;
53222         h = h.prevSibling;
53223         while(h){
53224             if(!cm.isHidden(v.getCellIndex(h))){
53225                 return h;
53226             }
53227             h = h.prevSibling;
53228         }
53229         return null;
53230     },
53231
53232     positionIndicator : function(h, n, e){
53233         var x = Roo.lib.Event.getPageX(e);
53234         var r = Roo.lib.Dom.getRegion(n.firstChild);
53235         var px, pt, py = r.top + this.proxyOffsets[1];
53236         if((r.right - x) <= (r.right-r.left)/2){
53237             px = r.right+this.view.borderWidth;
53238             pt = "after";
53239         }else{
53240             px = r.left;
53241             pt = "before";
53242         }
53243         var oldIndex = this.view.getCellIndex(h);
53244         var newIndex = this.view.getCellIndex(n);
53245
53246         if(this.grid.colModel.isFixed(newIndex)){
53247             return false;
53248         }
53249
53250         var locked = this.grid.colModel.isLocked(newIndex);
53251
53252         if(pt == "after"){
53253             newIndex++;
53254         }
53255         if(oldIndex < newIndex){
53256             newIndex--;
53257         }
53258         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53259             return false;
53260         }
53261         px +=  this.proxyOffsets[0];
53262         this.proxyTop.setLeftTop(px, py);
53263         this.proxyTop.show();
53264         if(!this.bottomOffset){
53265             this.bottomOffset = this.view.mainHd.getHeight();
53266         }
53267         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53268         this.proxyBottom.show();
53269         return pt;
53270     },
53271
53272     onNodeEnter : function(n, dd, e, data){
53273         if(data.header != n){
53274             this.positionIndicator(data.header, n, e);
53275         }
53276     },
53277
53278     onNodeOver : function(n, dd, e, data){
53279         var result = false;
53280         if(data.header != n){
53281             result = this.positionIndicator(data.header, n, e);
53282         }
53283         if(!result){
53284             this.proxyTop.hide();
53285             this.proxyBottom.hide();
53286         }
53287         return result ? this.dropAllowed : this.dropNotAllowed;
53288     },
53289
53290     onNodeOut : function(n, dd, e, data){
53291         this.proxyTop.hide();
53292         this.proxyBottom.hide();
53293     },
53294
53295     onNodeDrop : function(n, dd, e, data){
53296         var h = data.header;
53297         if(h != n){
53298             var cm = this.grid.colModel;
53299             var x = Roo.lib.Event.getPageX(e);
53300             var r = Roo.lib.Dom.getRegion(n.firstChild);
53301             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53302             var oldIndex = this.view.getCellIndex(h);
53303             var newIndex = this.view.getCellIndex(n);
53304             var locked = cm.isLocked(newIndex);
53305             if(pt == "after"){
53306                 newIndex++;
53307             }
53308             if(oldIndex < newIndex){
53309                 newIndex--;
53310             }
53311             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53312                 return false;
53313             }
53314             cm.setLocked(oldIndex, locked, true);
53315             cm.moveColumn(oldIndex, newIndex);
53316             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53317             return true;
53318         }
53319         return false;
53320     }
53321 });
53322 /*
53323  * Based on:
53324  * Ext JS Library 1.1.1
53325  * Copyright(c) 2006-2007, Ext JS, LLC.
53326  *
53327  * Originally Released Under LGPL - original licence link has changed is not relivant.
53328  *
53329  * Fork - LGPL
53330  * <script type="text/javascript">
53331  */
53332   
53333 /**
53334  * @class Roo.grid.GridView
53335  * @extends Roo.util.Observable
53336  *
53337  * @constructor
53338  * @param {Object} config
53339  */
53340 Roo.grid.GridView = function(config){
53341     Roo.grid.GridView.superclass.constructor.call(this);
53342     this.el = null;
53343
53344     Roo.apply(this, config);
53345 };
53346
53347 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53348
53349     unselectable :  'unselectable="on"',
53350     unselectableCls :  'x-unselectable',
53351     
53352     
53353     rowClass : "x-grid-row",
53354
53355     cellClass : "x-grid-col",
53356
53357     tdClass : "x-grid-td",
53358
53359     hdClass : "x-grid-hd",
53360
53361     splitClass : "x-grid-split",
53362
53363     sortClasses : ["sort-asc", "sort-desc"],
53364
53365     enableMoveAnim : false,
53366
53367     hlColor: "C3DAF9",
53368
53369     dh : Roo.DomHelper,
53370
53371     fly : Roo.Element.fly,
53372
53373     css : Roo.util.CSS,
53374
53375     borderWidth: 1,
53376
53377     splitOffset: 3,
53378
53379     scrollIncrement : 22,
53380
53381     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53382
53383     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53384
53385     bind : function(ds, cm){
53386         if(this.ds){
53387             this.ds.un("load", this.onLoad, this);
53388             this.ds.un("datachanged", this.onDataChange, this);
53389             this.ds.un("add", this.onAdd, this);
53390             this.ds.un("remove", this.onRemove, this);
53391             this.ds.un("update", this.onUpdate, this);
53392             this.ds.un("clear", this.onClear, this);
53393         }
53394         if(ds){
53395             ds.on("load", this.onLoad, this);
53396             ds.on("datachanged", this.onDataChange, this);
53397             ds.on("add", this.onAdd, this);
53398             ds.on("remove", this.onRemove, this);
53399             ds.on("update", this.onUpdate, this);
53400             ds.on("clear", this.onClear, this);
53401         }
53402         this.ds = ds;
53403
53404         if(this.cm){
53405             this.cm.un("widthchange", this.onColWidthChange, this);
53406             this.cm.un("headerchange", this.onHeaderChange, this);
53407             this.cm.un("hiddenchange", this.onHiddenChange, this);
53408             this.cm.un("columnmoved", this.onColumnMove, this);
53409             this.cm.un("columnlockchange", this.onColumnLock, this);
53410         }
53411         if(cm){
53412             this.generateRules(cm);
53413             cm.on("widthchange", this.onColWidthChange, this);
53414             cm.on("headerchange", this.onHeaderChange, this);
53415             cm.on("hiddenchange", this.onHiddenChange, this);
53416             cm.on("columnmoved", this.onColumnMove, this);
53417             cm.on("columnlockchange", this.onColumnLock, this);
53418         }
53419         this.cm = cm;
53420     },
53421
53422     init: function(grid){
53423         Roo.grid.GridView.superclass.init.call(this, grid);
53424
53425         this.bind(grid.dataSource, grid.colModel);
53426
53427         grid.on("headerclick", this.handleHeaderClick, this);
53428
53429         if(grid.trackMouseOver){
53430             grid.on("mouseover", this.onRowOver, this);
53431             grid.on("mouseout", this.onRowOut, this);
53432         }
53433         grid.cancelTextSelection = function(){};
53434         this.gridId = grid.id;
53435
53436         var tpls = this.templates || {};
53437
53438         if(!tpls.master){
53439             tpls.master = new Roo.Template(
53440                '<div class="x-grid" hidefocus="true">',
53441                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53442                   '<div class="x-grid-topbar"></div>',
53443                   '<div class="x-grid-scroller"><div></div></div>',
53444                   '<div class="x-grid-locked">',
53445                       '<div class="x-grid-header">{lockedHeader}</div>',
53446                       '<div class="x-grid-body">{lockedBody}</div>',
53447                   "</div>",
53448                   '<div class="x-grid-viewport">',
53449                       '<div class="x-grid-header">{header}</div>',
53450                       '<div class="x-grid-body">{body}</div>',
53451                   "</div>",
53452                   '<div class="x-grid-bottombar"></div>',
53453                  
53454                   '<div class="x-grid-resize-proxy">&#160;</div>',
53455                "</div>"
53456             );
53457             tpls.master.disableformats = true;
53458         }
53459
53460         if(!tpls.header){
53461             tpls.header = new Roo.Template(
53462                '<table border="0" cellspacing="0" cellpadding="0">',
53463                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53464                "</table>{splits}"
53465             );
53466             tpls.header.disableformats = true;
53467         }
53468         tpls.header.compile();
53469
53470         if(!tpls.hcell){
53471             tpls.hcell = new Roo.Template(
53472                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53473                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53474                 "</div></td>"
53475              );
53476              tpls.hcell.disableFormats = true;
53477         }
53478         tpls.hcell.compile();
53479
53480         if(!tpls.hsplit){
53481             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53482                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53483             tpls.hsplit.disableFormats = true;
53484         }
53485         tpls.hsplit.compile();
53486
53487         if(!tpls.body){
53488             tpls.body = new Roo.Template(
53489                '<table border="0" cellspacing="0" cellpadding="0">',
53490                "<tbody>{rows}</tbody>",
53491                "</table>"
53492             );
53493             tpls.body.disableFormats = true;
53494         }
53495         tpls.body.compile();
53496
53497         if(!tpls.row){
53498             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53499             tpls.row.disableFormats = true;
53500         }
53501         tpls.row.compile();
53502
53503         if(!tpls.cell){
53504             tpls.cell = new Roo.Template(
53505                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53506                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53507                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53508                 "</td>"
53509             );
53510             tpls.cell.disableFormats = true;
53511         }
53512         tpls.cell.compile();
53513
53514         this.templates = tpls;
53515     },
53516
53517     // remap these for backwards compat
53518     onColWidthChange : function(){
53519         this.updateColumns.apply(this, arguments);
53520     },
53521     onHeaderChange : function(){
53522         this.updateHeaders.apply(this, arguments);
53523     }, 
53524     onHiddenChange : function(){
53525         this.handleHiddenChange.apply(this, arguments);
53526     },
53527     onColumnMove : function(){
53528         this.handleColumnMove.apply(this, arguments);
53529     },
53530     onColumnLock : function(){
53531         this.handleLockChange.apply(this, arguments);
53532     },
53533
53534     onDataChange : function(){
53535         this.refresh();
53536         this.updateHeaderSortState();
53537     },
53538
53539     onClear : function(){
53540         this.refresh();
53541     },
53542
53543     onUpdate : function(ds, record){
53544         this.refreshRow(record);
53545     },
53546
53547     refreshRow : function(record){
53548         var ds = this.ds, index;
53549         if(typeof record == 'number'){
53550             index = record;
53551             record = ds.getAt(index);
53552         }else{
53553             index = ds.indexOf(record);
53554         }
53555         this.insertRows(ds, index, index, true);
53556         this.onRemove(ds, record, index+1, true);
53557         this.syncRowHeights(index, index);
53558         this.layout();
53559         this.fireEvent("rowupdated", this, index, record);
53560     },
53561
53562     onAdd : function(ds, records, index){
53563         this.insertRows(ds, index, index + (records.length-1));
53564     },
53565
53566     onRemove : function(ds, record, index, isUpdate){
53567         if(isUpdate !== true){
53568             this.fireEvent("beforerowremoved", this, index, record);
53569         }
53570         var bt = this.getBodyTable(), lt = this.getLockedTable();
53571         if(bt.rows[index]){
53572             bt.firstChild.removeChild(bt.rows[index]);
53573         }
53574         if(lt.rows[index]){
53575             lt.firstChild.removeChild(lt.rows[index]);
53576         }
53577         if(isUpdate !== true){
53578             this.stripeRows(index);
53579             this.syncRowHeights(index, index);
53580             this.layout();
53581             this.fireEvent("rowremoved", this, index, record);
53582         }
53583     },
53584
53585     onLoad : function(){
53586         this.scrollToTop();
53587     },
53588
53589     /**
53590      * Scrolls the grid to the top
53591      */
53592     scrollToTop : function(){
53593         if(this.scroller){
53594             this.scroller.dom.scrollTop = 0;
53595             this.syncScroll();
53596         }
53597     },
53598
53599     /**
53600      * Gets a panel in the header of the grid that can be used for toolbars etc.
53601      * After modifying the contents of this panel a call to grid.autoSize() may be
53602      * required to register any changes in size.
53603      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53604      * @return Roo.Element
53605      */
53606     getHeaderPanel : function(doShow){
53607         if(doShow){
53608             this.headerPanel.show();
53609         }
53610         return this.headerPanel;
53611     },
53612
53613     /**
53614      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53615      * After modifying the contents of this panel a call to grid.autoSize() may be
53616      * required to register any changes in size.
53617      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53618      * @return Roo.Element
53619      */
53620     getFooterPanel : function(doShow){
53621         if(doShow){
53622             this.footerPanel.show();
53623         }
53624         return this.footerPanel;
53625     },
53626
53627     initElements : function(){
53628         var E = Roo.Element;
53629         var el = this.grid.getGridEl().dom.firstChild;
53630         var cs = el.childNodes;
53631
53632         this.el = new E(el);
53633         
53634          this.focusEl = new E(el.firstChild);
53635         this.focusEl.swallowEvent("click", true);
53636         
53637         this.headerPanel = new E(cs[1]);
53638         this.headerPanel.enableDisplayMode("block");
53639
53640         this.scroller = new E(cs[2]);
53641         this.scrollSizer = new E(this.scroller.dom.firstChild);
53642
53643         this.lockedWrap = new E(cs[3]);
53644         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53645         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53646
53647         this.mainWrap = new E(cs[4]);
53648         this.mainHd = new E(this.mainWrap.dom.firstChild);
53649         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53650
53651         this.footerPanel = new E(cs[5]);
53652         this.footerPanel.enableDisplayMode("block");
53653
53654         this.resizeProxy = new E(cs[6]);
53655
53656         this.headerSelector = String.format(
53657            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53658            this.lockedHd.id, this.mainHd.id
53659         );
53660
53661         this.splitterSelector = String.format(
53662            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53663            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53664         );
53665     },
53666     idToCssName : function(s)
53667     {
53668         return s.replace(/[^a-z0-9]+/ig, '-');
53669     },
53670
53671     getHeaderCell : function(index){
53672         return Roo.DomQuery.select(this.headerSelector)[index];
53673     },
53674
53675     getHeaderCellMeasure : function(index){
53676         return this.getHeaderCell(index).firstChild;
53677     },
53678
53679     getHeaderCellText : function(index){
53680         return this.getHeaderCell(index).firstChild.firstChild;
53681     },
53682
53683     getLockedTable : function(){
53684         return this.lockedBody.dom.firstChild;
53685     },
53686
53687     getBodyTable : function(){
53688         return this.mainBody.dom.firstChild;
53689     },
53690
53691     getLockedRow : function(index){
53692         return this.getLockedTable().rows[index];
53693     },
53694
53695     getRow : function(index){
53696         return this.getBodyTable().rows[index];
53697     },
53698
53699     getRowComposite : function(index){
53700         if(!this.rowEl){
53701             this.rowEl = new Roo.CompositeElementLite();
53702         }
53703         var els = [], lrow, mrow;
53704         if(lrow = this.getLockedRow(index)){
53705             els.push(lrow);
53706         }
53707         if(mrow = this.getRow(index)){
53708             els.push(mrow);
53709         }
53710         this.rowEl.elements = els;
53711         return this.rowEl;
53712     },
53713     /**
53714      * Gets the 'td' of the cell
53715      * 
53716      * @param {Integer} rowIndex row to select
53717      * @param {Integer} colIndex column to select
53718      * 
53719      * @return {Object} 
53720      */
53721     getCell : function(rowIndex, colIndex){
53722         var locked = this.cm.getLockedCount();
53723         var source;
53724         if(colIndex < locked){
53725             source = this.lockedBody.dom.firstChild;
53726         }else{
53727             source = this.mainBody.dom.firstChild;
53728             colIndex -= locked;
53729         }
53730         return source.rows[rowIndex].childNodes[colIndex];
53731     },
53732
53733     getCellText : function(rowIndex, colIndex){
53734         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53735     },
53736
53737     getCellBox : function(cell){
53738         var b = this.fly(cell).getBox();
53739         if(Roo.isOpera){ // opera fails to report the Y
53740             b.y = cell.offsetTop + this.mainBody.getY();
53741         }
53742         return b;
53743     },
53744
53745     getCellIndex : function(cell){
53746         var id = String(cell.className).match(this.cellRE);
53747         if(id){
53748             return parseInt(id[1], 10);
53749         }
53750         return 0;
53751     },
53752
53753     findHeaderIndex : function(n){
53754         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53755         return r ? this.getCellIndex(r) : false;
53756     },
53757
53758     findHeaderCell : function(n){
53759         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53760         return r ? r : false;
53761     },
53762
53763     findRowIndex : function(n){
53764         if(!n){
53765             return false;
53766         }
53767         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53768         return r ? r.rowIndex : false;
53769     },
53770
53771     findCellIndex : function(node){
53772         var stop = this.el.dom;
53773         while(node && node != stop){
53774             if(this.findRE.test(node.className)){
53775                 return this.getCellIndex(node);
53776             }
53777             node = node.parentNode;
53778         }
53779         return false;
53780     },
53781
53782     getColumnId : function(index){
53783         return this.cm.getColumnId(index);
53784     },
53785
53786     getSplitters : function()
53787     {
53788         if(this.splitterSelector){
53789            return Roo.DomQuery.select(this.splitterSelector);
53790         }else{
53791             return null;
53792       }
53793     },
53794
53795     getSplitter : function(index){
53796         return this.getSplitters()[index];
53797     },
53798
53799     onRowOver : function(e, t){
53800         var row;
53801         if((row = this.findRowIndex(t)) !== false){
53802             this.getRowComposite(row).addClass("x-grid-row-over");
53803         }
53804     },
53805
53806     onRowOut : function(e, t){
53807         var row;
53808         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53809             this.getRowComposite(row).removeClass("x-grid-row-over");
53810         }
53811     },
53812
53813     renderHeaders : function(){
53814         var cm = this.cm;
53815         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53816         var cb = [], lb = [], sb = [], lsb = [], p = {};
53817         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53818             p.cellId = "x-grid-hd-0-" + i;
53819             p.splitId = "x-grid-csplit-0-" + i;
53820             p.id = cm.getColumnId(i);
53821             p.title = cm.getColumnTooltip(i) || "";
53822             p.value = cm.getColumnHeader(i) || "";
53823             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53824             if(!cm.isLocked(i)){
53825                 cb[cb.length] = ct.apply(p);
53826                 sb[sb.length] = st.apply(p);
53827             }else{
53828                 lb[lb.length] = ct.apply(p);
53829                 lsb[lsb.length] = st.apply(p);
53830             }
53831         }
53832         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53833                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53834     },
53835
53836     updateHeaders : function(){
53837         var html = this.renderHeaders();
53838         this.lockedHd.update(html[0]);
53839         this.mainHd.update(html[1]);
53840     },
53841
53842     /**
53843      * Focuses the specified row.
53844      * @param {Number} row The row index
53845      */
53846     focusRow : function(row)
53847     {
53848         //Roo.log('GridView.focusRow');
53849         var x = this.scroller.dom.scrollLeft;
53850         this.focusCell(row, 0, false);
53851         this.scroller.dom.scrollLeft = x;
53852     },
53853
53854     /**
53855      * Focuses the specified cell.
53856      * @param {Number} row The row index
53857      * @param {Number} col The column index
53858      * @param {Boolean} hscroll false to disable horizontal scrolling
53859      */
53860     focusCell : function(row, col, hscroll)
53861     {
53862         //Roo.log('GridView.focusCell');
53863         var el = this.ensureVisible(row, col, hscroll);
53864         this.focusEl.alignTo(el, "tl-tl");
53865         if(Roo.isGecko){
53866             this.focusEl.focus();
53867         }else{
53868             this.focusEl.focus.defer(1, this.focusEl);
53869         }
53870     },
53871
53872     /**
53873      * Scrolls the specified cell into view
53874      * @param {Number} row The row index
53875      * @param {Number} col The column index
53876      * @param {Boolean} hscroll false to disable horizontal scrolling
53877      */
53878     ensureVisible : function(row, col, hscroll)
53879     {
53880         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53881         //return null; //disable for testing.
53882         if(typeof row != "number"){
53883             row = row.rowIndex;
53884         }
53885         if(row < 0 && row >= this.ds.getCount()){
53886             return  null;
53887         }
53888         col = (col !== undefined ? col : 0);
53889         var cm = this.grid.colModel;
53890         while(cm.isHidden(col)){
53891             col++;
53892         }
53893
53894         var el = this.getCell(row, col);
53895         if(!el){
53896             return null;
53897         }
53898         var c = this.scroller.dom;
53899
53900         var ctop = parseInt(el.offsetTop, 10);
53901         var cleft = parseInt(el.offsetLeft, 10);
53902         var cbot = ctop + el.offsetHeight;
53903         var cright = cleft + el.offsetWidth;
53904         
53905         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53906         var stop = parseInt(c.scrollTop, 10);
53907         var sleft = parseInt(c.scrollLeft, 10);
53908         var sbot = stop + ch;
53909         var sright = sleft + c.clientWidth;
53910         /*
53911         Roo.log('GridView.ensureVisible:' +
53912                 ' ctop:' + ctop +
53913                 ' c.clientHeight:' + c.clientHeight +
53914                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53915                 ' stop:' + stop +
53916                 ' cbot:' + cbot +
53917                 ' sbot:' + sbot +
53918                 ' ch:' + ch  
53919                 );
53920         */
53921         if(ctop < stop){
53922              c.scrollTop = ctop;
53923             //Roo.log("set scrolltop to ctop DISABLE?");
53924         }else if(cbot > sbot){
53925             //Roo.log("set scrolltop to cbot-ch");
53926             c.scrollTop = cbot-ch;
53927         }
53928         
53929         if(hscroll !== false){
53930             if(cleft < sleft){
53931                 c.scrollLeft = cleft;
53932             }else if(cright > sright){
53933                 c.scrollLeft = cright-c.clientWidth;
53934             }
53935         }
53936          
53937         return el;
53938     },
53939
53940     updateColumns : function(){
53941         this.grid.stopEditing();
53942         var cm = this.grid.colModel, colIds = this.getColumnIds();
53943         //var totalWidth = cm.getTotalWidth();
53944         var pos = 0;
53945         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53946             //if(cm.isHidden(i)) continue;
53947             var w = cm.getColumnWidth(i);
53948             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53949             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53950         }
53951         this.updateSplitters();
53952     },
53953
53954     generateRules : function(cm){
53955         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53956         Roo.util.CSS.removeStyleSheet(rulesId);
53957         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53958             var cid = cm.getColumnId(i);
53959             var align = '';
53960             if(cm.config[i].align){
53961                 align = 'text-align:'+cm.config[i].align+';';
53962             }
53963             var hidden = '';
53964             if(cm.isHidden(i)){
53965                 hidden = 'display:none;';
53966             }
53967             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53968             ruleBuf.push(
53969                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53970                     this.hdSelector, cid, " {\n", align, width, "}\n",
53971                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53972                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53973         }
53974         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53975     },
53976
53977     updateSplitters : function(){
53978         var cm = this.cm, s = this.getSplitters();
53979         if(s){ // splitters not created yet
53980             var pos = 0, locked = true;
53981             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53982                 if(cm.isHidden(i)) continue;
53983                 var w = cm.getColumnWidth(i); // make sure it's a number
53984                 if(!cm.isLocked(i) && locked){
53985                     pos = 0;
53986                     locked = false;
53987                 }
53988                 pos += w;
53989                 s[i].style.left = (pos-this.splitOffset) + "px";
53990             }
53991         }
53992     },
53993
53994     handleHiddenChange : function(colModel, colIndex, hidden){
53995         if(hidden){
53996             this.hideColumn(colIndex);
53997         }else{
53998             this.unhideColumn(colIndex);
53999         }
54000     },
54001
54002     hideColumn : function(colIndex){
54003         var cid = this.getColumnId(colIndex);
54004         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54005         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54006         if(Roo.isSafari){
54007             this.updateHeaders();
54008         }
54009         this.updateSplitters();
54010         this.layout();
54011     },
54012
54013     unhideColumn : function(colIndex){
54014         var cid = this.getColumnId(colIndex);
54015         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54016         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54017
54018         if(Roo.isSafari){
54019             this.updateHeaders();
54020         }
54021         this.updateSplitters();
54022         this.layout();
54023     },
54024
54025     insertRows : function(dm, firstRow, lastRow, isUpdate){
54026         if(firstRow == 0 && lastRow == dm.getCount()-1){
54027             this.refresh();
54028         }else{
54029             if(!isUpdate){
54030                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54031             }
54032             var s = this.getScrollState();
54033             var markup = this.renderRows(firstRow, lastRow);
54034             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54035             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54036             this.restoreScroll(s);
54037             if(!isUpdate){
54038                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54039                 this.syncRowHeights(firstRow, lastRow);
54040                 this.stripeRows(firstRow);
54041                 this.layout();
54042             }
54043         }
54044     },
54045
54046     bufferRows : function(markup, target, index){
54047         var before = null, trows = target.rows, tbody = target.tBodies[0];
54048         if(index < trows.length){
54049             before = trows[index];
54050         }
54051         var b = document.createElement("div");
54052         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54053         var rows = b.firstChild.rows;
54054         for(var i = 0, len = rows.length; i < len; i++){
54055             if(before){
54056                 tbody.insertBefore(rows[0], before);
54057             }else{
54058                 tbody.appendChild(rows[0]);
54059             }
54060         }
54061         b.innerHTML = "";
54062         b = null;
54063     },
54064
54065     deleteRows : function(dm, firstRow, lastRow){
54066         if(dm.getRowCount()<1){
54067             this.fireEvent("beforerefresh", this);
54068             this.mainBody.update("");
54069             this.lockedBody.update("");
54070             this.fireEvent("refresh", this);
54071         }else{
54072             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54073             var bt = this.getBodyTable();
54074             var tbody = bt.firstChild;
54075             var rows = bt.rows;
54076             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54077                 tbody.removeChild(rows[firstRow]);
54078             }
54079             this.stripeRows(firstRow);
54080             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54081         }
54082     },
54083
54084     updateRows : function(dataSource, firstRow, lastRow){
54085         var s = this.getScrollState();
54086         this.refresh();
54087         this.restoreScroll(s);
54088     },
54089
54090     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54091         if(!noRefresh){
54092            this.refresh();
54093         }
54094         this.updateHeaderSortState();
54095     },
54096
54097     getScrollState : function(){
54098         
54099         var sb = this.scroller.dom;
54100         return {left: sb.scrollLeft, top: sb.scrollTop};
54101     },
54102
54103     stripeRows : function(startRow){
54104         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54105             return;
54106         }
54107         startRow = startRow || 0;
54108         var rows = this.getBodyTable().rows;
54109         var lrows = this.getLockedTable().rows;
54110         var cls = ' x-grid-row-alt ';
54111         for(var i = startRow, len = rows.length; i < len; i++){
54112             var row = rows[i], lrow = lrows[i];
54113             var isAlt = ((i+1) % 2 == 0);
54114             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54115             if(isAlt == hasAlt){
54116                 continue;
54117             }
54118             if(isAlt){
54119                 row.className += " x-grid-row-alt";
54120             }else{
54121                 row.className = row.className.replace("x-grid-row-alt", "");
54122             }
54123             if(lrow){
54124                 lrow.className = row.className;
54125             }
54126         }
54127     },
54128
54129     restoreScroll : function(state){
54130         //Roo.log('GridView.restoreScroll');
54131         var sb = this.scroller.dom;
54132         sb.scrollLeft = state.left;
54133         sb.scrollTop = state.top;
54134         this.syncScroll();
54135     },
54136
54137     syncScroll : function(){
54138         //Roo.log('GridView.syncScroll');
54139         var sb = this.scroller.dom;
54140         var sh = this.mainHd.dom;
54141         var bs = this.mainBody.dom;
54142         var lv = this.lockedBody.dom;
54143         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54144         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54145     },
54146
54147     handleScroll : function(e){
54148         this.syncScroll();
54149         var sb = this.scroller.dom;
54150         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54151         e.stopEvent();
54152     },
54153
54154     handleWheel : function(e){
54155         var d = e.getWheelDelta();
54156         this.scroller.dom.scrollTop -= d*22;
54157         // set this here to prevent jumpy scrolling on large tables
54158         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54159         e.stopEvent();
54160     },
54161
54162     renderRows : function(startRow, endRow){
54163         // pull in all the crap needed to render rows
54164         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54165         var colCount = cm.getColumnCount();
54166
54167         if(ds.getCount() < 1){
54168             return ["", ""];
54169         }
54170
54171         // build a map for all the columns
54172         var cs = [];
54173         for(var i = 0; i < colCount; i++){
54174             var name = cm.getDataIndex(i);
54175             cs[i] = {
54176                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54177                 renderer : cm.getRenderer(i),
54178                 id : cm.getColumnId(i),
54179                 locked : cm.isLocked(i)
54180             };
54181         }
54182
54183         startRow = startRow || 0;
54184         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54185
54186         // records to render
54187         var rs = ds.getRange(startRow, endRow);
54188
54189         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54190     },
54191
54192     // As much as I hate to duplicate code, this was branched because FireFox really hates
54193     // [].join("") on strings. The performance difference was substantial enough to
54194     // branch this function
54195     doRender : Roo.isGecko ?
54196             function(cs, rs, ds, startRow, colCount, stripe){
54197                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54198                 // buffers
54199                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54200                 
54201                 var hasListener = this.grid.hasListener('rowclass');
54202                 var rowcfg = {};
54203                 for(var j = 0, len = rs.length; j < len; j++){
54204                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54205                     for(var i = 0; i < colCount; i++){
54206                         c = cs[i];
54207                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54208                         p.id = c.id;
54209                         p.css = p.attr = "";
54210                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54211                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54212                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54213                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54214                         }
54215                         var markup = ct.apply(p);
54216                         if(!c.locked){
54217                             cb+= markup;
54218                         }else{
54219                             lcb+= markup;
54220                         }
54221                     }
54222                     var alt = [];
54223                     if(stripe && ((rowIndex+1) % 2 == 0)){
54224                         alt.push("x-grid-row-alt")
54225                     }
54226                     if(r.dirty){
54227                         alt.push(  " x-grid-dirty-row");
54228                     }
54229                     rp.cells = lcb;
54230                     if(this.getRowClass){
54231                         alt.push(this.getRowClass(r, rowIndex));
54232                     }
54233                     if (hasListener) {
54234                         rowcfg = {
54235                              
54236                             record: r,
54237                             rowIndex : rowIndex,
54238                             rowClass : ''
54239                         }
54240                         this.grid.fireEvent('rowclass', this, rowcfg);
54241                         alt.push(rowcfg.rowClass);
54242                     }
54243                     rp.alt = alt.join(" ");
54244                     lbuf+= rt.apply(rp);
54245                     rp.cells = cb;
54246                     buf+=  rt.apply(rp);
54247                 }
54248                 return [lbuf, buf];
54249             } :
54250             function(cs, rs, ds, startRow, colCount, stripe){
54251                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54252                 // buffers
54253                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54254                 var hasListener = this.grid.hasListener('rowclass');
54255  
54256                 var rowcfg = {};
54257                 for(var j = 0, len = rs.length; j < len; j++){
54258                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54259                     for(var i = 0; i < colCount; i++){
54260                         c = cs[i];
54261                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54262                         p.id = c.id;
54263                         p.css = p.attr = "";
54264                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54265                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54266                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54267                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54268                         }
54269                         
54270                         var markup = ct.apply(p);
54271                         if(!c.locked){
54272                             cb[cb.length] = markup;
54273                         }else{
54274                             lcb[lcb.length] = markup;
54275                         }
54276                     }
54277                     var alt = [];
54278                     if(stripe && ((rowIndex+1) % 2 == 0)){
54279                         alt.push( "x-grid-row-alt");
54280                     }
54281                     if(r.dirty){
54282                         alt.push(" x-grid-dirty-row");
54283                     }
54284                     rp.cells = lcb;
54285                     if(this.getRowClass){
54286                         alt.push( this.getRowClass(r, rowIndex));
54287                     }
54288                     if (hasListener) {
54289                         rowcfg = {
54290                              
54291                             record: r,
54292                             rowIndex : rowIndex,
54293                             rowClass : ''
54294                         }
54295                         this.grid.fireEvent('rowclass', this, rowcfg);
54296                         alt.push(rowcfg.rowClass);
54297                     }
54298                     rp.alt = alt.join(" ");
54299                     rp.cells = lcb.join("");
54300                     lbuf[lbuf.length] = rt.apply(rp);
54301                     rp.cells = cb.join("");
54302                     buf[buf.length] =  rt.apply(rp);
54303                 }
54304                 return [lbuf.join(""), buf.join("")];
54305             },
54306
54307     renderBody : function(){
54308         var markup = this.renderRows();
54309         var bt = this.templates.body;
54310         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54311     },
54312
54313     /**
54314      * Refreshes the grid
54315      * @param {Boolean} headersToo
54316      */
54317     refresh : function(headersToo){
54318         this.fireEvent("beforerefresh", this);
54319         this.grid.stopEditing();
54320         var result = this.renderBody();
54321         this.lockedBody.update(result[0]);
54322         this.mainBody.update(result[1]);
54323         if(headersToo === true){
54324             this.updateHeaders();
54325             this.updateColumns();
54326             this.updateSplitters();
54327             this.updateHeaderSortState();
54328         }
54329         this.syncRowHeights();
54330         this.layout();
54331         this.fireEvent("refresh", this);
54332     },
54333
54334     handleColumnMove : function(cm, oldIndex, newIndex){
54335         this.indexMap = null;
54336         var s = this.getScrollState();
54337         this.refresh(true);
54338         this.restoreScroll(s);
54339         this.afterMove(newIndex);
54340     },
54341
54342     afterMove : function(colIndex){
54343         if(this.enableMoveAnim && Roo.enableFx){
54344             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54345         }
54346         // if multisort - fix sortOrder, and reload..
54347         if (this.grid.dataSource.multiSort) {
54348             // the we can call sort again..
54349             var dm = this.grid.dataSource;
54350             var cm = this.grid.colModel;
54351             var so = [];
54352             for(var i = 0; i < cm.config.length; i++ ) {
54353                 
54354                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54355                     continue; // dont' bother, it's not in sort list or being set.
54356                 }
54357                 
54358                 so.push(cm.config[i].dataIndex);
54359             };
54360             dm.sortOrder = so;
54361             dm.load(dm.lastOptions);
54362             
54363             
54364         }
54365         
54366     },
54367
54368     updateCell : function(dm, rowIndex, dataIndex){
54369         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54370         if(typeof colIndex == "undefined"){ // not present in grid
54371             return;
54372         }
54373         var cm = this.grid.colModel;
54374         var cell = this.getCell(rowIndex, colIndex);
54375         var cellText = this.getCellText(rowIndex, colIndex);
54376
54377         var p = {
54378             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54379             id : cm.getColumnId(colIndex),
54380             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54381         };
54382         var renderer = cm.getRenderer(colIndex);
54383         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54384         if(typeof val == "undefined" || val === "") val = "&#160;";
54385         cellText.innerHTML = val;
54386         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54387         this.syncRowHeights(rowIndex, rowIndex);
54388     },
54389
54390     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54391         var maxWidth = 0;
54392         if(this.grid.autoSizeHeaders){
54393             var h = this.getHeaderCellMeasure(colIndex);
54394             maxWidth = Math.max(maxWidth, h.scrollWidth);
54395         }
54396         var tb, index;
54397         if(this.cm.isLocked(colIndex)){
54398             tb = this.getLockedTable();
54399             index = colIndex;
54400         }else{
54401             tb = this.getBodyTable();
54402             index = colIndex - this.cm.getLockedCount();
54403         }
54404         if(tb && tb.rows){
54405             var rows = tb.rows;
54406             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54407             for(var i = 0; i < stopIndex; i++){
54408                 var cell = rows[i].childNodes[index].firstChild;
54409                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54410             }
54411         }
54412         return maxWidth + /*margin for error in IE*/ 5;
54413     },
54414     /**
54415      * Autofit a column to its content.
54416      * @param {Number} colIndex
54417      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54418      */
54419      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54420          if(this.cm.isHidden(colIndex)){
54421              return; // can't calc a hidden column
54422          }
54423         if(forceMinSize){
54424             var cid = this.cm.getColumnId(colIndex);
54425             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54426            if(this.grid.autoSizeHeaders){
54427                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54428            }
54429         }
54430         var newWidth = this.calcColumnWidth(colIndex);
54431         this.cm.setColumnWidth(colIndex,
54432             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54433         if(!suppressEvent){
54434             this.grid.fireEvent("columnresize", colIndex, newWidth);
54435         }
54436     },
54437
54438     /**
54439      * Autofits all columns to their content and then expands to fit any extra space in the grid
54440      */
54441      autoSizeColumns : function(){
54442         var cm = this.grid.colModel;
54443         var colCount = cm.getColumnCount();
54444         for(var i = 0; i < colCount; i++){
54445             this.autoSizeColumn(i, true, true);
54446         }
54447         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54448             this.fitColumns();
54449         }else{
54450             this.updateColumns();
54451             this.layout();
54452         }
54453     },
54454
54455     /**
54456      * Autofits all columns to the grid's width proportionate with their current size
54457      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54458      */
54459     fitColumns : function(reserveScrollSpace){
54460         var cm = this.grid.colModel;
54461         var colCount = cm.getColumnCount();
54462         var cols = [];
54463         var width = 0;
54464         var i, w;
54465         for (i = 0; i < colCount; i++){
54466             if(!cm.isHidden(i) && !cm.isFixed(i)){
54467                 w = cm.getColumnWidth(i);
54468                 cols.push(i);
54469                 cols.push(w);
54470                 width += w;
54471             }
54472         }
54473         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54474         if(reserveScrollSpace){
54475             avail -= 17;
54476         }
54477         var frac = (avail - cm.getTotalWidth())/width;
54478         while (cols.length){
54479             w = cols.pop();
54480             i = cols.pop();
54481             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54482         }
54483         this.updateColumns();
54484         this.layout();
54485     },
54486
54487     onRowSelect : function(rowIndex){
54488         var row = this.getRowComposite(rowIndex);
54489         row.addClass("x-grid-row-selected");
54490     },
54491
54492     onRowDeselect : function(rowIndex){
54493         var row = this.getRowComposite(rowIndex);
54494         row.removeClass("x-grid-row-selected");
54495     },
54496
54497     onCellSelect : function(row, col){
54498         var cell = this.getCell(row, col);
54499         if(cell){
54500             Roo.fly(cell).addClass("x-grid-cell-selected");
54501         }
54502     },
54503
54504     onCellDeselect : function(row, col){
54505         var cell = this.getCell(row, col);
54506         if(cell){
54507             Roo.fly(cell).removeClass("x-grid-cell-selected");
54508         }
54509     },
54510
54511     updateHeaderSortState : function(){
54512         
54513         // sort state can be single { field: xxx, direction : yyy}
54514         // or   { xxx=>ASC , yyy : DESC ..... }
54515         
54516         var mstate = {};
54517         if (!this.ds.multiSort) { 
54518             var state = this.ds.getSortState();
54519             if(!state){
54520                 return;
54521             }
54522             mstate[state.field] = state.direction;
54523             // FIXME... - this is not used here.. but might be elsewhere..
54524             this.sortState = state;
54525             
54526         } else {
54527             mstate = this.ds.sortToggle;
54528         }
54529         //remove existing sort classes..
54530         
54531         var sc = this.sortClasses;
54532         var hds = this.el.select(this.headerSelector).removeClass(sc);
54533         
54534         for(var f in mstate) {
54535         
54536             var sortColumn = this.cm.findColumnIndex(f);
54537             
54538             if(sortColumn != -1){
54539                 var sortDir = mstate[f];        
54540                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54541             }
54542         }
54543         
54544          
54545         
54546     },
54547
54548
54549     handleHeaderClick : function(g, index,e){
54550         
54551         Roo.log("header click");
54552         
54553         if (Roo.isTouch) {
54554             // touch events on header are handled by context
54555             this.handleHdCtx(g,index,e);
54556             return;
54557         }
54558         
54559         
54560         if(this.headersDisabled){
54561             return;
54562         }
54563         var dm = g.dataSource, cm = g.colModel;
54564         if(!cm.isSortable(index)){
54565             return;
54566         }
54567         g.stopEditing();
54568         
54569         if (dm.multiSort) {
54570             // update the sortOrder
54571             var so = [];
54572             for(var i = 0; i < cm.config.length; i++ ) {
54573                 
54574                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54575                     continue; // dont' bother, it's not in sort list or being set.
54576                 }
54577                 
54578                 so.push(cm.config[i].dataIndex);
54579             };
54580             dm.sortOrder = so;
54581         }
54582         
54583         
54584         dm.sort(cm.getDataIndex(index));
54585     },
54586
54587
54588     destroy : function(){
54589         if(this.colMenu){
54590             this.colMenu.removeAll();
54591             Roo.menu.MenuMgr.unregister(this.colMenu);
54592             this.colMenu.getEl().remove();
54593             delete this.colMenu;
54594         }
54595         if(this.hmenu){
54596             this.hmenu.removeAll();
54597             Roo.menu.MenuMgr.unregister(this.hmenu);
54598             this.hmenu.getEl().remove();
54599             delete this.hmenu;
54600         }
54601         if(this.grid.enableColumnMove){
54602             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54603             if(dds){
54604                 for(var dd in dds){
54605                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54606                         var elid = dds[dd].dragElId;
54607                         dds[dd].unreg();
54608                         Roo.get(elid).remove();
54609                     } else if(dds[dd].config.isTarget){
54610                         dds[dd].proxyTop.remove();
54611                         dds[dd].proxyBottom.remove();
54612                         dds[dd].unreg();
54613                     }
54614                     if(Roo.dd.DDM.locationCache[dd]){
54615                         delete Roo.dd.DDM.locationCache[dd];
54616                     }
54617                 }
54618                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54619             }
54620         }
54621         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54622         this.bind(null, null);
54623         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54624     },
54625
54626     handleLockChange : function(){
54627         this.refresh(true);
54628     },
54629
54630     onDenyColumnLock : function(){
54631
54632     },
54633
54634     onDenyColumnHide : function(){
54635
54636     },
54637
54638     handleHdMenuClick : function(item){
54639         var index = this.hdCtxIndex;
54640         var cm = this.cm, ds = this.ds;
54641         switch(item.id){
54642             case "asc":
54643                 ds.sort(cm.getDataIndex(index), "ASC");
54644                 break;
54645             case "desc":
54646                 ds.sort(cm.getDataIndex(index), "DESC");
54647                 break;
54648             case "lock":
54649                 var lc = cm.getLockedCount();
54650                 if(cm.getColumnCount(true) <= lc+1){
54651                     this.onDenyColumnLock();
54652                     return;
54653                 }
54654                 if(lc != index){
54655                     cm.setLocked(index, true, true);
54656                     cm.moveColumn(index, lc);
54657                     this.grid.fireEvent("columnmove", index, lc);
54658                 }else{
54659                     cm.setLocked(index, true);
54660                 }
54661             break;
54662             case "unlock":
54663                 var lc = cm.getLockedCount();
54664                 if((lc-1) != index){
54665                     cm.setLocked(index, false, true);
54666                     cm.moveColumn(index, lc-1);
54667                     this.grid.fireEvent("columnmove", index, lc-1);
54668                 }else{
54669                     cm.setLocked(index, false);
54670                 }
54671             break;
54672             case 'wider': // used to expand cols on touch..
54673             case 'narrow':
54674                 var cw = cm.getColumnWidth(index);
54675                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54676                 cw = Math.max(0, cw);
54677                 cw = Math.min(cw,4000);
54678                 cm.setColumnWidth(index, cw);
54679                 break;
54680                 
54681             default:
54682                 index = cm.getIndexById(item.id.substr(4));
54683                 if(index != -1){
54684                     if(item.checked && cm.getColumnCount(true) <= 1){
54685                         this.onDenyColumnHide();
54686                         return false;
54687                     }
54688                     cm.setHidden(index, item.checked);
54689                 }
54690         }
54691         return true;
54692     },
54693
54694     beforeColMenuShow : function(){
54695         var cm = this.cm,  colCount = cm.getColumnCount();
54696         this.colMenu.removeAll();
54697         for(var i = 0; i < colCount; i++){
54698             this.colMenu.add(new Roo.menu.CheckItem({
54699                 id: "col-"+cm.getColumnId(i),
54700                 text: cm.getColumnHeader(i),
54701                 checked: !cm.isHidden(i),
54702                 hideOnClick:false
54703             }));
54704         }
54705     },
54706
54707     handleHdCtx : function(g, index, e){
54708         e.stopEvent();
54709         var hd = this.getHeaderCell(index);
54710         this.hdCtxIndex = index;
54711         var ms = this.hmenu.items, cm = this.cm;
54712         ms.get("asc").setDisabled(!cm.isSortable(index));
54713         ms.get("desc").setDisabled(!cm.isSortable(index));
54714         if(this.grid.enableColLock !== false){
54715             ms.get("lock").setDisabled(cm.isLocked(index));
54716             ms.get("unlock").setDisabled(!cm.isLocked(index));
54717         }
54718         this.hmenu.show(hd, "tl-bl");
54719     },
54720
54721     handleHdOver : function(e){
54722         var hd = this.findHeaderCell(e.getTarget());
54723         if(hd && !this.headersDisabled){
54724             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54725                this.fly(hd).addClass("x-grid-hd-over");
54726             }
54727         }
54728     },
54729
54730     handleHdOut : function(e){
54731         var hd = this.findHeaderCell(e.getTarget());
54732         if(hd){
54733             this.fly(hd).removeClass("x-grid-hd-over");
54734         }
54735     },
54736
54737     handleSplitDblClick : function(e, t){
54738         var i = this.getCellIndex(t);
54739         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54740             this.autoSizeColumn(i, true);
54741             this.layout();
54742         }
54743     },
54744
54745     render : function(){
54746
54747         var cm = this.cm;
54748         var colCount = cm.getColumnCount();
54749
54750         if(this.grid.monitorWindowResize === true){
54751             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54752         }
54753         var header = this.renderHeaders();
54754         var body = this.templates.body.apply({rows:""});
54755         var html = this.templates.master.apply({
54756             lockedBody: body,
54757             body: body,
54758             lockedHeader: header[0],
54759             header: header[1]
54760         });
54761
54762         //this.updateColumns();
54763
54764         this.grid.getGridEl().dom.innerHTML = html;
54765
54766         this.initElements();
54767         
54768         // a kludge to fix the random scolling effect in webkit
54769         this.el.on("scroll", function() {
54770             this.el.dom.scrollTop=0; // hopefully not recursive..
54771         },this);
54772
54773         this.scroller.on("scroll", this.handleScroll, this);
54774         this.lockedBody.on("mousewheel", this.handleWheel, this);
54775         this.mainBody.on("mousewheel", this.handleWheel, this);
54776
54777         this.mainHd.on("mouseover", this.handleHdOver, this);
54778         this.mainHd.on("mouseout", this.handleHdOut, this);
54779         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54780                 {delegate: "."+this.splitClass});
54781
54782         this.lockedHd.on("mouseover", this.handleHdOver, this);
54783         this.lockedHd.on("mouseout", this.handleHdOut, this);
54784         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54785                 {delegate: "."+this.splitClass});
54786
54787         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54788             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54789         }
54790
54791         this.updateSplitters();
54792
54793         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54794             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54795             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54796         }
54797
54798         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54799             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54800             this.hmenu.add(
54801                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54802                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54803             );
54804             if(this.grid.enableColLock !== false){
54805                 this.hmenu.add('-',
54806                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54807                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54808                 );
54809             }
54810             if (Roo.isTouch) {
54811                  this.hmenu.add('-',
54812                     {id:"wider", text: this.columnsWiderText},
54813                     {id:"narrow", text: this.columnsNarrowText }
54814                 );
54815                 
54816                  
54817             }
54818             
54819             if(this.grid.enableColumnHide !== false){
54820
54821                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54822                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54823                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54824
54825                 this.hmenu.add('-',
54826                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54827                 );
54828             }
54829             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54830
54831             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54832         }
54833
54834         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54835             this.dd = new Roo.grid.GridDragZone(this.grid, {
54836                 ddGroup : this.grid.ddGroup || 'GridDD'
54837             });
54838             
54839         }
54840
54841         /*
54842         for(var i = 0; i < colCount; i++){
54843             if(cm.isHidden(i)){
54844                 this.hideColumn(i);
54845             }
54846             if(cm.config[i].align){
54847                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54848                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54849             }
54850         }*/
54851         
54852         this.updateHeaderSortState();
54853
54854         this.beforeInitialResize();
54855         this.layout(true);
54856
54857         // two part rendering gives faster view to the user
54858         this.renderPhase2.defer(1, this);
54859     },
54860
54861     renderPhase2 : function(){
54862         // render the rows now
54863         this.refresh();
54864         if(this.grid.autoSizeColumns){
54865             this.autoSizeColumns();
54866         }
54867     },
54868
54869     beforeInitialResize : function(){
54870
54871     },
54872
54873     onColumnSplitterMoved : function(i, w){
54874         this.userResized = true;
54875         var cm = this.grid.colModel;
54876         cm.setColumnWidth(i, w, true);
54877         var cid = cm.getColumnId(i);
54878         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54879         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54880         this.updateSplitters();
54881         this.layout();
54882         this.grid.fireEvent("columnresize", i, w);
54883     },
54884
54885     syncRowHeights : function(startIndex, endIndex){
54886         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54887             startIndex = startIndex || 0;
54888             var mrows = this.getBodyTable().rows;
54889             var lrows = this.getLockedTable().rows;
54890             var len = mrows.length-1;
54891             endIndex = Math.min(endIndex || len, len);
54892             for(var i = startIndex; i <= endIndex; i++){
54893                 var m = mrows[i], l = lrows[i];
54894                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54895                 m.style.height = l.style.height = h + "px";
54896             }
54897         }
54898     },
54899
54900     layout : function(initialRender, is2ndPass){
54901         var g = this.grid;
54902         var auto = g.autoHeight;
54903         var scrollOffset = 16;
54904         var c = g.getGridEl(), cm = this.cm,
54905                 expandCol = g.autoExpandColumn,
54906                 gv = this;
54907         //c.beginMeasure();
54908
54909         if(!c.dom.offsetWidth){ // display:none?
54910             if(initialRender){
54911                 this.lockedWrap.show();
54912                 this.mainWrap.show();
54913             }
54914             return;
54915         }
54916
54917         var hasLock = this.cm.isLocked(0);
54918
54919         var tbh = this.headerPanel.getHeight();
54920         var bbh = this.footerPanel.getHeight();
54921
54922         if(auto){
54923             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54924             var newHeight = ch + c.getBorderWidth("tb");
54925             if(g.maxHeight){
54926                 newHeight = Math.min(g.maxHeight, newHeight);
54927             }
54928             c.setHeight(newHeight);
54929         }
54930
54931         if(g.autoWidth){
54932             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54933         }
54934
54935         var s = this.scroller;
54936
54937         var csize = c.getSize(true);
54938
54939         this.el.setSize(csize.width, csize.height);
54940
54941         this.headerPanel.setWidth(csize.width);
54942         this.footerPanel.setWidth(csize.width);
54943
54944         var hdHeight = this.mainHd.getHeight();
54945         var vw = csize.width;
54946         var vh = csize.height - (tbh + bbh);
54947
54948         s.setSize(vw, vh);
54949
54950         var bt = this.getBodyTable();
54951         var ltWidth = hasLock ?
54952                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54953
54954         var scrollHeight = bt.offsetHeight;
54955         var scrollWidth = ltWidth + bt.offsetWidth;
54956         var vscroll = false, hscroll = false;
54957
54958         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54959
54960         var lw = this.lockedWrap, mw = this.mainWrap;
54961         var lb = this.lockedBody, mb = this.mainBody;
54962
54963         setTimeout(function(){
54964             var t = s.dom.offsetTop;
54965             var w = s.dom.clientWidth,
54966                 h = s.dom.clientHeight;
54967
54968             lw.setTop(t);
54969             lw.setSize(ltWidth, h);
54970
54971             mw.setLeftTop(ltWidth, t);
54972             mw.setSize(w-ltWidth, h);
54973
54974             lb.setHeight(h-hdHeight);
54975             mb.setHeight(h-hdHeight);
54976
54977             if(is2ndPass !== true && !gv.userResized && expandCol){
54978                 // high speed resize without full column calculation
54979                 
54980                 var ci = cm.getIndexById(expandCol);
54981                 if (ci < 0) {
54982                     ci = cm.findColumnIndex(expandCol);
54983                 }
54984                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54985                 var expandId = cm.getColumnId(ci);
54986                 var  tw = cm.getTotalWidth(false);
54987                 var currentWidth = cm.getColumnWidth(ci);
54988                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54989                 if(currentWidth != cw){
54990                     cm.setColumnWidth(ci, cw, true);
54991                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54992                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54993                     gv.updateSplitters();
54994                     gv.layout(false, true);
54995                 }
54996             }
54997
54998             if(initialRender){
54999                 lw.show();
55000                 mw.show();
55001             }
55002             //c.endMeasure();
55003         }, 10);
55004     },
55005
55006     onWindowResize : function(){
55007         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55008             return;
55009         }
55010         this.layout();
55011     },
55012
55013     appendFooter : function(parentEl){
55014         return null;
55015     },
55016
55017     sortAscText : "Sort Ascending",
55018     sortDescText : "Sort Descending",
55019     lockText : "Lock Column",
55020     unlockText : "Unlock Column",
55021     columnsText : "Columns",
55022  
55023     columnsWiderText : "Wider",
55024     columnsNarrowText : "Thinner"
55025 });
55026
55027
55028 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55029     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55030     this.proxy.el.addClass('x-grid3-col-dd');
55031 };
55032
55033 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55034     handleMouseDown : function(e){
55035
55036     },
55037
55038     callHandleMouseDown : function(e){
55039         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55040     }
55041 });
55042 /*
55043  * Based on:
55044  * Ext JS Library 1.1.1
55045  * Copyright(c) 2006-2007, Ext JS, LLC.
55046  *
55047  * Originally Released Under LGPL - original licence link has changed is not relivant.
55048  *
55049  * Fork - LGPL
55050  * <script type="text/javascript">
55051  */
55052  
55053 // private
55054 // This is a support class used internally by the Grid components
55055 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55056     this.grid = grid;
55057     this.view = grid.getView();
55058     this.proxy = this.view.resizeProxy;
55059     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55060         "gridSplitters" + this.grid.getGridEl().id, {
55061         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55062     });
55063     this.setHandleElId(Roo.id(hd));
55064     this.setOuterHandleElId(Roo.id(hd2));
55065     this.scroll = false;
55066 };
55067 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55068     fly: Roo.Element.fly,
55069
55070     b4StartDrag : function(x, y){
55071         this.view.headersDisabled = true;
55072         this.proxy.setHeight(this.view.mainWrap.getHeight());
55073         var w = this.cm.getColumnWidth(this.cellIndex);
55074         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55075         this.resetConstraints();
55076         this.setXConstraint(minw, 1000);
55077         this.setYConstraint(0, 0);
55078         this.minX = x - minw;
55079         this.maxX = x + 1000;
55080         this.startPos = x;
55081         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55082     },
55083
55084
55085     handleMouseDown : function(e){
55086         ev = Roo.EventObject.setEvent(e);
55087         var t = this.fly(ev.getTarget());
55088         if(t.hasClass("x-grid-split")){
55089             this.cellIndex = this.view.getCellIndex(t.dom);
55090             this.split = t.dom;
55091             this.cm = this.grid.colModel;
55092             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55093                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55094             }
55095         }
55096     },
55097
55098     endDrag : function(e){
55099         this.view.headersDisabled = false;
55100         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55101         var diff = endX - this.startPos;
55102         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55103     },
55104
55105     autoOffset : function(){
55106         this.setDelta(0,0);
55107     }
55108 });/*
55109  * Based on:
55110  * Ext JS Library 1.1.1
55111  * Copyright(c) 2006-2007, Ext JS, LLC.
55112  *
55113  * Originally Released Under LGPL - original licence link has changed is not relivant.
55114  *
55115  * Fork - LGPL
55116  * <script type="text/javascript">
55117  */
55118  
55119 // private
55120 // This is a support class used internally by the Grid components
55121 Roo.grid.GridDragZone = function(grid, config){
55122     this.view = grid.getView();
55123     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55124     if(this.view.lockedBody){
55125         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55126         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55127     }
55128     this.scroll = false;
55129     this.grid = grid;
55130     this.ddel = document.createElement('div');
55131     this.ddel.className = 'x-grid-dd-wrap';
55132 };
55133
55134 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55135     ddGroup : "GridDD",
55136
55137     getDragData : function(e){
55138         var t = Roo.lib.Event.getTarget(e);
55139         var rowIndex = this.view.findRowIndex(t);
55140         var sm = this.grid.selModel;
55141             
55142         //Roo.log(rowIndex);
55143         
55144         if (sm.getSelectedCell) {
55145             // cell selection..
55146             if (!sm.getSelectedCell()) {
55147                 return false;
55148             }
55149             if (rowIndex != sm.getSelectedCell()[0]) {
55150                 return false;
55151             }
55152         
55153         }
55154         
55155         if(rowIndex !== false){
55156             
55157             // if editorgrid.. 
55158             
55159             
55160             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55161                
55162             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55163               //  
55164             //}
55165             if (e.hasModifier()){
55166                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55167             }
55168             
55169             Roo.log("getDragData");
55170             
55171             return {
55172                 grid: this.grid,
55173                 ddel: this.ddel,
55174                 rowIndex: rowIndex,
55175                 selections:sm.getSelections ? sm.getSelections() : (
55176                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55177                 )
55178             };
55179         }
55180         return false;
55181     },
55182
55183     onInitDrag : function(e){
55184         var data = this.dragData;
55185         this.ddel.innerHTML = this.grid.getDragDropText();
55186         this.proxy.update(this.ddel);
55187         // fire start drag?
55188     },
55189
55190     afterRepair : function(){
55191         this.dragging = false;
55192     },
55193
55194     getRepairXY : function(e, data){
55195         return false;
55196     },
55197
55198     onEndDrag : function(data, e){
55199         // fire end drag?
55200     },
55201
55202     onValidDrop : function(dd, e, id){
55203         // fire drag drop?
55204         this.hideProxy();
55205     },
55206
55207     beforeInvalidDrop : function(e, id){
55208
55209     }
55210 });/*
55211  * Based on:
55212  * Ext JS Library 1.1.1
55213  * Copyright(c) 2006-2007, Ext JS, LLC.
55214  *
55215  * Originally Released Under LGPL - original licence link has changed is not relivant.
55216  *
55217  * Fork - LGPL
55218  * <script type="text/javascript">
55219  */
55220  
55221
55222 /**
55223  * @class Roo.grid.ColumnModel
55224  * @extends Roo.util.Observable
55225  * This is the default implementation of a ColumnModel used by the Grid. It defines
55226  * the columns in the grid.
55227  * <br>Usage:<br>
55228  <pre><code>
55229  var colModel = new Roo.grid.ColumnModel([
55230         {header: "Ticker", width: 60, sortable: true, locked: true},
55231         {header: "Company Name", width: 150, sortable: true},
55232         {header: "Market Cap.", width: 100, sortable: true},
55233         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55234         {header: "Employees", width: 100, sortable: true, resizable: false}
55235  ]);
55236  </code></pre>
55237  * <p>
55238  
55239  * The config options listed for this class are options which may appear in each
55240  * individual column definition.
55241  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55242  * @constructor
55243  * @param {Object} config An Array of column config objects. See this class's
55244  * config objects for details.
55245 */
55246 Roo.grid.ColumnModel = function(config){
55247         /**
55248      * The config passed into the constructor
55249      */
55250     this.config = config;
55251     this.lookup = {};
55252
55253     // if no id, create one
55254     // if the column does not have a dataIndex mapping,
55255     // map it to the order it is in the config
55256     for(var i = 0, len = config.length; i < len; i++){
55257         var c = config[i];
55258         if(typeof c.dataIndex == "undefined"){
55259             c.dataIndex = i;
55260         }
55261         if(typeof c.renderer == "string"){
55262             c.renderer = Roo.util.Format[c.renderer];
55263         }
55264         if(typeof c.id == "undefined"){
55265             c.id = Roo.id();
55266         }
55267         if(c.editor && c.editor.xtype){
55268             c.editor  = Roo.factory(c.editor, Roo.grid);
55269         }
55270         if(c.editor && c.editor.isFormField){
55271             c.editor = new Roo.grid.GridEditor(c.editor);
55272         }
55273         this.lookup[c.id] = c;
55274     }
55275
55276     /**
55277      * The width of columns which have no width specified (defaults to 100)
55278      * @type Number
55279      */
55280     this.defaultWidth = 100;
55281
55282     /**
55283      * Default sortable of columns which have no sortable specified (defaults to false)
55284      * @type Boolean
55285      */
55286     this.defaultSortable = false;
55287
55288     this.addEvents({
55289         /**
55290              * @event widthchange
55291              * Fires when the width of a column changes.
55292              * @param {ColumnModel} this
55293              * @param {Number} columnIndex The column index
55294              * @param {Number} newWidth The new width
55295              */
55296             "widthchange": true,
55297         /**
55298              * @event headerchange
55299              * Fires when the text of a header changes.
55300              * @param {ColumnModel} this
55301              * @param {Number} columnIndex The column index
55302              * @param {Number} newText The new header text
55303              */
55304             "headerchange": true,
55305         /**
55306              * @event hiddenchange
55307              * Fires when a column is hidden or "unhidden".
55308              * @param {ColumnModel} this
55309              * @param {Number} columnIndex The column index
55310              * @param {Boolean} hidden true if hidden, false otherwise
55311              */
55312             "hiddenchange": true,
55313             /**
55314          * @event columnmoved
55315          * Fires when a column is moved.
55316          * @param {ColumnModel} this
55317          * @param {Number} oldIndex
55318          * @param {Number} newIndex
55319          */
55320         "columnmoved" : true,
55321         /**
55322          * @event columlockchange
55323          * Fires when a column's locked state is changed
55324          * @param {ColumnModel} this
55325          * @param {Number} colIndex
55326          * @param {Boolean} locked true if locked
55327          */
55328         "columnlockchange" : true
55329     });
55330     Roo.grid.ColumnModel.superclass.constructor.call(this);
55331 };
55332 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55333     /**
55334      * @cfg {String} header The header text to display in the Grid view.
55335      */
55336     /**
55337      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55338      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55339      * specified, the column's index is used as an index into the Record's data Array.
55340      */
55341     /**
55342      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55343      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55344      */
55345     /**
55346      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55347      * Defaults to the value of the {@link #defaultSortable} property.
55348      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55349      */
55350     /**
55351      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55352      */
55353     /**
55354      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55355      */
55356     /**
55357      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55358      */
55359     /**
55360      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55361      */
55362     /**
55363      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55364      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55365      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55366      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55367      */
55368        /**
55369      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55370      */
55371     /**
55372      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55373      */
55374     /**
55375      * @cfg {String} cursor (Optional)
55376      */
55377     /**
55378      * @cfg {String} tooltip (Optional)
55379      */
55380     /**
55381      * Returns the id of the column at the specified index.
55382      * @param {Number} index The column index
55383      * @return {String} the id
55384      */
55385     getColumnId : function(index){
55386         return this.config[index].id;
55387     },
55388
55389     /**
55390      * Returns the column for a specified id.
55391      * @param {String} id The column id
55392      * @return {Object} the column
55393      */
55394     getColumnById : function(id){
55395         return this.lookup[id];
55396     },
55397
55398     
55399     /**
55400      * Returns the column for a specified dataIndex.
55401      * @param {String} dataIndex The column dataIndex
55402      * @return {Object|Boolean} the column or false if not found
55403      */
55404     getColumnByDataIndex: function(dataIndex){
55405         var index = this.findColumnIndex(dataIndex);
55406         return index > -1 ? this.config[index] : false;
55407     },
55408     
55409     /**
55410      * Returns the index for a specified column id.
55411      * @param {String} id The column id
55412      * @return {Number} the index, or -1 if not found
55413      */
55414     getIndexById : function(id){
55415         for(var i = 0, len = this.config.length; i < len; i++){
55416             if(this.config[i].id == id){
55417                 return i;
55418             }
55419         }
55420         return -1;
55421     },
55422     
55423     /**
55424      * Returns the index for a specified column dataIndex.
55425      * @param {String} dataIndex The column dataIndex
55426      * @return {Number} the index, or -1 if not found
55427      */
55428     
55429     findColumnIndex : function(dataIndex){
55430         for(var i = 0, len = this.config.length; i < len; i++){
55431             if(this.config[i].dataIndex == dataIndex){
55432                 return i;
55433             }
55434         }
55435         return -1;
55436     },
55437     
55438     
55439     moveColumn : function(oldIndex, newIndex){
55440         var c = this.config[oldIndex];
55441         this.config.splice(oldIndex, 1);
55442         this.config.splice(newIndex, 0, c);
55443         this.dataMap = null;
55444         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55445     },
55446
55447     isLocked : function(colIndex){
55448         return this.config[colIndex].locked === true;
55449     },
55450
55451     setLocked : function(colIndex, value, suppressEvent){
55452         if(this.isLocked(colIndex) == value){
55453             return;
55454         }
55455         this.config[colIndex].locked = value;
55456         if(!suppressEvent){
55457             this.fireEvent("columnlockchange", this, colIndex, value);
55458         }
55459     },
55460
55461     getTotalLockedWidth : function(){
55462         var totalWidth = 0;
55463         for(var i = 0; i < this.config.length; i++){
55464             if(this.isLocked(i) && !this.isHidden(i)){
55465                 this.totalWidth += this.getColumnWidth(i);
55466             }
55467         }
55468         return totalWidth;
55469     },
55470
55471     getLockedCount : function(){
55472         for(var i = 0, len = this.config.length; i < len; i++){
55473             if(!this.isLocked(i)){
55474                 return i;
55475             }
55476         }
55477     },
55478
55479     /**
55480      * Returns the number of columns.
55481      * @return {Number}
55482      */
55483     getColumnCount : function(visibleOnly){
55484         if(visibleOnly === true){
55485             var c = 0;
55486             for(var i = 0, len = this.config.length; i < len; i++){
55487                 if(!this.isHidden(i)){
55488                     c++;
55489                 }
55490             }
55491             return c;
55492         }
55493         return this.config.length;
55494     },
55495
55496     /**
55497      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55498      * @param {Function} fn
55499      * @param {Object} scope (optional)
55500      * @return {Array} result
55501      */
55502     getColumnsBy : function(fn, scope){
55503         var r = [];
55504         for(var i = 0, len = this.config.length; i < len; i++){
55505             var c = this.config[i];
55506             if(fn.call(scope||this, c, i) === true){
55507                 r[r.length] = c;
55508             }
55509         }
55510         return r;
55511     },
55512
55513     /**
55514      * Returns true if the specified column is sortable.
55515      * @param {Number} col The column index
55516      * @return {Boolean}
55517      */
55518     isSortable : function(col){
55519         if(typeof this.config[col].sortable == "undefined"){
55520             return this.defaultSortable;
55521         }
55522         return this.config[col].sortable;
55523     },
55524
55525     /**
55526      * Returns the rendering (formatting) function defined for the column.
55527      * @param {Number} col The column index.
55528      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55529      */
55530     getRenderer : function(col){
55531         if(!this.config[col].renderer){
55532             return Roo.grid.ColumnModel.defaultRenderer;
55533         }
55534         return this.config[col].renderer;
55535     },
55536
55537     /**
55538      * Sets the rendering (formatting) function for a column.
55539      * @param {Number} col The column index
55540      * @param {Function} fn The function to use to process the cell's raw data
55541      * to return HTML markup for the grid view. The render function is called with
55542      * the following parameters:<ul>
55543      * <li>Data value.</li>
55544      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55545      * <li>css A CSS style string to apply to the table cell.</li>
55546      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55547      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55548      * <li>Row index</li>
55549      * <li>Column index</li>
55550      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55551      */
55552     setRenderer : function(col, fn){
55553         this.config[col].renderer = fn;
55554     },
55555
55556     /**
55557      * Returns the width for the specified column.
55558      * @param {Number} col The column index
55559      * @return {Number}
55560      */
55561     getColumnWidth : function(col){
55562         return this.config[col].width * 1 || this.defaultWidth;
55563     },
55564
55565     /**
55566      * Sets the width for a column.
55567      * @param {Number} col The column index
55568      * @param {Number} width The new width
55569      */
55570     setColumnWidth : function(col, width, suppressEvent){
55571         this.config[col].width = width;
55572         this.totalWidth = null;
55573         if(!suppressEvent){
55574              this.fireEvent("widthchange", this, col, width);
55575         }
55576     },
55577
55578     /**
55579      * Returns the total width of all columns.
55580      * @param {Boolean} includeHidden True to include hidden column widths
55581      * @return {Number}
55582      */
55583     getTotalWidth : function(includeHidden){
55584         if(!this.totalWidth){
55585             this.totalWidth = 0;
55586             for(var i = 0, len = this.config.length; i < len; i++){
55587                 if(includeHidden || !this.isHidden(i)){
55588                     this.totalWidth += this.getColumnWidth(i);
55589                 }
55590             }
55591         }
55592         return this.totalWidth;
55593     },
55594
55595     /**
55596      * Returns the header for the specified column.
55597      * @param {Number} col The column index
55598      * @return {String}
55599      */
55600     getColumnHeader : function(col){
55601         return this.config[col].header;
55602     },
55603
55604     /**
55605      * Sets the header for a column.
55606      * @param {Number} col The column index
55607      * @param {String} header The new header
55608      */
55609     setColumnHeader : function(col, header){
55610         this.config[col].header = header;
55611         this.fireEvent("headerchange", this, col, header);
55612     },
55613
55614     /**
55615      * Returns the tooltip for the specified column.
55616      * @param {Number} col The column index
55617      * @return {String}
55618      */
55619     getColumnTooltip : function(col){
55620             return this.config[col].tooltip;
55621     },
55622     /**
55623      * Sets the tooltip for a column.
55624      * @param {Number} col The column index
55625      * @param {String} tooltip The new tooltip
55626      */
55627     setColumnTooltip : function(col, tooltip){
55628             this.config[col].tooltip = tooltip;
55629     },
55630
55631     /**
55632      * Returns the dataIndex for the specified column.
55633      * @param {Number} col The column index
55634      * @return {Number}
55635      */
55636     getDataIndex : function(col){
55637         return this.config[col].dataIndex;
55638     },
55639
55640     /**
55641      * Sets the dataIndex for a column.
55642      * @param {Number} col The column index
55643      * @param {Number} dataIndex The new dataIndex
55644      */
55645     setDataIndex : function(col, dataIndex){
55646         this.config[col].dataIndex = dataIndex;
55647     },
55648
55649     
55650     
55651     /**
55652      * Returns true if the cell is editable.
55653      * @param {Number} colIndex The column index
55654      * @param {Number} rowIndex The row index
55655      * @return {Boolean}
55656      */
55657     isCellEditable : function(colIndex, rowIndex){
55658         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55659     },
55660
55661     /**
55662      * Returns the editor defined for the cell/column.
55663      * return false or null to disable editing.
55664      * @param {Number} colIndex The column index
55665      * @param {Number} rowIndex The row index
55666      * @return {Object}
55667      */
55668     getCellEditor : function(colIndex, rowIndex){
55669         return this.config[colIndex].editor;
55670     },
55671
55672     /**
55673      * Sets if a column is editable.
55674      * @param {Number} col The column index
55675      * @param {Boolean} editable True if the column is editable
55676      */
55677     setEditable : function(col, editable){
55678         this.config[col].editable = editable;
55679     },
55680
55681
55682     /**
55683      * Returns true if the column is hidden.
55684      * @param {Number} colIndex The column index
55685      * @return {Boolean}
55686      */
55687     isHidden : function(colIndex){
55688         return this.config[colIndex].hidden;
55689     },
55690
55691
55692     /**
55693      * Returns true if the column width cannot be changed
55694      */
55695     isFixed : function(colIndex){
55696         return this.config[colIndex].fixed;
55697     },
55698
55699     /**
55700      * Returns true if the column can be resized
55701      * @return {Boolean}
55702      */
55703     isResizable : function(colIndex){
55704         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55705     },
55706     /**
55707      * Sets if a column is hidden.
55708      * @param {Number} colIndex The column index
55709      * @param {Boolean} hidden True if the column is hidden
55710      */
55711     setHidden : function(colIndex, hidden){
55712         this.config[colIndex].hidden = hidden;
55713         this.totalWidth = null;
55714         this.fireEvent("hiddenchange", this, colIndex, hidden);
55715     },
55716
55717     /**
55718      * Sets the editor for a column.
55719      * @param {Number} col The column index
55720      * @param {Object} editor The editor object
55721      */
55722     setEditor : function(col, editor){
55723         this.config[col].editor = editor;
55724     }
55725 });
55726
55727 Roo.grid.ColumnModel.defaultRenderer = function(value){
55728         if(typeof value == "string" && value.length < 1){
55729             return "&#160;";
55730         }
55731         return value;
55732 };
55733
55734 // Alias for backwards compatibility
55735 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55736 /*
55737  * Based on:
55738  * Ext JS Library 1.1.1
55739  * Copyright(c) 2006-2007, Ext JS, LLC.
55740  *
55741  * Originally Released Under LGPL - original licence link has changed is not relivant.
55742  *
55743  * Fork - LGPL
55744  * <script type="text/javascript">
55745  */
55746
55747 /**
55748  * @class Roo.grid.AbstractSelectionModel
55749  * @extends Roo.util.Observable
55750  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55751  * implemented by descendant classes.  This class should not be directly instantiated.
55752  * @constructor
55753  */
55754 Roo.grid.AbstractSelectionModel = function(){
55755     this.locked = false;
55756     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55757 };
55758
55759 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55760     /** @ignore Called by the grid automatically. Do not call directly. */
55761     init : function(grid){
55762         this.grid = grid;
55763         this.initEvents();
55764     },
55765
55766     /**
55767      * Locks the selections.
55768      */
55769     lock : function(){
55770         this.locked = true;
55771     },
55772
55773     /**
55774      * Unlocks the selections.
55775      */
55776     unlock : function(){
55777         this.locked = false;
55778     },
55779
55780     /**
55781      * Returns true if the selections are locked.
55782      * @return {Boolean}
55783      */
55784     isLocked : function(){
55785         return this.locked;
55786     }
55787 });/*
55788  * Based on:
55789  * Ext JS Library 1.1.1
55790  * Copyright(c) 2006-2007, Ext JS, LLC.
55791  *
55792  * Originally Released Under LGPL - original licence link has changed is not relivant.
55793  *
55794  * Fork - LGPL
55795  * <script type="text/javascript">
55796  */
55797 /**
55798  * @extends Roo.grid.AbstractSelectionModel
55799  * @class Roo.grid.RowSelectionModel
55800  * The default SelectionModel used by {@link Roo.grid.Grid}.
55801  * It supports multiple selections and keyboard selection/navigation. 
55802  * @constructor
55803  * @param {Object} config
55804  */
55805 Roo.grid.RowSelectionModel = function(config){
55806     Roo.apply(this, config);
55807     this.selections = new Roo.util.MixedCollection(false, function(o){
55808         return o.id;
55809     });
55810
55811     this.last = false;
55812     this.lastActive = false;
55813
55814     this.addEvents({
55815         /**
55816              * @event selectionchange
55817              * Fires when the selection changes
55818              * @param {SelectionModel} this
55819              */
55820             "selectionchange" : true,
55821         /**
55822              * @event afterselectionchange
55823              * Fires after the selection changes (eg. by key press or clicking)
55824              * @param {SelectionModel} this
55825              */
55826             "afterselectionchange" : true,
55827         /**
55828              * @event beforerowselect
55829              * Fires when a row is selected being selected, return false to cancel.
55830              * @param {SelectionModel} this
55831              * @param {Number} rowIndex The selected index
55832              * @param {Boolean} keepExisting False if other selections will be cleared
55833              */
55834             "beforerowselect" : true,
55835         /**
55836              * @event rowselect
55837              * Fires when a row is selected.
55838              * @param {SelectionModel} this
55839              * @param {Number} rowIndex The selected index
55840              * @param {Roo.data.Record} r The record
55841              */
55842             "rowselect" : true,
55843         /**
55844              * @event rowdeselect
55845              * Fires when a row is deselected.
55846              * @param {SelectionModel} this
55847              * @param {Number} rowIndex The selected index
55848              */
55849         "rowdeselect" : true
55850     });
55851     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55852     this.locked = false;
55853 };
55854
55855 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55856     /**
55857      * @cfg {Boolean} singleSelect
55858      * True to allow selection of only one row at a time (defaults to false)
55859      */
55860     singleSelect : false,
55861
55862     // private
55863     initEvents : function(){
55864
55865         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55866             this.grid.on("mousedown", this.handleMouseDown, this);
55867         }else{ // allow click to work like normal
55868             this.grid.on("rowclick", this.handleDragableRowClick, this);
55869         }
55870
55871         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55872             "up" : function(e){
55873                 if(!e.shiftKey){
55874                     this.selectPrevious(e.shiftKey);
55875                 }else if(this.last !== false && this.lastActive !== false){
55876                     var last = this.last;
55877                     this.selectRange(this.last,  this.lastActive-1);
55878                     this.grid.getView().focusRow(this.lastActive);
55879                     if(last !== false){
55880                         this.last = last;
55881                     }
55882                 }else{
55883                     this.selectFirstRow();
55884                 }
55885                 this.fireEvent("afterselectionchange", this);
55886             },
55887             "down" : function(e){
55888                 if(!e.shiftKey){
55889                     this.selectNext(e.shiftKey);
55890                 }else if(this.last !== false && this.lastActive !== false){
55891                     var last = this.last;
55892                     this.selectRange(this.last,  this.lastActive+1);
55893                     this.grid.getView().focusRow(this.lastActive);
55894                     if(last !== false){
55895                         this.last = last;
55896                     }
55897                 }else{
55898                     this.selectFirstRow();
55899                 }
55900                 this.fireEvent("afterselectionchange", this);
55901             },
55902             scope: this
55903         });
55904
55905         var view = this.grid.view;
55906         view.on("refresh", this.onRefresh, this);
55907         view.on("rowupdated", this.onRowUpdated, this);
55908         view.on("rowremoved", this.onRemove, this);
55909     },
55910
55911     // private
55912     onRefresh : function(){
55913         var ds = this.grid.dataSource, i, v = this.grid.view;
55914         var s = this.selections;
55915         s.each(function(r){
55916             if((i = ds.indexOfId(r.id)) != -1){
55917                 v.onRowSelect(i);
55918             }else{
55919                 s.remove(r);
55920             }
55921         });
55922     },
55923
55924     // private
55925     onRemove : function(v, index, r){
55926         this.selections.remove(r);
55927     },
55928
55929     // private
55930     onRowUpdated : function(v, index, r){
55931         if(this.isSelected(r)){
55932             v.onRowSelect(index);
55933         }
55934     },
55935
55936     /**
55937      * Select records.
55938      * @param {Array} records The records to select
55939      * @param {Boolean} keepExisting (optional) True to keep existing selections
55940      */
55941     selectRecords : function(records, keepExisting){
55942         if(!keepExisting){
55943             this.clearSelections();
55944         }
55945         var ds = this.grid.dataSource;
55946         for(var i = 0, len = records.length; i < len; i++){
55947             this.selectRow(ds.indexOf(records[i]), true);
55948         }
55949     },
55950
55951     /**
55952      * Gets the number of selected rows.
55953      * @return {Number}
55954      */
55955     getCount : function(){
55956         return this.selections.length;
55957     },
55958
55959     /**
55960      * Selects the first row in the grid.
55961      */
55962     selectFirstRow : function(){
55963         this.selectRow(0);
55964     },
55965
55966     /**
55967      * Select the last row.
55968      * @param {Boolean} keepExisting (optional) True to keep existing selections
55969      */
55970     selectLastRow : function(keepExisting){
55971         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55972     },
55973
55974     /**
55975      * Selects the row immediately following the last selected row.
55976      * @param {Boolean} keepExisting (optional) True to keep existing selections
55977      */
55978     selectNext : function(keepExisting){
55979         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55980             this.selectRow(this.last+1, keepExisting);
55981             this.grid.getView().focusRow(this.last);
55982         }
55983     },
55984
55985     /**
55986      * Selects the row that precedes the last selected row.
55987      * @param {Boolean} keepExisting (optional) True to keep existing selections
55988      */
55989     selectPrevious : function(keepExisting){
55990         if(this.last){
55991             this.selectRow(this.last-1, keepExisting);
55992             this.grid.getView().focusRow(this.last);
55993         }
55994     },
55995
55996     /**
55997      * Returns the selected records
55998      * @return {Array} Array of selected records
55999      */
56000     getSelections : function(){
56001         return [].concat(this.selections.items);
56002     },
56003
56004     /**
56005      * Returns the first selected record.
56006      * @return {Record}
56007      */
56008     getSelected : function(){
56009         return this.selections.itemAt(0);
56010     },
56011
56012
56013     /**
56014      * Clears all selections.
56015      */
56016     clearSelections : function(fast){
56017         if(this.locked) return;
56018         if(fast !== true){
56019             var ds = this.grid.dataSource;
56020             var s = this.selections;
56021             s.each(function(r){
56022                 this.deselectRow(ds.indexOfId(r.id));
56023             }, this);
56024             s.clear();
56025         }else{
56026             this.selections.clear();
56027         }
56028         this.last = false;
56029     },
56030
56031
56032     /**
56033      * Selects all rows.
56034      */
56035     selectAll : function(){
56036         if(this.locked) return;
56037         this.selections.clear();
56038         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56039             this.selectRow(i, true);
56040         }
56041     },
56042
56043     /**
56044      * Returns True if there is a selection.
56045      * @return {Boolean}
56046      */
56047     hasSelection : function(){
56048         return this.selections.length > 0;
56049     },
56050
56051     /**
56052      * Returns True if the specified row is selected.
56053      * @param {Number/Record} record The record or index of the record to check
56054      * @return {Boolean}
56055      */
56056     isSelected : function(index){
56057         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56058         return (r && this.selections.key(r.id) ? true : false);
56059     },
56060
56061     /**
56062      * Returns True if the specified record id is selected.
56063      * @param {String} id The id of record to check
56064      * @return {Boolean}
56065      */
56066     isIdSelected : function(id){
56067         return (this.selections.key(id) ? true : false);
56068     },
56069
56070     // private
56071     handleMouseDown : function(e, t){
56072         var view = this.grid.getView(), rowIndex;
56073         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56074             return;
56075         };
56076         if(e.shiftKey && this.last !== false){
56077             var last = this.last;
56078             this.selectRange(last, rowIndex, e.ctrlKey);
56079             this.last = last; // reset the last
56080             view.focusRow(rowIndex);
56081         }else{
56082             var isSelected = this.isSelected(rowIndex);
56083             if(e.button !== 0 && isSelected){
56084                 view.focusRow(rowIndex);
56085             }else if(e.ctrlKey && isSelected){
56086                 this.deselectRow(rowIndex);
56087             }else if(!isSelected){
56088                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56089                 view.focusRow(rowIndex);
56090             }
56091         }
56092         this.fireEvent("afterselectionchange", this);
56093     },
56094     // private
56095     handleDragableRowClick :  function(grid, rowIndex, e) 
56096     {
56097         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56098             this.selectRow(rowIndex, false);
56099             grid.view.focusRow(rowIndex);
56100              this.fireEvent("afterselectionchange", this);
56101         }
56102     },
56103     
56104     /**
56105      * Selects multiple rows.
56106      * @param {Array} rows Array of the indexes of the row to select
56107      * @param {Boolean} keepExisting (optional) True to keep existing selections
56108      */
56109     selectRows : function(rows, keepExisting){
56110         if(!keepExisting){
56111             this.clearSelections();
56112         }
56113         for(var i = 0, len = rows.length; i < len; i++){
56114             this.selectRow(rows[i], true);
56115         }
56116     },
56117
56118     /**
56119      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56120      * @param {Number} startRow The index of the first row in the range
56121      * @param {Number} endRow The index of the last row in the range
56122      * @param {Boolean} keepExisting (optional) True to retain existing selections
56123      */
56124     selectRange : function(startRow, endRow, keepExisting){
56125         if(this.locked) return;
56126         if(!keepExisting){
56127             this.clearSelections();
56128         }
56129         if(startRow <= endRow){
56130             for(var i = startRow; i <= endRow; i++){
56131                 this.selectRow(i, true);
56132             }
56133         }else{
56134             for(var i = startRow; i >= endRow; i--){
56135                 this.selectRow(i, true);
56136             }
56137         }
56138     },
56139
56140     /**
56141      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56142      * @param {Number} startRow The index of the first row in the range
56143      * @param {Number} endRow The index of the last row in the range
56144      */
56145     deselectRange : function(startRow, endRow, preventViewNotify){
56146         if(this.locked) return;
56147         for(var i = startRow; i <= endRow; i++){
56148             this.deselectRow(i, preventViewNotify);
56149         }
56150     },
56151
56152     /**
56153      * Selects a row.
56154      * @param {Number} row The index of the row to select
56155      * @param {Boolean} keepExisting (optional) True to keep existing selections
56156      */
56157     selectRow : function(index, keepExisting, preventViewNotify){
56158         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56159         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56160             if(!keepExisting || this.singleSelect){
56161                 this.clearSelections();
56162             }
56163             var r = this.grid.dataSource.getAt(index);
56164             this.selections.add(r);
56165             this.last = this.lastActive = index;
56166             if(!preventViewNotify){
56167                 this.grid.getView().onRowSelect(index);
56168             }
56169             this.fireEvent("rowselect", this, index, r);
56170             this.fireEvent("selectionchange", this);
56171         }
56172     },
56173
56174     /**
56175      * Deselects a row.
56176      * @param {Number} row The index of the row to deselect
56177      */
56178     deselectRow : function(index, preventViewNotify){
56179         if(this.locked) return;
56180         if(this.last == index){
56181             this.last = false;
56182         }
56183         if(this.lastActive == index){
56184             this.lastActive = false;
56185         }
56186         var r = this.grid.dataSource.getAt(index);
56187         this.selections.remove(r);
56188         if(!preventViewNotify){
56189             this.grid.getView().onRowDeselect(index);
56190         }
56191         this.fireEvent("rowdeselect", this, index);
56192         this.fireEvent("selectionchange", this);
56193     },
56194
56195     // private
56196     restoreLast : function(){
56197         if(this._last){
56198             this.last = this._last;
56199         }
56200     },
56201
56202     // private
56203     acceptsNav : function(row, col, cm){
56204         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56205     },
56206
56207     // private
56208     onEditorKey : function(field, e){
56209         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56210         if(k == e.TAB){
56211             e.stopEvent();
56212             ed.completeEdit();
56213             if(e.shiftKey){
56214                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56215             }else{
56216                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56217             }
56218         }else if(k == e.ENTER && !e.ctrlKey){
56219             e.stopEvent();
56220             ed.completeEdit();
56221             if(e.shiftKey){
56222                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56223             }else{
56224                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56225             }
56226         }else if(k == e.ESC){
56227             ed.cancelEdit();
56228         }
56229         if(newCell){
56230             g.startEditing(newCell[0], newCell[1]);
56231         }
56232     }
56233 });/*
56234  * Based on:
56235  * Ext JS Library 1.1.1
56236  * Copyright(c) 2006-2007, Ext JS, LLC.
56237  *
56238  * Originally Released Under LGPL - original licence link has changed is not relivant.
56239  *
56240  * Fork - LGPL
56241  * <script type="text/javascript">
56242  */
56243 /**
56244  * @class Roo.grid.CellSelectionModel
56245  * @extends Roo.grid.AbstractSelectionModel
56246  * This class provides the basic implementation for cell selection in a grid.
56247  * @constructor
56248  * @param {Object} config The object containing the configuration of this model.
56249  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56250  */
56251 Roo.grid.CellSelectionModel = function(config){
56252     Roo.apply(this, config);
56253
56254     this.selection = null;
56255
56256     this.addEvents({
56257         /**
56258              * @event beforerowselect
56259              * Fires before a cell is selected.
56260              * @param {SelectionModel} this
56261              * @param {Number} rowIndex The selected row index
56262              * @param {Number} colIndex The selected cell index
56263              */
56264             "beforecellselect" : true,
56265         /**
56266              * @event cellselect
56267              * Fires when a cell is selected.
56268              * @param {SelectionModel} this
56269              * @param {Number} rowIndex The selected row index
56270              * @param {Number} colIndex The selected cell index
56271              */
56272             "cellselect" : true,
56273         /**
56274              * @event selectionchange
56275              * Fires when the active selection changes.
56276              * @param {SelectionModel} this
56277              * @param {Object} selection null for no selection or an object (o) with two properties
56278                 <ul>
56279                 <li>o.record: the record object for the row the selection is in</li>
56280                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56281                 </ul>
56282              */
56283             "selectionchange" : true,
56284         /**
56285              * @event tabend
56286              * Fires when the tab (or enter) was pressed on the last editable cell
56287              * You can use this to trigger add new row.
56288              * @param {SelectionModel} this
56289              */
56290             "tabend" : true,
56291          /**
56292              * @event beforeeditnext
56293              * Fires before the next editable sell is made active
56294              * You can use this to skip to another cell or fire the tabend
56295              *    if you set cell to false
56296              * @param {Object} eventdata object : { cell : [ row, col ] } 
56297              */
56298             "beforeeditnext" : true
56299     });
56300     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56301 };
56302
56303 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56304     
56305     enter_is_tab: false,
56306
56307     /** @ignore */
56308     initEvents : function(){
56309         this.grid.on("mousedown", this.handleMouseDown, this);
56310         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56311         var view = this.grid.view;
56312         view.on("refresh", this.onViewChange, this);
56313         view.on("rowupdated", this.onRowUpdated, this);
56314         view.on("beforerowremoved", this.clearSelections, this);
56315         view.on("beforerowsinserted", this.clearSelections, this);
56316         if(this.grid.isEditor){
56317             this.grid.on("beforeedit", this.beforeEdit,  this);
56318         }
56319     },
56320
56321         //private
56322     beforeEdit : function(e){
56323         this.select(e.row, e.column, false, true, e.record);
56324     },
56325
56326         //private
56327     onRowUpdated : function(v, index, r){
56328         if(this.selection && this.selection.record == r){
56329             v.onCellSelect(index, this.selection.cell[1]);
56330         }
56331     },
56332
56333         //private
56334     onViewChange : function(){
56335         this.clearSelections(true);
56336     },
56337
56338         /**
56339          * Returns the currently selected cell,.
56340          * @return {Array} The selected cell (row, column) or null if none selected.
56341          */
56342     getSelectedCell : function(){
56343         return this.selection ? this.selection.cell : null;
56344     },
56345
56346     /**
56347      * Clears all selections.
56348      * @param {Boolean} true to prevent the gridview from being notified about the change.
56349      */
56350     clearSelections : function(preventNotify){
56351         var s = this.selection;
56352         if(s){
56353             if(preventNotify !== true){
56354                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56355             }
56356             this.selection = null;
56357             this.fireEvent("selectionchange", this, null);
56358         }
56359     },
56360
56361     /**
56362      * Returns true if there is a selection.
56363      * @return {Boolean}
56364      */
56365     hasSelection : function(){
56366         return this.selection ? true : false;
56367     },
56368
56369     /** @ignore */
56370     handleMouseDown : function(e, t){
56371         var v = this.grid.getView();
56372         if(this.isLocked()){
56373             return;
56374         };
56375         var row = v.findRowIndex(t);
56376         var cell = v.findCellIndex(t);
56377         if(row !== false && cell !== false){
56378             this.select(row, cell);
56379         }
56380     },
56381
56382     /**
56383      * Selects a cell.
56384      * @param {Number} rowIndex
56385      * @param {Number} collIndex
56386      */
56387     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56388         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56389             this.clearSelections();
56390             r = r || this.grid.dataSource.getAt(rowIndex);
56391             this.selection = {
56392                 record : r,
56393                 cell : [rowIndex, colIndex]
56394             };
56395             if(!preventViewNotify){
56396                 var v = this.grid.getView();
56397                 v.onCellSelect(rowIndex, colIndex);
56398                 if(preventFocus !== true){
56399                     v.focusCell(rowIndex, colIndex);
56400                 }
56401             }
56402             this.fireEvent("cellselect", this, rowIndex, colIndex);
56403             this.fireEvent("selectionchange", this, this.selection);
56404         }
56405     },
56406
56407         //private
56408     isSelectable : function(rowIndex, colIndex, cm){
56409         return !cm.isHidden(colIndex);
56410     },
56411
56412     /** @ignore */
56413     handleKeyDown : function(e){
56414         //Roo.log('Cell Sel Model handleKeyDown');
56415         if(!e.isNavKeyPress()){
56416             return;
56417         }
56418         var g = this.grid, s = this.selection;
56419         if(!s){
56420             e.stopEvent();
56421             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56422             if(cell){
56423                 this.select(cell[0], cell[1]);
56424             }
56425             return;
56426         }
56427         var sm = this;
56428         var walk = function(row, col, step){
56429             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56430         };
56431         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56432         var newCell;
56433
56434       
56435
56436         switch(k){
56437             case e.TAB:
56438                 // handled by onEditorKey
56439                 if (g.isEditor && g.editing) {
56440                     return;
56441                 }
56442                 if(e.shiftKey) {
56443                     newCell = walk(r, c-1, -1);
56444                 } else {
56445                     newCell = walk(r, c+1, 1);
56446                 }
56447                 break;
56448             
56449             case e.DOWN:
56450                newCell = walk(r+1, c, 1);
56451                 break;
56452             
56453             case e.UP:
56454                 newCell = walk(r-1, c, -1);
56455                 break;
56456             
56457             case e.RIGHT:
56458                 newCell = walk(r, c+1, 1);
56459                 break;
56460             
56461             case e.LEFT:
56462                 newCell = walk(r, c-1, -1);
56463                 break;
56464             
56465             case e.ENTER:
56466                 
56467                 if(g.isEditor && !g.editing){
56468                    g.startEditing(r, c);
56469                    e.stopEvent();
56470                    return;
56471                 }
56472                 
56473                 
56474              break;
56475         };
56476         if(newCell){
56477             this.select(newCell[0], newCell[1]);
56478             e.stopEvent();
56479             
56480         }
56481     },
56482
56483     acceptsNav : function(row, col, cm){
56484         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56485     },
56486     /**
56487      * Selects a cell.
56488      * @param {Number} field (not used) - as it's normally used as a listener
56489      * @param {Number} e - event - fake it by using
56490      *
56491      * var e = Roo.EventObjectImpl.prototype;
56492      * e.keyCode = e.TAB
56493      *
56494      * 
56495      */
56496     onEditorKey : function(field, e){
56497         
56498         var k = e.getKey(),
56499             newCell,
56500             g = this.grid,
56501             ed = g.activeEditor,
56502             forward = false;
56503         ///Roo.log('onEditorKey' + k);
56504         
56505         
56506         if (this.enter_is_tab && k == e.ENTER) {
56507             k = e.TAB;
56508         }
56509         
56510         if(k == e.TAB){
56511             if(e.shiftKey){
56512                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56513             }else{
56514                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56515                 forward = true;
56516             }
56517             
56518             e.stopEvent();
56519             
56520         } else if(k == e.ENTER &&  !e.ctrlKey){
56521             ed.completeEdit();
56522             e.stopEvent();
56523             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56524         
56525                 } else if(k == e.ESC){
56526             ed.cancelEdit();
56527         }
56528                 
56529         if (newCell) {
56530             var ecall = { cell : newCell, forward : forward };
56531             this.fireEvent('beforeeditnext', ecall );
56532             newCell = ecall.cell;
56533                         forward = ecall.forward;
56534         }
56535                 
56536         if(newCell){
56537             //Roo.log('next cell after edit');
56538             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56539         } else if (forward) {
56540             // tabbed past last
56541             this.fireEvent.defer(100, this, ['tabend',this]);
56542         }
56543     }
56544 });/*
56545  * Based on:
56546  * Ext JS Library 1.1.1
56547  * Copyright(c) 2006-2007, Ext JS, LLC.
56548  *
56549  * Originally Released Under LGPL - original licence link has changed is not relivant.
56550  *
56551  * Fork - LGPL
56552  * <script type="text/javascript">
56553  */
56554  
56555 /**
56556  * @class Roo.grid.EditorGrid
56557  * @extends Roo.grid.Grid
56558  * Class for creating and editable grid.
56559  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56560  * The container MUST have some type of size defined for the grid to fill. The container will be 
56561  * automatically set to position relative if it isn't already.
56562  * @param {Object} dataSource The data model to bind to
56563  * @param {Object} colModel The column model with info about this grid's columns
56564  */
56565 Roo.grid.EditorGrid = function(container, config){
56566     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56567     this.getGridEl().addClass("xedit-grid");
56568
56569     if(!this.selModel){
56570         this.selModel = new Roo.grid.CellSelectionModel();
56571     }
56572
56573     this.activeEditor = null;
56574
56575         this.addEvents({
56576             /**
56577              * @event beforeedit
56578              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56579              * <ul style="padding:5px;padding-left:16px;">
56580              * <li>grid - This grid</li>
56581              * <li>record - The record being edited</li>
56582              * <li>field - The field name being edited</li>
56583              * <li>value - The value for the field being edited.</li>
56584              * <li>row - The grid row index</li>
56585              * <li>column - The grid column index</li>
56586              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56587              * </ul>
56588              * @param {Object} e An edit event (see above for description)
56589              */
56590             "beforeedit" : true,
56591             /**
56592              * @event afteredit
56593              * Fires after a cell is edited. <br />
56594              * <ul style="padding:5px;padding-left:16px;">
56595              * <li>grid - This grid</li>
56596              * <li>record - The record being edited</li>
56597              * <li>field - The field name being edited</li>
56598              * <li>value - The value being set</li>
56599              * <li>originalValue - The original value for the field, before the edit.</li>
56600              * <li>row - The grid row index</li>
56601              * <li>column - The grid column index</li>
56602              * </ul>
56603              * @param {Object} e An edit event (see above for description)
56604              */
56605             "afteredit" : true,
56606             /**
56607              * @event validateedit
56608              * Fires after a cell is edited, but before the value is set in the record. 
56609          * You can use this to modify the value being set in the field, Return false
56610              * to cancel the change. The edit event object has the following properties <br />
56611              * <ul style="padding:5px;padding-left:16px;">
56612          * <li>editor - This editor</li>
56613              * <li>grid - This grid</li>
56614              * <li>record - The record being edited</li>
56615              * <li>field - The field name being edited</li>
56616              * <li>value - The value being set</li>
56617              * <li>originalValue - The original value for the field, before the edit.</li>
56618              * <li>row - The grid row index</li>
56619              * <li>column - The grid column index</li>
56620              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56621              * </ul>
56622              * @param {Object} e An edit event (see above for description)
56623              */
56624             "validateedit" : true
56625         });
56626     this.on("bodyscroll", this.stopEditing,  this);
56627     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56628 };
56629
56630 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56631     /**
56632      * @cfg {Number} clicksToEdit
56633      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56634      */
56635     clicksToEdit: 2,
56636
56637     // private
56638     isEditor : true,
56639     // private
56640     trackMouseOver: false, // causes very odd FF errors
56641
56642     onCellDblClick : function(g, row, col){
56643         this.startEditing(row, col);
56644     },
56645
56646     onEditComplete : function(ed, value, startValue){
56647         this.editing = false;
56648         this.activeEditor = null;
56649         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56650         var r = ed.record;
56651         var field = this.colModel.getDataIndex(ed.col);
56652         var e = {
56653             grid: this,
56654             record: r,
56655             field: field,
56656             originalValue: startValue,
56657             value: value,
56658             row: ed.row,
56659             column: ed.col,
56660             cancel:false,
56661             editor: ed
56662         };
56663         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56664         cell.show();
56665           
56666         if(String(value) !== String(startValue)){
56667             
56668             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56669                 r.set(field, e.value);
56670                 // if we are dealing with a combo box..
56671                 // then we also set the 'name' colum to be the displayField
56672                 if (ed.field.displayField && ed.field.name) {
56673                     r.set(ed.field.name, ed.field.el.dom.value);
56674                 }
56675                 
56676                 delete e.cancel; //?? why!!!
56677                 this.fireEvent("afteredit", e);
56678             }
56679         } else {
56680             this.fireEvent("afteredit", e); // always fire it!
56681         }
56682         this.view.focusCell(ed.row, ed.col);
56683     },
56684
56685     /**
56686      * Starts editing the specified for the specified row/column
56687      * @param {Number} rowIndex
56688      * @param {Number} colIndex
56689      */
56690     startEditing : function(row, col){
56691         this.stopEditing();
56692         if(this.colModel.isCellEditable(col, row)){
56693             this.view.ensureVisible(row, col, true);
56694           
56695             var r = this.dataSource.getAt(row);
56696             var field = this.colModel.getDataIndex(col);
56697             var cell = Roo.get(this.view.getCell(row,col));
56698             var e = {
56699                 grid: this,
56700                 record: r,
56701                 field: field,
56702                 value: r.data[field],
56703                 row: row,
56704                 column: col,
56705                 cancel:false 
56706             };
56707             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56708                 this.editing = true;
56709                 var ed = this.colModel.getCellEditor(col, row);
56710                 
56711                 if (!ed) {
56712                     return;
56713                 }
56714                 if(!ed.rendered){
56715                     ed.render(ed.parentEl || document.body);
56716                 }
56717                 ed.field.reset();
56718                
56719                 cell.hide();
56720                 
56721                 (function(){ // complex but required for focus issues in safari, ie and opera
56722                     ed.row = row;
56723                     ed.col = col;
56724                     ed.record = r;
56725                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56726                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56727                     this.activeEditor = ed;
56728                     var v = r.data[field];
56729                     ed.startEdit(this.view.getCell(row, col), v);
56730                     // combo's with 'displayField and name set
56731                     if (ed.field.displayField && ed.field.name) {
56732                         ed.field.el.dom.value = r.data[ed.field.name];
56733                     }
56734                     
56735                     
56736                 }).defer(50, this);
56737             }
56738         }
56739     },
56740         
56741     /**
56742      * Stops any active editing
56743      */
56744     stopEditing : function(){
56745         if(this.activeEditor){
56746             this.activeEditor.completeEdit();
56747         }
56748         this.activeEditor = null;
56749     },
56750         
56751          /**
56752      * Called to get grid's drag proxy text, by default returns this.ddText.
56753      * @return {String}
56754      */
56755     getDragDropText : function(){
56756         var count = this.selModel.getSelectedCell() ? 1 : 0;
56757         return String.format(this.ddText, count, count == 1 ? '' : 's');
56758     }
56759         
56760 });/*
56761  * Based on:
56762  * Ext JS Library 1.1.1
56763  * Copyright(c) 2006-2007, Ext JS, LLC.
56764  *
56765  * Originally Released Under LGPL - original licence link has changed is not relivant.
56766  *
56767  * Fork - LGPL
56768  * <script type="text/javascript">
56769  */
56770
56771 // private - not really -- you end up using it !
56772 // This is a support class used internally by the Grid components
56773
56774 /**
56775  * @class Roo.grid.GridEditor
56776  * @extends Roo.Editor
56777  * Class for creating and editable grid elements.
56778  * @param {Object} config any settings (must include field)
56779  */
56780 Roo.grid.GridEditor = function(field, config){
56781     if (!config && field.field) {
56782         config = field;
56783         field = Roo.factory(config.field, Roo.form);
56784     }
56785     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56786     field.monitorTab = false;
56787 };
56788
56789 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56790     
56791     /**
56792      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56793      */
56794     
56795     alignment: "tl-tl",
56796     autoSize: "width",
56797     hideEl : false,
56798     cls: "x-small-editor x-grid-editor",
56799     shim:false,
56800     shadow:"frame"
56801 });/*
56802  * Based on:
56803  * Ext JS Library 1.1.1
56804  * Copyright(c) 2006-2007, Ext JS, LLC.
56805  *
56806  * Originally Released Under LGPL - original licence link has changed is not relivant.
56807  *
56808  * Fork - LGPL
56809  * <script type="text/javascript">
56810  */
56811   
56812
56813   
56814 Roo.grid.PropertyRecord = Roo.data.Record.create([
56815     {name:'name',type:'string'},  'value'
56816 ]);
56817
56818
56819 Roo.grid.PropertyStore = function(grid, source){
56820     this.grid = grid;
56821     this.store = new Roo.data.Store({
56822         recordType : Roo.grid.PropertyRecord
56823     });
56824     this.store.on('update', this.onUpdate,  this);
56825     if(source){
56826         this.setSource(source);
56827     }
56828     Roo.grid.PropertyStore.superclass.constructor.call(this);
56829 };
56830
56831
56832
56833 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56834     setSource : function(o){
56835         this.source = o;
56836         this.store.removeAll();
56837         var data = [];
56838         for(var k in o){
56839             if(this.isEditableValue(o[k])){
56840                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56841             }
56842         }
56843         this.store.loadRecords({records: data}, {}, true);
56844     },
56845
56846     onUpdate : function(ds, record, type){
56847         if(type == Roo.data.Record.EDIT){
56848             var v = record.data['value'];
56849             var oldValue = record.modified['value'];
56850             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56851                 this.source[record.id] = v;
56852                 record.commit();
56853                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56854             }else{
56855                 record.reject();
56856             }
56857         }
56858     },
56859
56860     getProperty : function(row){
56861        return this.store.getAt(row);
56862     },
56863
56864     isEditableValue: function(val){
56865         if(val && val instanceof Date){
56866             return true;
56867         }else if(typeof val == 'object' || typeof val == 'function'){
56868             return false;
56869         }
56870         return true;
56871     },
56872
56873     setValue : function(prop, value){
56874         this.source[prop] = value;
56875         this.store.getById(prop).set('value', value);
56876     },
56877
56878     getSource : function(){
56879         return this.source;
56880     }
56881 });
56882
56883 Roo.grid.PropertyColumnModel = function(grid, store){
56884     this.grid = grid;
56885     var g = Roo.grid;
56886     g.PropertyColumnModel.superclass.constructor.call(this, [
56887         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56888         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56889     ]);
56890     this.store = store;
56891     this.bselect = Roo.DomHelper.append(document.body, {
56892         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56893             {tag: 'option', value: 'true', html: 'true'},
56894             {tag: 'option', value: 'false', html: 'false'}
56895         ]
56896     });
56897     Roo.id(this.bselect);
56898     var f = Roo.form;
56899     this.editors = {
56900         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56901         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56902         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56903         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56904         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56905     };
56906     this.renderCellDelegate = this.renderCell.createDelegate(this);
56907     this.renderPropDelegate = this.renderProp.createDelegate(this);
56908 };
56909
56910 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56911     
56912     
56913     nameText : 'Name',
56914     valueText : 'Value',
56915     
56916     dateFormat : 'm/j/Y',
56917     
56918     
56919     renderDate : function(dateVal){
56920         return dateVal.dateFormat(this.dateFormat);
56921     },
56922
56923     renderBool : function(bVal){
56924         return bVal ? 'true' : 'false';
56925     },
56926
56927     isCellEditable : function(colIndex, rowIndex){
56928         return colIndex == 1;
56929     },
56930
56931     getRenderer : function(col){
56932         return col == 1 ?
56933             this.renderCellDelegate : this.renderPropDelegate;
56934     },
56935
56936     renderProp : function(v){
56937         return this.getPropertyName(v);
56938     },
56939
56940     renderCell : function(val){
56941         var rv = val;
56942         if(val instanceof Date){
56943             rv = this.renderDate(val);
56944         }else if(typeof val == 'boolean'){
56945             rv = this.renderBool(val);
56946         }
56947         return Roo.util.Format.htmlEncode(rv);
56948     },
56949
56950     getPropertyName : function(name){
56951         var pn = this.grid.propertyNames;
56952         return pn && pn[name] ? pn[name] : name;
56953     },
56954
56955     getCellEditor : function(colIndex, rowIndex){
56956         var p = this.store.getProperty(rowIndex);
56957         var n = p.data['name'], val = p.data['value'];
56958         
56959         if(typeof(this.grid.customEditors[n]) == 'string'){
56960             return this.editors[this.grid.customEditors[n]];
56961         }
56962         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56963             return this.grid.customEditors[n];
56964         }
56965         if(val instanceof Date){
56966             return this.editors['date'];
56967         }else if(typeof val == 'number'){
56968             return this.editors['number'];
56969         }else if(typeof val == 'boolean'){
56970             return this.editors['boolean'];
56971         }else{
56972             return this.editors['string'];
56973         }
56974     }
56975 });
56976
56977 /**
56978  * @class Roo.grid.PropertyGrid
56979  * @extends Roo.grid.EditorGrid
56980  * This class represents the  interface of a component based property grid control.
56981  * <br><br>Usage:<pre><code>
56982  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56983       
56984  });
56985  // set any options
56986  grid.render();
56987  * </code></pre>
56988   
56989  * @constructor
56990  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56991  * The container MUST have some type of size defined for the grid to fill. The container will be
56992  * automatically set to position relative if it isn't already.
56993  * @param {Object} config A config object that sets properties on this grid.
56994  */
56995 Roo.grid.PropertyGrid = function(container, config){
56996     config = config || {};
56997     var store = new Roo.grid.PropertyStore(this);
56998     this.store = store;
56999     var cm = new Roo.grid.PropertyColumnModel(this, store);
57000     store.store.sort('name', 'ASC');
57001     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57002         ds: store.store,
57003         cm: cm,
57004         enableColLock:false,
57005         enableColumnMove:false,
57006         stripeRows:false,
57007         trackMouseOver: false,
57008         clicksToEdit:1
57009     }, config));
57010     this.getGridEl().addClass('x-props-grid');
57011     this.lastEditRow = null;
57012     this.on('columnresize', this.onColumnResize, this);
57013     this.addEvents({
57014          /**
57015              * @event beforepropertychange
57016              * Fires before a property changes (return false to stop?)
57017              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57018              * @param {String} id Record Id
57019              * @param {String} newval New Value
57020          * @param {String} oldval Old Value
57021              */
57022         "beforepropertychange": true,
57023         /**
57024              * @event propertychange
57025              * Fires after a property changes
57026              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57027              * @param {String} id Record Id
57028              * @param {String} newval New Value
57029          * @param {String} oldval Old Value
57030              */
57031         "propertychange": true
57032     });
57033     this.customEditors = this.customEditors || {};
57034 };
57035 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57036     
57037      /**
57038      * @cfg {Object} customEditors map of colnames=> custom editors.
57039      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57040      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57041      * false disables editing of the field.
57042          */
57043     
57044       /**
57045      * @cfg {Object} propertyNames map of property Names to their displayed value
57046          */
57047     
57048     render : function(){
57049         Roo.grid.PropertyGrid.superclass.render.call(this);
57050         this.autoSize.defer(100, this);
57051     },
57052
57053     autoSize : function(){
57054         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57055         if(this.view){
57056             this.view.fitColumns();
57057         }
57058     },
57059
57060     onColumnResize : function(){
57061         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57062         this.autoSize();
57063     },
57064     /**
57065      * Sets the data for the Grid
57066      * accepts a Key => Value object of all the elements avaiable.
57067      * @param {Object} data  to appear in grid.
57068      */
57069     setSource : function(source){
57070         this.store.setSource(source);
57071         //this.autoSize();
57072     },
57073     /**
57074      * Gets all the data from the grid.
57075      * @return {Object} data  data stored in grid
57076      */
57077     getSource : function(){
57078         return this.store.getSource();
57079     }
57080 });/*
57081   
57082  * Licence LGPL
57083  
57084  */
57085  
57086 /**
57087  * @class Roo.grid.Calendar
57088  * @extends Roo.util.Grid
57089  * This class extends the Grid to provide a calendar widget
57090  * <br><br>Usage:<pre><code>
57091  var grid = new Roo.grid.Calendar("my-container-id", {
57092      ds: myDataStore,
57093      cm: myColModel,
57094      selModel: mySelectionModel,
57095      autoSizeColumns: true,
57096      monitorWindowResize: false,
57097      trackMouseOver: true
57098      eventstore : real data store..
57099  });
57100  // set any options
57101  grid.render();
57102   
57103   * @constructor
57104  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57105  * The container MUST have some type of size defined for the grid to fill. The container will be
57106  * automatically set to position relative if it isn't already.
57107  * @param {Object} config A config object that sets properties on this grid.
57108  */
57109 Roo.grid.Calendar = function(container, config){
57110         // initialize the container
57111         this.container = Roo.get(container);
57112         this.container.update("");
57113         this.container.setStyle("overflow", "hidden");
57114     this.container.addClass('x-grid-container');
57115
57116     this.id = this.container.id;
57117
57118     Roo.apply(this, config);
57119     // check and correct shorthanded configs
57120     
57121     var rows = [];
57122     var d =1;
57123     for (var r = 0;r < 6;r++) {
57124         
57125         rows[r]=[];
57126         for (var c =0;c < 7;c++) {
57127             rows[r][c]= '';
57128         }
57129     }
57130     if (this.eventStore) {
57131         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57132         this.eventStore.on('load',this.onLoad, this);
57133         this.eventStore.on('beforeload',this.clearEvents, this);
57134          
57135     }
57136     
57137     this.dataSource = new Roo.data.Store({
57138             proxy: new Roo.data.MemoryProxy(rows),
57139             reader: new Roo.data.ArrayReader({}, [
57140                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57141     });
57142
57143     this.dataSource.load();
57144     this.ds = this.dataSource;
57145     this.ds.xmodule = this.xmodule || false;
57146     
57147     
57148     var cellRender = function(v,x,r)
57149     {
57150         return String.format(
57151             '<div class="fc-day  fc-widget-content"><div>' +
57152                 '<div class="fc-event-container"></div>' +
57153                 '<div class="fc-day-number">{0}</div>'+
57154                 
57155                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57156             '</div></div>', v);
57157     
57158     }
57159     
57160     
57161     this.colModel = new Roo.grid.ColumnModel( [
57162         {
57163             xtype: 'ColumnModel',
57164             xns: Roo.grid,
57165             dataIndex : 'weekday0',
57166             header : 'Sunday',
57167             renderer : cellRender
57168         },
57169         {
57170             xtype: 'ColumnModel',
57171             xns: Roo.grid,
57172             dataIndex : 'weekday1',
57173             header : 'Monday',
57174             renderer : cellRender
57175         },
57176         {
57177             xtype: 'ColumnModel',
57178             xns: Roo.grid,
57179             dataIndex : 'weekday2',
57180             header : 'Tuesday',
57181             renderer : cellRender
57182         },
57183         {
57184             xtype: 'ColumnModel',
57185             xns: Roo.grid,
57186             dataIndex : 'weekday3',
57187             header : 'Wednesday',
57188             renderer : cellRender
57189         },
57190         {
57191             xtype: 'ColumnModel',
57192             xns: Roo.grid,
57193             dataIndex : 'weekday4',
57194             header : 'Thursday',
57195             renderer : cellRender
57196         },
57197         {
57198             xtype: 'ColumnModel',
57199             xns: Roo.grid,
57200             dataIndex : 'weekday5',
57201             header : 'Friday',
57202             renderer : cellRender
57203         },
57204         {
57205             xtype: 'ColumnModel',
57206             xns: Roo.grid,
57207             dataIndex : 'weekday6',
57208             header : 'Saturday',
57209             renderer : cellRender
57210         }
57211     ]);
57212     this.cm = this.colModel;
57213     this.cm.xmodule = this.xmodule || false;
57214  
57215         
57216           
57217     //this.selModel = new Roo.grid.CellSelectionModel();
57218     //this.sm = this.selModel;
57219     //this.selModel.init(this);
57220     
57221     
57222     if(this.width){
57223         this.container.setWidth(this.width);
57224     }
57225
57226     if(this.height){
57227         this.container.setHeight(this.height);
57228     }
57229     /** @private */
57230         this.addEvents({
57231         // raw events
57232         /**
57233          * @event click
57234          * The raw click event for the entire grid.
57235          * @param {Roo.EventObject} e
57236          */
57237         "click" : true,
57238         /**
57239          * @event dblclick
57240          * The raw dblclick event for the entire grid.
57241          * @param {Roo.EventObject} e
57242          */
57243         "dblclick" : true,
57244         /**
57245          * @event contextmenu
57246          * The raw contextmenu event for the entire grid.
57247          * @param {Roo.EventObject} e
57248          */
57249         "contextmenu" : true,
57250         /**
57251          * @event mousedown
57252          * The raw mousedown event for the entire grid.
57253          * @param {Roo.EventObject} e
57254          */
57255         "mousedown" : true,
57256         /**
57257          * @event mouseup
57258          * The raw mouseup event for the entire grid.
57259          * @param {Roo.EventObject} e
57260          */
57261         "mouseup" : true,
57262         /**
57263          * @event mouseover
57264          * The raw mouseover event for the entire grid.
57265          * @param {Roo.EventObject} e
57266          */
57267         "mouseover" : true,
57268         /**
57269          * @event mouseout
57270          * The raw mouseout event for the entire grid.
57271          * @param {Roo.EventObject} e
57272          */
57273         "mouseout" : true,
57274         /**
57275          * @event keypress
57276          * The raw keypress event for the entire grid.
57277          * @param {Roo.EventObject} e
57278          */
57279         "keypress" : true,
57280         /**
57281          * @event keydown
57282          * The raw keydown event for the entire grid.
57283          * @param {Roo.EventObject} e
57284          */
57285         "keydown" : true,
57286
57287         // custom events
57288
57289         /**
57290          * @event cellclick
57291          * Fires when a cell is clicked
57292          * @param {Grid} this
57293          * @param {Number} rowIndex
57294          * @param {Number} columnIndex
57295          * @param {Roo.EventObject} e
57296          */
57297         "cellclick" : true,
57298         /**
57299          * @event celldblclick
57300          * Fires when a cell is double clicked
57301          * @param {Grid} this
57302          * @param {Number} rowIndex
57303          * @param {Number} columnIndex
57304          * @param {Roo.EventObject} e
57305          */
57306         "celldblclick" : true,
57307         /**
57308          * @event rowclick
57309          * Fires when a row is clicked
57310          * @param {Grid} this
57311          * @param {Number} rowIndex
57312          * @param {Roo.EventObject} e
57313          */
57314         "rowclick" : true,
57315         /**
57316          * @event rowdblclick
57317          * Fires when a row is double clicked
57318          * @param {Grid} this
57319          * @param {Number} rowIndex
57320          * @param {Roo.EventObject} e
57321          */
57322         "rowdblclick" : true,
57323         /**
57324          * @event headerclick
57325          * Fires when a header is clicked
57326          * @param {Grid} this
57327          * @param {Number} columnIndex
57328          * @param {Roo.EventObject} e
57329          */
57330         "headerclick" : true,
57331         /**
57332          * @event headerdblclick
57333          * Fires when a header cell is double clicked
57334          * @param {Grid} this
57335          * @param {Number} columnIndex
57336          * @param {Roo.EventObject} e
57337          */
57338         "headerdblclick" : true,
57339         /**
57340          * @event rowcontextmenu
57341          * Fires when a row is right clicked
57342          * @param {Grid} this
57343          * @param {Number} rowIndex
57344          * @param {Roo.EventObject} e
57345          */
57346         "rowcontextmenu" : true,
57347         /**
57348          * @event cellcontextmenu
57349          * Fires when a cell is right clicked
57350          * @param {Grid} this
57351          * @param {Number} rowIndex
57352          * @param {Number} cellIndex
57353          * @param {Roo.EventObject} e
57354          */
57355          "cellcontextmenu" : true,
57356         /**
57357          * @event headercontextmenu
57358          * Fires when a header is right clicked
57359          * @param {Grid} this
57360          * @param {Number} columnIndex
57361          * @param {Roo.EventObject} e
57362          */
57363         "headercontextmenu" : true,
57364         /**
57365          * @event bodyscroll
57366          * Fires when the body element is scrolled
57367          * @param {Number} scrollLeft
57368          * @param {Number} scrollTop
57369          */
57370         "bodyscroll" : true,
57371         /**
57372          * @event columnresize
57373          * Fires when the user resizes a column
57374          * @param {Number} columnIndex
57375          * @param {Number} newSize
57376          */
57377         "columnresize" : true,
57378         /**
57379          * @event columnmove
57380          * Fires when the user moves a column
57381          * @param {Number} oldIndex
57382          * @param {Number} newIndex
57383          */
57384         "columnmove" : true,
57385         /**
57386          * @event startdrag
57387          * Fires when row(s) start being dragged
57388          * @param {Grid} this
57389          * @param {Roo.GridDD} dd The drag drop object
57390          * @param {event} e The raw browser event
57391          */
57392         "startdrag" : true,
57393         /**
57394          * @event enddrag
57395          * Fires when a drag operation is complete
57396          * @param {Grid} this
57397          * @param {Roo.GridDD} dd The drag drop object
57398          * @param {event} e The raw browser event
57399          */
57400         "enddrag" : true,
57401         /**
57402          * @event dragdrop
57403          * Fires when dragged row(s) are dropped on a valid DD target
57404          * @param {Grid} this
57405          * @param {Roo.GridDD} dd The drag drop object
57406          * @param {String} targetId The target drag drop object
57407          * @param {event} e The raw browser event
57408          */
57409         "dragdrop" : true,
57410         /**
57411          * @event dragover
57412          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57413          * @param {Grid} this
57414          * @param {Roo.GridDD} dd The drag drop object
57415          * @param {String} targetId The target drag drop object
57416          * @param {event} e The raw browser event
57417          */
57418         "dragover" : true,
57419         /**
57420          * @event dragenter
57421          *  Fires when the dragged row(s) first cross another DD target while being dragged
57422          * @param {Grid} this
57423          * @param {Roo.GridDD} dd The drag drop object
57424          * @param {String} targetId The target drag drop object
57425          * @param {event} e The raw browser event
57426          */
57427         "dragenter" : true,
57428         /**
57429          * @event dragout
57430          * Fires when the dragged row(s) leave another DD target while being dragged
57431          * @param {Grid} this
57432          * @param {Roo.GridDD} dd The drag drop object
57433          * @param {String} targetId The target drag drop object
57434          * @param {event} e The raw browser event
57435          */
57436         "dragout" : true,
57437         /**
57438          * @event rowclass
57439          * Fires when a row is rendered, so you can change add a style to it.
57440          * @param {GridView} gridview   The grid view
57441          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57442          */
57443         'rowclass' : true,
57444
57445         /**
57446          * @event render
57447          * Fires when the grid is rendered
57448          * @param {Grid} grid
57449          */
57450         'render' : true,
57451             /**
57452              * @event select
57453              * Fires when a date is selected
57454              * @param {DatePicker} this
57455              * @param {Date} date The selected date
57456              */
57457         'select': true,
57458         /**
57459              * @event monthchange
57460              * Fires when the displayed month changes 
57461              * @param {DatePicker} this
57462              * @param {Date} date The selected month
57463              */
57464         'monthchange': true,
57465         /**
57466              * @event evententer
57467              * Fires when mouse over an event
57468              * @param {Calendar} this
57469              * @param {event} Event
57470              */
57471         'evententer': true,
57472         /**
57473              * @event eventleave
57474              * Fires when the mouse leaves an
57475              * @param {Calendar} this
57476              * @param {event}
57477              */
57478         'eventleave': true,
57479         /**
57480              * @event eventclick
57481              * Fires when the mouse click an
57482              * @param {Calendar} this
57483              * @param {event}
57484              */
57485         'eventclick': true,
57486         /**
57487              * @event eventrender
57488              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57489              * @param {Calendar} this
57490              * @param {data} data to be modified
57491              */
57492         'eventrender': true
57493         
57494     });
57495
57496     Roo.grid.Grid.superclass.constructor.call(this);
57497     this.on('render', function() {
57498         this.view.el.addClass('x-grid-cal'); 
57499         
57500         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57501
57502     },this);
57503     
57504     if (!Roo.grid.Calendar.style) {
57505         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57506             
57507             
57508             '.x-grid-cal .x-grid-col' :  {
57509                 height: 'auto !important',
57510                 'vertical-align': 'top'
57511             },
57512             '.x-grid-cal  .fc-event-hori' : {
57513                 height: '14px'
57514             }
57515              
57516             
57517         }, Roo.id());
57518     }
57519
57520     
57521     
57522 };
57523 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57524     /**
57525      * @cfg {Store} eventStore The store that loads events.
57526      */
57527     eventStore : 25,
57528
57529      
57530     activeDate : false,
57531     startDay : 0,
57532     autoWidth : true,
57533     monitorWindowResize : false,
57534
57535     
57536     resizeColumns : function() {
57537         var col = (this.view.el.getWidth() / 7) - 3;
57538         // loop through cols, and setWidth
57539         for(var i =0 ; i < 7 ; i++){
57540             this.cm.setColumnWidth(i, col);
57541         }
57542     },
57543      setDate :function(date) {
57544         
57545         Roo.log('setDate?');
57546         
57547         this.resizeColumns();
57548         var vd = this.activeDate;
57549         this.activeDate = date;
57550 //        if(vd && this.el){
57551 //            var t = date.getTime();
57552 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57553 //                Roo.log('using add remove');
57554 //                
57555 //                this.fireEvent('monthchange', this, date);
57556 //                
57557 //                this.cells.removeClass("fc-state-highlight");
57558 //                this.cells.each(function(c){
57559 //                   if(c.dateValue == t){
57560 //                       c.addClass("fc-state-highlight");
57561 //                       setTimeout(function(){
57562 //                            try{c.dom.firstChild.focus();}catch(e){}
57563 //                       }, 50);
57564 //                       return false;
57565 //                   }
57566 //                   return true;
57567 //                });
57568 //                return;
57569 //            }
57570 //        }
57571         
57572         var days = date.getDaysInMonth();
57573         
57574         var firstOfMonth = date.getFirstDateOfMonth();
57575         var startingPos = firstOfMonth.getDay()-this.startDay;
57576         
57577         if(startingPos < this.startDay){
57578             startingPos += 7;
57579         }
57580         
57581         var pm = date.add(Date.MONTH, -1);
57582         var prevStart = pm.getDaysInMonth()-startingPos;
57583 //        
57584         
57585         
57586         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57587         
57588         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57589         //this.cells.addClassOnOver('fc-state-hover');
57590         
57591         var cells = this.cells.elements;
57592         var textEls = this.textNodes;
57593         
57594         //Roo.each(cells, function(cell){
57595         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57596         //});
57597         
57598         days += startingPos;
57599
57600         // convert everything to numbers so it's fast
57601         var day = 86400000;
57602         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57603         //Roo.log(d);
57604         //Roo.log(pm);
57605         //Roo.log(prevStart);
57606         
57607         var today = new Date().clearTime().getTime();
57608         var sel = date.clearTime().getTime();
57609         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57610         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57611         var ddMatch = this.disabledDatesRE;
57612         var ddText = this.disabledDatesText;
57613         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57614         var ddaysText = this.disabledDaysText;
57615         var format = this.format;
57616         
57617         var setCellClass = function(cal, cell){
57618             
57619             //Roo.log('set Cell Class');
57620             cell.title = "";
57621             var t = d.getTime();
57622             
57623             //Roo.log(d);
57624             
57625             
57626             cell.dateValue = t;
57627             if(t == today){
57628                 cell.className += " fc-today";
57629                 cell.className += " fc-state-highlight";
57630                 cell.title = cal.todayText;
57631             }
57632             if(t == sel){
57633                 // disable highlight in other month..
57634                 cell.className += " fc-state-highlight";
57635                 
57636             }
57637             // disabling
57638             if(t < min) {
57639                 //cell.className = " fc-state-disabled";
57640                 cell.title = cal.minText;
57641                 return;
57642             }
57643             if(t > max) {
57644                 //cell.className = " fc-state-disabled";
57645                 cell.title = cal.maxText;
57646                 return;
57647             }
57648             if(ddays){
57649                 if(ddays.indexOf(d.getDay()) != -1){
57650                     // cell.title = ddaysText;
57651                    // cell.className = " fc-state-disabled";
57652                 }
57653             }
57654             if(ddMatch && format){
57655                 var fvalue = d.dateFormat(format);
57656                 if(ddMatch.test(fvalue)){
57657                     cell.title = ddText.replace("%0", fvalue);
57658                    cell.className = " fc-state-disabled";
57659                 }
57660             }
57661             
57662             if (!cell.initialClassName) {
57663                 cell.initialClassName = cell.dom.className;
57664             }
57665             
57666             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57667         };
57668
57669         var i = 0;
57670         
57671         for(; i < startingPos; i++) {
57672             cells[i].dayName =  (++prevStart);
57673             Roo.log(textEls[i]);
57674             d.setDate(d.getDate()+1);
57675             
57676             //cells[i].className = "fc-past fc-other-month";
57677             setCellClass(this, cells[i]);
57678         }
57679         
57680         var intDay = 0;
57681         
57682         for(; i < days; i++){
57683             intDay = i - startingPos + 1;
57684             cells[i].dayName =  (intDay);
57685             d.setDate(d.getDate()+1);
57686             
57687             cells[i].className = ''; // "x-date-active";
57688             setCellClass(this, cells[i]);
57689         }
57690         var extraDays = 0;
57691         
57692         for(; i < 42; i++) {
57693             //textEls[i].innerHTML = (++extraDays);
57694             
57695             d.setDate(d.getDate()+1);
57696             cells[i].dayName = (++extraDays);
57697             cells[i].className = "fc-future fc-other-month";
57698             setCellClass(this, cells[i]);
57699         }
57700         
57701         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57702         
57703         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57704         
57705         // this will cause all the cells to mis
57706         var rows= [];
57707         var i =0;
57708         for (var r = 0;r < 6;r++) {
57709             for (var c =0;c < 7;c++) {
57710                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57711             }    
57712         }
57713         
57714         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57715         for(i=0;i<cells.length;i++) {
57716             
57717             this.cells.elements[i].dayName = cells[i].dayName ;
57718             this.cells.elements[i].className = cells[i].className;
57719             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57720             this.cells.elements[i].title = cells[i].title ;
57721             this.cells.elements[i].dateValue = cells[i].dateValue ;
57722         }
57723         
57724         
57725         
57726         
57727         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57728         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57729         
57730         ////if(totalRows != 6){
57731             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57732            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57733        // }
57734         
57735         this.fireEvent('monthchange', this, date);
57736         
57737         
57738     },
57739  /**
57740      * Returns the grid's SelectionModel.
57741      * @return {SelectionModel}
57742      */
57743     getSelectionModel : function(){
57744         if(!this.selModel){
57745             this.selModel = new Roo.grid.CellSelectionModel();
57746         }
57747         return this.selModel;
57748     },
57749
57750     load: function() {
57751         this.eventStore.load()
57752         
57753         
57754         
57755     },
57756     
57757     findCell : function(dt) {
57758         dt = dt.clearTime().getTime();
57759         var ret = false;
57760         this.cells.each(function(c){
57761             //Roo.log("check " +c.dateValue + '?=' + dt);
57762             if(c.dateValue == dt){
57763                 ret = c;
57764                 return false;
57765             }
57766             return true;
57767         });
57768         
57769         return ret;
57770     },
57771     
57772     findCells : function(rec) {
57773         var s = rec.data.start_dt.clone().clearTime().getTime();
57774        // Roo.log(s);
57775         var e= rec.data.end_dt.clone().clearTime().getTime();
57776        // Roo.log(e);
57777         var ret = [];
57778         this.cells.each(function(c){
57779              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57780             
57781             if(c.dateValue > e){
57782                 return ;
57783             }
57784             if(c.dateValue < s){
57785                 return ;
57786             }
57787             ret.push(c);
57788         });
57789         
57790         return ret;    
57791     },
57792     
57793     findBestRow: function(cells)
57794     {
57795         var ret = 0;
57796         
57797         for (var i =0 ; i < cells.length;i++) {
57798             ret  = Math.max(cells[i].rows || 0,ret);
57799         }
57800         return ret;
57801         
57802     },
57803     
57804     
57805     addItem : function(rec)
57806     {
57807         // look for vertical location slot in
57808         var cells = this.findCells(rec);
57809         
57810         rec.row = this.findBestRow(cells);
57811         
57812         // work out the location.
57813         
57814         var crow = false;
57815         var rows = [];
57816         for(var i =0; i < cells.length; i++) {
57817             if (!crow) {
57818                 crow = {
57819                     start : cells[i],
57820                     end :  cells[i]
57821                 };
57822                 continue;
57823             }
57824             if (crow.start.getY() == cells[i].getY()) {
57825                 // on same row.
57826                 crow.end = cells[i];
57827                 continue;
57828             }
57829             // different row.
57830             rows.push(crow);
57831             crow = {
57832                 start: cells[i],
57833                 end : cells[i]
57834             };
57835             
57836         }
57837         
57838         rows.push(crow);
57839         rec.els = [];
57840         rec.rows = rows;
57841         rec.cells = cells;
57842         for (var i = 0; i < cells.length;i++) {
57843             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57844             
57845         }
57846         
57847         
57848     },
57849     
57850     clearEvents: function() {
57851         
57852         if (!this.eventStore.getCount()) {
57853             return;
57854         }
57855         // reset number of rows in cells.
57856         Roo.each(this.cells.elements, function(c){
57857             c.rows = 0;
57858         });
57859         
57860         this.eventStore.each(function(e) {
57861             this.clearEvent(e);
57862         },this);
57863         
57864     },
57865     
57866     clearEvent : function(ev)
57867     {
57868         if (ev.els) {
57869             Roo.each(ev.els, function(el) {
57870                 el.un('mouseenter' ,this.onEventEnter, this);
57871                 el.un('mouseleave' ,this.onEventLeave, this);
57872                 el.remove();
57873             },this);
57874             ev.els = [];
57875         }
57876     },
57877     
57878     
57879     renderEvent : function(ev,ctr) {
57880         if (!ctr) {
57881              ctr = this.view.el.select('.fc-event-container',true).first();
57882         }
57883         
57884          
57885         this.clearEvent(ev);
57886             //code
57887        
57888         
57889         
57890         ev.els = [];
57891         var cells = ev.cells;
57892         var rows = ev.rows;
57893         this.fireEvent('eventrender', this, ev);
57894         
57895         for(var i =0; i < rows.length; i++) {
57896             
57897             cls = '';
57898             if (i == 0) {
57899                 cls += ' fc-event-start';
57900             }
57901             if ((i+1) == rows.length) {
57902                 cls += ' fc-event-end';
57903             }
57904             
57905             //Roo.log(ev.data);
57906             // how many rows should it span..
57907             var cg = this.eventTmpl.append(ctr,Roo.apply({
57908                 fccls : cls
57909                 
57910             }, ev.data) , true);
57911             
57912             
57913             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57914             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57915             cg.on('click', this.onEventClick, this, ev);
57916             
57917             ev.els.push(cg);
57918             
57919             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57920             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57921             //Roo.log(cg);
57922              
57923             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57924             cg.setWidth(ebox.right - sbox.x -2);
57925         }
57926     },
57927     
57928     renderEvents: function()
57929     {   
57930         // first make sure there is enough space..
57931         
57932         if (!this.eventTmpl) {
57933             this.eventTmpl = new Roo.Template(
57934                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57935                     '<div class="fc-event-inner">' +
57936                         '<span class="fc-event-time">{time}</span>' +
57937                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57938                     '</div>' +
57939                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57940                 '</div>'
57941             );
57942                 
57943         }
57944                
57945         
57946         
57947         this.cells.each(function(c) {
57948             //Roo.log(c.select('.fc-day-content div',true).first());
57949             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57950         });
57951         
57952         var ctr = this.view.el.select('.fc-event-container',true).first();
57953         
57954         var cls;
57955         this.eventStore.each(function(ev){
57956             
57957             this.renderEvent(ev);
57958              
57959              
57960         }, this);
57961         this.view.layout();
57962         
57963     },
57964     
57965     onEventEnter: function (e, el,event,d) {
57966         this.fireEvent('evententer', this, el, event);
57967     },
57968     
57969     onEventLeave: function (e, el,event,d) {
57970         this.fireEvent('eventleave', this, el, event);
57971     },
57972     
57973     onEventClick: function (e, el,event,d) {
57974         this.fireEvent('eventclick', this, el, event);
57975     },
57976     
57977     onMonthChange: function () {
57978         this.store.load();
57979     },
57980     
57981     onLoad: function () {
57982         
57983         //Roo.log('calendar onload');
57984 //         
57985         if(this.eventStore.getCount() > 0){
57986             
57987            
57988             
57989             this.eventStore.each(function(d){
57990                 
57991                 
57992                 // FIXME..
57993                 var add =   d.data;
57994                 if (typeof(add.end_dt) == 'undefined')  {
57995                     Roo.log("Missing End time in calendar data: ");
57996                     Roo.log(d);
57997                     return;
57998                 }
57999                 if (typeof(add.start_dt) == 'undefined')  {
58000                     Roo.log("Missing Start time in calendar data: ");
58001                     Roo.log(d);
58002                     return;
58003                 }
58004                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58005                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58006                 add.id = add.id || d.id;
58007                 add.title = add.title || '??';
58008                 
58009                 this.addItem(d);
58010                 
58011              
58012             },this);
58013         }
58014         
58015         this.renderEvents();
58016     }
58017     
58018
58019 });
58020 /*
58021  grid : {
58022                 xtype: 'Grid',
58023                 xns: Roo.grid,
58024                 listeners : {
58025                     render : function ()
58026                     {
58027                         _this.grid = this;
58028                         
58029                         if (!this.view.el.hasClass('course-timesheet')) {
58030                             this.view.el.addClass('course-timesheet');
58031                         }
58032                         if (this.tsStyle) {
58033                             this.ds.load({});
58034                             return; 
58035                         }
58036                         Roo.log('width');
58037                         Roo.log(_this.grid.view.el.getWidth());
58038                         
58039                         
58040                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58041                             '.course-timesheet .x-grid-row' : {
58042                                 height: '80px'
58043                             },
58044                             '.x-grid-row td' : {
58045                                 'vertical-align' : 0
58046                             },
58047                             '.course-edit-link' : {
58048                                 'color' : 'blue',
58049                                 'text-overflow' : 'ellipsis',
58050                                 'overflow' : 'hidden',
58051                                 'white-space' : 'nowrap',
58052                                 'cursor' : 'pointer'
58053                             },
58054                             '.sub-link' : {
58055                                 'color' : 'green'
58056                             },
58057                             '.de-act-sup-link' : {
58058                                 'color' : 'purple',
58059                                 'text-decoration' : 'line-through'
58060                             },
58061                             '.de-act-link' : {
58062                                 'color' : 'red',
58063                                 'text-decoration' : 'line-through'
58064                             },
58065                             '.course-timesheet .course-highlight' : {
58066                                 'border-top-style': 'dashed !important',
58067                                 'border-bottom-bottom': 'dashed !important'
58068                             },
58069                             '.course-timesheet .course-item' : {
58070                                 'font-family'   : 'tahoma, arial, helvetica',
58071                                 'font-size'     : '11px',
58072                                 'overflow'      : 'hidden',
58073                                 'padding-left'  : '10px',
58074                                 'padding-right' : '10px',
58075                                 'padding-top' : '10px' 
58076                             }
58077                             
58078                         }, Roo.id());
58079                                 this.ds.load({});
58080                     }
58081                 },
58082                 autoWidth : true,
58083                 monitorWindowResize : false,
58084                 cellrenderer : function(v,x,r)
58085                 {
58086                     return v;
58087                 },
58088                 sm : {
58089                     xtype: 'CellSelectionModel',
58090                     xns: Roo.grid
58091                 },
58092                 dataSource : {
58093                     xtype: 'Store',
58094                     xns: Roo.data,
58095                     listeners : {
58096                         beforeload : function (_self, options)
58097                         {
58098                             options.params = options.params || {};
58099                             options.params._month = _this.monthField.getValue();
58100                             options.params.limit = 9999;
58101                             options.params['sort'] = 'when_dt';    
58102                             options.params['dir'] = 'ASC';    
58103                             this.proxy.loadResponse = this.loadResponse;
58104                             Roo.log("load?");
58105                             //this.addColumns();
58106                         },
58107                         load : function (_self, records, options)
58108                         {
58109                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58110                                 // if you click on the translation.. you can edit it...
58111                                 var el = Roo.get(this);
58112                                 var id = el.dom.getAttribute('data-id');
58113                                 var d = el.dom.getAttribute('data-date');
58114                                 var t = el.dom.getAttribute('data-time');
58115                                 //var id = this.child('span').dom.textContent;
58116                                 
58117                                 //Roo.log(this);
58118                                 Pman.Dialog.CourseCalendar.show({
58119                                     id : id,
58120                                     when_d : d,
58121                                     when_t : t,
58122                                     productitem_active : id ? 1 : 0
58123                                 }, function() {
58124                                     _this.grid.ds.load({});
58125                                 });
58126                            
58127                            });
58128                            
58129                            _this.panel.fireEvent('resize', [ '', '' ]);
58130                         }
58131                     },
58132                     loadResponse : function(o, success, response){
58133                             // this is overridden on before load..
58134                             
58135                             Roo.log("our code?");       
58136                             //Roo.log(success);
58137                             //Roo.log(response)
58138                             delete this.activeRequest;
58139                             if(!success){
58140                                 this.fireEvent("loadexception", this, o, response);
58141                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58142                                 return;
58143                             }
58144                             var result;
58145                             try {
58146                                 result = o.reader.read(response);
58147                             }catch(e){
58148                                 Roo.log("load exception?");
58149                                 this.fireEvent("loadexception", this, o, response, e);
58150                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58151                                 return;
58152                             }
58153                             Roo.log("ready...");        
58154                             // loop through result.records;
58155                             // and set this.tdate[date] = [] << array of records..
58156                             _this.tdata  = {};
58157                             Roo.each(result.records, function(r){
58158                                 //Roo.log(r.data);
58159                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58160                                     _this.tdata[r.data.when_dt.format('j')] = [];
58161                                 }
58162                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58163                             });
58164                             
58165                             //Roo.log(_this.tdata);
58166                             
58167                             result.records = [];
58168                             result.totalRecords = 6;
58169                     
58170                             // let's generate some duumy records for the rows.
58171                             //var st = _this.dateField.getValue();
58172                             
58173                             // work out monday..
58174                             //st = st.add(Date.DAY, -1 * st.format('w'));
58175                             
58176                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58177                             
58178                             var firstOfMonth = date.getFirstDayOfMonth();
58179                             var days = date.getDaysInMonth();
58180                             var d = 1;
58181                             var firstAdded = false;
58182                             for (var i = 0; i < result.totalRecords ; i++) {
58183                                 //var d= st.add(Date.DAY, i);
58184                                 var row = {};
58185                                 var added = 0;
58186                                 for(var w = 0 ; w < 7 ; w++){
58187                                     if(!firstAdded && firstOfMonth != w){
58188                                         continue;
58189                                     }
58190                                     if(d > days){
58191                                         continue;
58192                                     }
58193                                     firstAdded = true;
58194                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58195                                     row['weekday'+w] = String.format(
58196                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58197                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58198                                                     d,
58199                                                     date.format('Y-m-')+dd
58200                                                 );
58201                                     added++;
58202                                     if(typeof(_this.tdata[d]) != 'undefined'){
58203                                         Roo.each(_this.tdata[d], function(r){
58204                                             var is_sub = '';
58205                                             var deactive = '';
58206                                             var id = r.id;
58207                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58208                                             if(r.parent_id*1>0){
58209                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58210                                                 id = r.parent_id;
58211                                             }
58212                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58213                                                 deactive = 'de-act-link';
58214                                             }
58215                                             
58216                                             row['weekday'+w] += String.format(
58217                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58218                                                     id, //0
58219                                                     r.product_id_name, //1
58220                                                     r.when_dt.format('h:ia'), //2
58221                                                     is_sub, //3
58222                                                     deactive, //4
58223                                                     desc // 5
58224                                             );
58225                                         });
58226                                     }
58227                                     d++;
58228                                 }
58229                                 
58230                                 // only do this if something added..
58231                                 if(added > 0){ 
58232                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58233                                 }
58234                                 
58235                                 
58236                                 // push it twice. (second one with an hour..
58237                                 
58238                             }
58239                             //Roo.log(result);
58240                             this.fireEvent("load", this, o, o.request.arg);
58241                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58242                         },
58243                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58244                     proxy : {
58245                         xtype: 'HttpProxy',
58246                         xns: Roo.data,
58247                         method : 'GET',
58248                         url : baseURL + '/Roo/Shop_course.php'
58249                     },
58250                     reader : {
58251                         xtype: 'JsonReader',
58252                         xns: Roo.data,
58253                         id : 'id',
58254                         fields : [
58255                             {
58256                                 'name': 'id',
58257                                 'type': 'int'
58258                             },
58259                             {
58260                                 'name': 'when_dt',
58261                                 'type': 'string'
58262                             },
58263                             {
58264                                 'name': 'end_dt',
58265                                 'type': 'string'
58266                             },
58267                             {
58268                                 'name': 'parent_id',
58269                                 'type': 'int'
58270                             },
58271                             {
58272                                 'name': 'product_id',
58273                                 'type': 'int'
58274                             },
58275                             {
58276                                 'name': 'productitem_id',
58277                                 'type': 'int'
58278                             },
58279                             {
58280                                 'name': 'guid',
58281                                 'type': 'int'
58282                             }
58283                         ]
58284                     }
58285                 },
58286                 toolbar : {
58287                     xtype: 'Toolbar',
58288                     xns: Roo,
58289                     items : [
58290                         {
58291                             xtype: 'Button',
58292                             xns: Roo.Toolbar,
58293                             listeners : {
58294                                 click : function (_self, e)
58295                                 {
58296                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58297                                     sd.setMonth(sd.getMonth()-1);
58298                                     _this.monthField.setValue(sd.format('Y-m-d'));
58299                                     _this.grid.ds.load({});
58300                                 }
58301                             },
58302                             text : "Back"
58303                         },
58304                         {
58305                             xtype: 'Separator',
58306                             xns: Roo.Toolbar
58307                         },
58308                         {
58309                             xtype: 'MonthField',
58310                             xns: Roo.form,
58311                             listeners : {
58312                                 render : function (_self)
58313                                 {
58314                                     _this.monthField = _self;
58315                                    // _this.monthField.set  today
58316                                 },
58317                                 select : function (combo, date)
58318                                 {
58319                                     _this.grid.ds.load({});
58320                                 }
58321                             },
58322                             value : (function() { return new Date(); })()
58323                         },
58324                         {
58325                             xtype: 'Separator',
58326                             xns: Roo.Toolbar
58327                         },
58328                         {
58329                             xtype: 'TextItem',
58330                             xns: Roo.Toolbar,
58331                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58332                         },
58333                         {
58334                             xtype: 'Fill',
58335                             xns: Roo.Toolbar
58336                         },
58337                         {
58338                             xtype: 'Button',
58339                             xns: Roo.Toolbar,
58340                             listeners : {
58341                                 click : function (_self, e)
58342                                 {
58343                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58344                                     sd.setMonth(sd.getMonth()+1);
58345                                     _this.monthField.setValue(sd.format('Y-m-d'));
58346                                     _this.grid.ds.load({});
58347                                 }
58348                             },
58349                             text : "Next"
58350                         }
58351                     ]
58352                 },
58353                  
58354             }
58355         };
58356         
58357         *//*
58358  * Based on:
58359  * Ext JS Library 1.1.1
58360  * Copyright(c) 2006-2007, Ext JS, LLC.
58361  *
58362  * Originally Released Under LGPL - original licence link has changed is not relivant.
58363  *
58364  * Fork - LGPL
58365  * <script type="text/javascript">
58366  */
58367  
58368 /**
58369  * @class Roo.LoadMask
58370  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58371  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58372  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58373  * element's UpdateManager load indicator and will be destroyed after the initial load.
58374  * @constructor
58375  * Create a new LoadMask
58376  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58377  * @param {Object} config The config object
58378  */
58379 Roo.LoadMask = function(el, config){
58380     this.el = Roo.get(el);
58381     Roo.apply(this, config);
58382     if(this.store){
58383         this.store.on('beforeload', this.onBeforeLoad, this);
58384         this.store.on('load', this.onLoad, this);
58385         this.store.on('loadexception', this.onLoadException, this);
58386         this.removeMask = false;
58387     }else{
58388         var um = this.el.getUpdateManager();
58389         um.showLoadIndicator = false; // disable the default indicator
58390         um.on('beforeupdate', this.onBeforeLoad, this);
58391         um.on('update', this.onLoad, this);
58392         um.on('failure', this.onLoad, this);
58393         this.removeMask = true;
58394     }
58395 };
58396
58397 Roo.LoadMask.prototype = {
58398     /**
58399      * @cfg {Boolean} removeMask
58400      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58401      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58402      */
58403     /**
58404      * @cfg {String} msg
58405      * The text to display in a centered loading message box (defaults to 'Loading...')
58406      */
58407     msg : 'Loading...',
58408     /**
58409      * @cfg {String} msgCls
58410      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58411      */
58412     msgCls : 'x-mask-loading',
58413
58414     /**
58415      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58416      * @type Boolean
58417      */
58418     disabled: false,
58419
58420     /**
58421      * Disables the mask to prevent it from being displayed
58422      */
58423     disable : function(){
58424        this.disabled = true;
58425     },
58426
58427     /**
58428      * Enables the mask so that it can be displayed
58429      */
58430     enable : function(){
58431         this.disabled = false;
58432     },
58433     
58434     onLoadException : function()
58435     {
58436         Roo.log(arguments);
58437         
58438         if (typeof(arguments[3]) != 'undefined') {
58439             Roo.MessageBox.alert("Error loading",arguments[3]);
58440         } 
58441         /*
58442         try {
58443             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58444                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58445             }   
58446         } catch(e) {
58447             
58448         }
58449         */
58450     
58451         
58452         
58453         this.el.unmask(this.removeMask);
58454     },
58455     // private
58456     onLoad : function()
58457     {
58458         this.el.unmask(this.removeMask);
58459     },
58460
58461     // private
58462     onBeforeLoad : function(){
58463         if(!this.disabled){
58464             this.el.mask(this.msg, this.msgCls);
58465         }
58466     },
58467
58468     // private
58469     destroy : function(){
58470         if(this.store){
58471             this.store.un('beforeload', this.onBeforeLoad, this);
58472             this.store.un('load', this.onLoad, this);
58473             this.store.un('loadexception', this.onLoadException, this);
58474         }else{
58475             var um = this.el.getUpdateManager();
58476             um.un('beforeupdate', this.onBeforeLoad, this);
58477             um.un('update', this.onLoad, this);
58478             um.un('failure', this.onLoad, this);
58479         }
58480     }
58481 };/*
58482  * Based on:
58483  * Ext JS Library 1.1.1
58484  * Copyright(c) 2006-2007, Ext JS, LLC.
58485  *
58486  * Originally Released Under LGPL - original licence link has changed is not relivant.
58487  *
58488  * Fork - LGPL
58489  * <script type="text/javascript">
58490  */
58491
58492
58493 /**
58494  * @class Roo.XTemplate
58495  * @extends Roo.Template
58496  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58497 <pre><code>
58498 var t = new Roo.XTemplate(
58499         '&lt;select name="{name}"&gt;',
58500                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58501         '&lt;/select&gt;'
58502 );
58503  
58504 // then append, applying the master template values
58505  </code></pre>
58506  *
58507  * Supported features:
58508  *
58509  *  Tags:
58510
58511 <pre><code>
58512       {a_variable} - output encoded.
58513       {a_variable.format:("Y-m-d")} - call a method on the variable
58514       {a_variable:raw} - unencoded output
58515       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58516       {a_variable:this.method_on_template(...)} - call a method on the template object.
58517  
58518 </code></pre>
58519  *  The tpl tag:
58520 <pre><code>
58521         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58522         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58523         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58524         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58525   
58526         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58527         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58528 </code></pre>
58529  *      
58530  */
58531 Roo.XTemplate = function()
58532 {
58533     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58534     if (this.html) {
58535         this.compile();
58536     }
58537 };
58538
58539
58540 Roo.extend(Roo.XTemplate, Roo.Template, {
58541
58542     /**
58543      * The various sub templates
58544      */
58545     tpls : false,
58546     /**
58547      *
58548      * basic tag replacing syntax
58549      * WORD:WORD()
58550      *
58551      * // you can fake an object call by doing this
58552      *  x.t:(test,tesT) 
58553      * 
58554      */
58555     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58556
58557     /**
58558      * compile the template
58559      *
58560      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58561      *
58562      */
58563     compile: function()
58564     {
58565         var s = this.html;
58566      
58567         s = ['<tpl>', s, '</tpl>'].join('');
58568     
58569         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58570             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58571             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58572             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58573             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58574             m,
58575             id     = 0,
58576             tpls   = [];
58577     
58578         while(true == !!(m = s.match(re))){
58579             var forMatch   = m[0].match(nameRe),
58580                 ifMatch   = m[0].match(ifRe),
58581                 execMatch   = m[0].match(execRe),
58582                 namedMatch   = m[0].match(namedRe),
58583                 
58584                 exp  = null, 
58585                 fn   = null,
58586                 exec = null,
58587                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58588                 
58589             if (ifMatch) {
58590                 // if - puts fn into test..
58591                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58592                 if(exp){
58593                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58594                 }
58595             }
58596             
58597             if (execMatch) {
58598                 // exec - calls a function... returns empty if true is  returned.
58599                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58600                 if(exp){
58601                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58602                 }
58603             }
58604             
58605             
58606             if (name) {
58607                 // for = 
58608                 switch(name){
58609                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58610                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58611                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58612                 }
58613             }
58614             var uid = namedMatch ? namedMatch[1] : id;
58615             
58616             
58617             tpls.push({
58618                 id:     namedMatch ? namedMatch[1] : id,
58619                 target: name,
58620                 exec:   exec,
58621                 test:   fn,
58622                 body:   m[1] || ''
58623             });
58624             if (namedMatch) {
58625                 s = s.replace(m[0], '');
58626             } else { 
58627                 s = s.replace(m[0], '{xtpl'+ id + '}');
58628             }
58629             ++id;
58630         }
58631         this.tpls = [];
58632         for(var i = tpls.length-1; i >= 0; --i){
58633             this.compileTpl(tpls[i]);
58634             this.tpls[tpls[i].id] = tpls[i];
58635         }
58636         this.master = tpls[tpls.length-1];
58637         return this;
58638     },
58639     /**
58640      * same as applyTemplate, except it's done to one of the subTemplates
58641      * when using named templates, you can do:
58642      *
58643      * var str = pl.applySubTemplate('your-name', values);
58644      *
58645      * 
58646      * @param {Number} id of the template
58647      * @param {Object} values to apply to template
58648      * @param {Object} parent (normaly the instance of this object)
58649      */
58650     applySubTemplate : function(id, values, parent)
58651     {
58652         
58653         
58654         var t = this.tpls[id];
58655         
58656         
58657         try { 
58658             if(t.test && !t.test.call(this, values, parent)){
58659                 return '';
58660             }
58661         } catch(e) {
58662             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58663             Roo.log(e.toString());
58664             Roo.log(t.test);
58665             return ''
58666         }
58667         try { 
58668             
58669             if(t.exec && t.exec.call(this, values, parent)){
58670                 return '';
58671             }
58672         } catch(e) {
58673             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58674             Roo.log(e.toString());
58675             Roo.log(t.exec);
58676             return ''
58677         }
58678         try {
58679             var vs = t.target ? t.target.call(this, values, parent) : values;
58680             parent = t.target ? values : parent;
58681             if(t.target && vs instanceof Array){
58682                 var buf = [];
58683                 for(var i = 0, len = vs.length; i < len; i++){
58684                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58685                 }
58686                 return buf.join('');
58687             }
58688             return t.compiled.call(this, vs, parent);
58689         } catch (e) {
58690             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58691             Roo.log(e.toString());
58692             Roo.log(t.compiled);
58693             return '';
58694         }
58695     },
58696
58697     compileTpl : function(tpl)
58698     {
58699         var fm = Roo.util.Format;
58700         var useF = this.disableFormats !== true;
58701         var sep = Roo.isGecko ? "+" : ",";
58702         var undef = function(str) {
58703             Roo.log("Property not found :"  + str);
58704             return '';
58705         };
58706         
58707         var fn = function(m, name, format, args)
58708         {
58709             //Roo.log(arguments);
58710             args = args ? args.replace(/\\'/g,"'") : args;
58711             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58712             if (typeof(format) == 'undefined') {
58713                 format= 'htmlEncode';
58714             }
58715             if (format == 'raw' ) {
58716                 format = false;
58717             }
58718             
58719             if(name.substr(0, 4) == 'xtpl'){
58720                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58721             }
58722             
58723             // build an array of options to determine if value is undefined..
58724             
58725             // basically get 'xxxx.yyyy' then do
58726             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58727             //    (function () { Roo.log("Property not found"); return ''; })() :
58728             //    ......
58729             
58730             var udef_ar = [];
58731             var lookfor = '';
58732             Roo.each(name.split('.'), function(st) {
58733                 lookfor += (lookfor.length ? '.': '') + st;
58734                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58735             });
58736             
58737             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58738             
58739             
58740             if(format && useF){
58741                 
58742                 args = args ? ',' + args : "";
58743                  
58744                 if(format.substr(0, 5) != "this."){
58745                     format = "fm." + format + '(';
58746                 }else{
58747                     format = 'this.call("'+ format.substr(5) + '", ';
58748                     args = ", values";
58749                 }
58750                 
58751                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58752             }
58753              
58754             if (args.length) {
58755                 // called with xxyx.yuu:(test,test)
58756                 // change to ()
58757                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58758             }
58759             // raw.. - :raw modifier..
58760             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58761             
58762         };
58763         var body;
58764         // branched to use + in gecko and [].join() in others
58765         if(Roo.isGecko){
58766             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58767                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58768                     "';};};";
58769         }else{
58770             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58771             body.push(tpl.body.replace(/(\r\n|\n)/g,
58772                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58773             body.push("'].join('');};};");
58774             body = body.join('');
58775         }
58776         
58777         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58778        
58779         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58780         eval(body);
58781         
58782         return this;
58783     },
58784
58785     applyTemplate : function(values){
58786         return this.master.compiled.call(this, values, {});
58787         //var s = this.subs;
58788     },
58789
58790     apply : function(){
58791         return this.applyTemplate.apply(this, arguments);
58792     }
58793
58794  });
58795
58796 Roo.XTemplate.from = function(el){
58797     el = Roo.getDom(el);
58798     return new Roo.XTemplate(el.value || el.innerHTML);
58799 };