roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /Trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isTouch =  (function() {
67             try {  
68                 document.createEvent("TouchEvent");  
69                 return true;  
70             } catch (e) {  
71                 return false;  
72             } 
73             
74         })();
75     // remove css image flicker
76         if(isIE && !isIE7){
77         try{
78             document.execCommand("BackgroundImageCache", false, true);
79         }catch(e){}
80     }
81     
82     Roo.apply(Roo, {
83         /**
84          * True if the browser is in strict mode
85          * @type Boolean
86          */
87         isStrict : isStrict,
88         /**
89          * True if the page is running over SSL
90          * @type Boolean
91          */
92         isSecure : isSecure,
93         /**
94          * True when the document is fully initialized and ready for action
95          * @type Boolean
96          */
97         isReady : false,
98         /**
99          * Turn on debugging output (currently only the factory uses this)
100          * @type Boolean
101          */
102         
103         debug: false,
104
105         /**
106          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
107          * @type Boolean
108          */
109         enableGarbageCollector : true,
110
111         /**
112          * True to automatically purge event listeners after uncaching an element (defaults to false).
113          * Note: this only happens if enableGarbageCollector is true.
114          * @type Boolean
115          */
116         enableListenerCollection:false,
117
118         /**
119          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
120          * the IE insecure content warning (defaults to javascript:false).
121          * @type String
122          */
123         SSL_SECURE_URL : "javascript:false",
124
125         /**
126          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
127          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
128          * @type String
129          */
130         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
131
132         emptyFn : function(){},
133         
134         /**
135          * Copies all the properties of config to obj if they don't already exist.
136          * @param {Object} obj The receiver of the properties
137          * @param {Object} config The source of the properties
138          * @return {Object} returns obj
139          */
140         applyIf : function(o, c){
141             if(o && c){
142                 for(var p in c){
143                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
144                 }
145             }
146             return o;
147         },
148
149         /**
150          * Applies event listeners to elements by selectors when the document is ready.
151          * The event name is specified with an @ suffix.
152 <pre><code>
153 Roo.addBehaviors({
154    // add a listener for click on all anchors in element with id foo
155    '#foo a@click' : function(e, t){
156        // do something
157    },
158
159    // add the same listener to multiple selectors (separated by comma BEFORE the @)
160    '#foo a, #bar span.some-class@mouseover' : function(){
161        // do something
162    }
163 });
164 </code></pre>
165          * @param {Object} obj The list of behaviors to apply
166          */
167         addBehaviors : function(o){
168             if(!Roo.isReady){
169                 Roo.onReady(function(){
170                     Roo.addBehaviors(o);
171                 });
172                 return;
173             }
174             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
175             for(var b in o){
176                 var parts = b.split('@');
177                 if(parts[1]){ // for Object prototype breakers
178                     var s = parts[0];
179                     if(!cache[s]){
180                         cache[s] = Roo.select(s);
181                     }
182                     cache[s].on(parts[1], o[b]);
183                 }
184             }
185             cache = null;
186         },
187
188         /**
189          * Generates unique ids. If the element already has an id, it is unchanged
190          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
191          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
192          * @return {String} The generated Id.
193          */
194         id : function(el, prefix){
195             prefix = prefix || "roo-gen";
196             el = Roo.getDom(el);
197             var id = prefix + (++idSeed);
198             return el ? (el.id ? el.id : (el.id = id)) : id;
199         },
200          
201        
202         /**
203          * Extends one class with another class and optionally overrides members with the passed literal. This class
204          * also adds the function "override()" to the class that can be used to override
205          * members on an instance.
206          * @param {Object} subclass The class inheriting the functionality
207          * @param {Object} superclass The class being extended
208          * @param {Object} overrides (optional) A literal with members
209          * @method extend
210          */
211         extend : function(){
212             // inline overrides
213             var io = function(o){
214                 for(var m in o){
215                     this[m] = o[m];
216                 }
217             };
218             return function(sb, sp, overrides){
219                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
220                     overrides = sp;
221                     sp = sb;
222                     sb = function(){sp.apply(this, arguments);};
223                 }
224                 var F = function(){}, sbp, spp = sp.prototype;
225                 F.prototype = spp;
226                 sbp = sb.prototype = new F();
227                 sbp.constructor=sb;
228                 sb.superclass=spp;
229                 
230                 if(spp.constructor == Object.prototype.constructor){
231                     spp.constructor=sp;
232                    
233                 }
234                 
235                 sb.override = function(o){
236                     Roo.override(sb, o);
237                 };
238                 sbp.override = io;
239                 Roo.override(sb, overrides);
240                 return sb;
241             };
242         }(),
243
244         /**
245          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
246          * Usage:<pre><code>
247 Roo.override(MyClass, {
248     newMethod1: function(){
249         // etc.
250     },
251     newMethod2: function(foo){
252         // etc.
253     }
254 });
255  </code></pre>
256          * @param {Object} origclass The class to override
257          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
258          * containing one or more methods.
259          * @method override
260          */
261         override : function(origclass, overrides){
262             if(overrides){
263                 var p = origclass.prototype;
264                 for(var method in overrides){
265                     p[method] = overrides[method];
266                 }
267             }
268         },
269         /**
270          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
271          * <pre><code>
272 Roo.namespace('Company', 'Company.data');
273 Company.Widget = function() { ... }
274 Company.data.CustomStore = function(config) { ... }
275 </code></pre>
276          * @param {String} namespace1
277          * @param {String} namespace2
278          * @param {String} etc
279          * @method namespace
280          */
281         namespace : function(){
282             var a=arguments, o=null, i, j, d, rt;
283             for (i=0; i<a.length; ++i) {
284                 d=a[i].split(".");
285                 rt = d[0];
286                 /** eval:var:o */
287                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
288                 for (j=1; j<d.length; ++j) {
289                     o[d[j]]=o[d[j]] || {};
290                     o=o[d[j]];
291                 }
292             }
293         },
294         /**
295          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
296          * <pre><code>
297 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
298 Roo.factory(conf, Roo.data);
299 </code></pre>
300          * @param {String} classname
301          * @param {String} namespace (optional)
302          * @method factory
303          */
304          
305         factory : function(c, ns)
306         {
307             // no xtype, no ns or c.xns - or forced off by c.xns
308             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
309                 return c;
310             }
311             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
312             if (c.constructor == ns[c.xtype]) {// already created...
313                 return c;
314             }
315             if (ns[c.xtype]) {
316                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
317                 var ret = new ns[c.xtype](c);
318                 ret.xns = false;
319                 return ret;
320             }
321             c.xns = false; // prevent recursion..
322             return c;
323         },
324          /**
325          * Logs to console if it can.
326          *
327          * @param {String|Object} string
328          * @method log
329          */
330         log : function(s)
331         {
332             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
333                 return; // alerT?
334             }
335             console.log(s);
336             
337         },
338         /**
339          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
340          * @param {Object} o
341          * @return {String}
342          */
343         urlEncode : function(o){
344             if(!o){
345                 return "";
346             }
347             var buf = [];
348             for(var key in o){
349                 var ov = o[key], k = Roo.encodeURIComponent(key);
350                 var type = typeof ov;
351                 if(type == 'undefined'){
352                     buf.push(k, "=&");
353                 }else if(type != "function" && type != "object"){
354                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
355                 }else if(ov instanceof Array){
356                     if (ov.length) {
357                             for(var i = 0, len = ov.length; i < len; i++) {
358                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
359                             }
360                         } else {
361                             buf.push(k, "=&");
362                         }
363                 }
364             }
365             buf.pop();
366             return buf.join("");
367         },
368          /**
369          * Safe version of encodeURIComponent
370          * @param {String} data 
371          * @return {String} 
372          */
373         
374         encodeURIComponent : function (data)
375         {
376             try {
377                 return encodeURIComponent(data);
378             } catch(e) {} // should be an uri encode error.
379             
380             if (data == '' || data == null){
381                return '';
382             }
383             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
384             function nibble_to_hex(nibble){
385                 var chars = '0123456789ABCDEF';
386                 return chars.charAt(nibble);
387             }
388             data = data.toString();
389             var buffer = '';
390             for(var i=0; i<data.length; i++){
391                 var c = data.charCodeAt(i);
392                 var bs = new Array();
393                 if (c > 0x10000){
394                         // 4 bytes
395                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
396                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
397                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
398                     bs[3] = 0x80 | (c & 0x3F);
399                 }else if (c > 0x800){
400                          // 3 bytes
401                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
402                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
403                     bs[2] = 0x80 | (c & 0x3F);
404                 }else if (c > 0x80){
405                        // 2 bytes
406                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
407                     bs[1] = 0x80 | (c & 0x3F);
408                 }else{
409                         // 1 byte
410                     bs[0] = c;
411                 }
412                 for(var j=0; j<bs.length; j++){
413                     var b = bs[j];
414                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
415                             + nibble_to_hex(b &0x0F);
416                     buffer += '%'+hex;
417                }
418             }
419             return buffer;    
420              
421         },
422
423         /**
424          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
425          * @param {String} string
426          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
427          * @return {Object} A literal with members
428          */
429         urlDecode : function(string, overwrite){
430             if(!string || !string.length){
431                 return {};
432             }
433             var obj = {};
434             var pairs = string.split('&');
435             var pair, name, value;
436             for(var i = 0, len = pairs.length; i < len; i++){
437                 pair = pairs[i].split('=');
438                 name = decodeURIComponent(pair[0]);
439                 value = decodeURIComponent(pair[1]);
440                 if(overwrite !== true){
441                     if(typeof obj[name] == "undefined"){
442                         obj[name] = value;
443                     }else if(typeof obj[name] == "string"){
444                         obj[name] = [obj[name]];
445                         obj[name].push(value);
446                     }else{
447                         obj[name].push(value);
448                     }
449                 }else{
450                     obj[name] = value;
451                 }
452             }
453             return obj;
454         },
455
456         /**
457          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
458          * passed array is not really an array, your function is called once with it.
459          * The supplied function is called with (Object item, Number index, Array allItems).
460          * @param {Array/NodeList/Mixed} array
461          * @param {Function} fn
462          * @param {Object} scope
463          */
464         each : function(array, fn, scope){
465             if(typeof array.length == "undefined" || typeof array == "string"){
466                 array = [array];
467             }
468             for(var i = 0, len = array.length; i < len; i++){
469                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
470             }
471         },
472
473         // deprecated
474         combine : function(){
475             var as = arguments, l = as.length, r = [];
476             for(var i = 0; i < l; i++){
477                 var a = as[i];
478                 if(a instanceof Array){
479                     r = r.concat(a);
480                 }else if(a.length !== undefined && !a.substr){
481                     r = r.concat(Array.prototype.slice.call(a, 0));
482                 }else{
483                     r.push(a);
484                 }
485             }
486             return r;
487         },
488
489         /**
490          * Escapes the passed string for use in a regular expression
491          * @param {String} str
492          * @return {String}
493          */
494         escapeRe : function(s) {
495             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
496         },
497
498         // internal
499         callback : function(cb, scope, args, delay){
500             if(typeof cb == "function"){
501                 if(delay){
502                     cb.defer(delay, scope, args || []);
503                 }else{
504                     cb.apply(scope, args || []);
505                 }
506             }
507         },
508
509         /**
510          * Return the dom node for the passed string (id), dom node, or Roo.Element
511          * @param {String/HTMLElement/Roo.Element} el
512          * @return HTMLElement
513          */
514         getDom : function(el){
515             if(!el){
516                 return null;
517             }
518             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
519         },
520
521         /**
522         * Shorthand for {@link Roo.ComponentMgr#get}
523         * @param {String} id
524         * @return Roo.Component
525         */
526         getCmp : function(id){
527             return Roo.ComponentMgr.get(id);
528         },
529          
530         num : function(v, defaultValue){
531             if(typeof v != 'number'){
532                 return defaultValue;
533             }
534             return v;
535         },
536
537         destroy : function(){
538             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
539                 var as = a[i];
540                 if(as){
541                     if(as.dom){
542                         as.removeAllListeners();
543                         as.remove();
544                         continue;
545                     }
546                     if(typeof as.purgeListeners == 'function'){
547                         as.purgeListeners();
548                     }
549                     if(typeof as.destroy == 'function'){
550                         as.destroy();
551                     }
552                 }
553             }
554         },
555
556         // inpired by a similar function in mootools library
557         /**
558          * Returns the type of object that is passed in. If the object passed in is null or undefined it
559          * return false otherwise it returns one of the following values:<ul>
560          * <li><b>string</b>: If the object passed is a string</li>
561          * <li><b>number</b>: If the object passed is a number</li>
562          * <li><b>boolean</b>: If the object passed is a boolean value</li>
563          * <li><b>function</b>: If the object passed is a function reference</li>
564          * <li><b>object</b>: If the object passed is an object</li>
565          * <li><b>array</b>: If the object passed is an array</li>
566          * <li><b>regexp</b>: If the object passed is a regular expression</li>
567          * <li><b>element</b>: If the object passed is a DOM Element</li>
568          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
569          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
570          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
571          * @param {Mixed} object
572          * @return {String}
573          */
574         type : function(o){
575             if(o === undefined || o === null){
576                 return false;
577             }
578             if(o.htmlElement){
579                 return 'element';
580             }
581             var t = typeof o;
582             if(t == 'object' && o.nodeName) {
583                 switch(o.nodeType) {
584                     case 1: return 'element';
585                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
586                 }
587             }
588             if(t == 'object' || t == 'function') {
589                 switch(o.constructor) {
590                     case Array: return 'array';
591                     case RegExp: return 'regexp';
592                 }
593                 if(typeof o.length == 'number' && typeof o.item == 'function') {
594                     return 'nodelist';
595                 }
596             }
597             return t;
598         },
599
600         /**
601          * Returns true if the passed value is null, undefined or an empty string (optional).
602          * @param {Mixed} value The value to test
603          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
604          * @return {Boolean}
605          */
606         isEmpty : function(v, allowBlank){
607             return v === null || v === undefined || (!allowBlank ? v === '' : false);
608         },
609         
610         /** @type Boolean */
611         isOpera : isOpera,
612         /** @type Boolean */
613         isSafari : isSafari,
614         /** @type Boolean */
615         isFirefox : isFirefox,
616         /** @type Boolean */
617         isIE : isIE,
618         /** @type Boolean */
619         isIE7 : isIE7,
620         /** @type Boolean */
621         isIE11 : isIE11,
622         /** @type Boolean */
623         isGecko : isGecko,
624         /** @type Boolean */
625         isBorderBox : isBorderBox,
626         /** @type Boolean */
627         isWindows : isWindows,
628         /** @type Boolean */
629         isLinux : isLinux,
630         /** @type Boolean */
631         isMac : isMac,
632         /** @type Boolean */
633         isTouch : isTouch,
634
635         /**
636          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
637          * you may want to set this to true.
638          * @type Boolean
639          */
640         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
641         
642         
643                 
644         /**
645          * Selects a single element as a Roo Element
646          * This is about as close as you can get to jQuery's $('do crazy stuff')
647          * @param {String} selector The selector/xpath query
648          * @param {Node} root (optional) The start of the query (defaults to document).
649          * @return {Roo.Element}
650          */
651         selectNode : function(selector, root) 
652         {
653             var node = Roo.DomQuery.selectNode(selector,root);
654             return node ? Roo.get(node) : new Roo.Element(false);
655         }
656         
657     });
658
659
660 })();
661
662 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
663                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
664                 "Roo.app", "Roo.ux",
665                 "Roo.bootstrap",
666                 "Roo.bootstrap.dash");
667 /*
668  * Based on:
669  * Ext JS Library 1.1.1
670  * Copyright(c) 2006-2007, Ext JS, LLC.
671  *
672  * Originally Released Under LGPL - original licence link has changed is not relivant.
673  *
674  * Fork - LGPL
675  * <script type="text/javascript">
676  */
677
678 (function() {    
679     // wrappedn so fnCleanup is not in global scope...
680     if(Roo.isIE) {
681         function fnCleanUp() {
682             var p = Function.prototype;
683             delete p.createSequence;
684             delete p.defer;
685             delete p.createDelegate;
686             delete p.createCallback;
687             delete p.createInterceptor;
688
689             window.detachEvent("onunload", fnCleanUp);
690         }
691         window.attachEvent("onunload", fnCleanUp);
692     }
693 })();
694
695
696 /**
697  * @class Function
698  * These functions are available on every Function object (any JavaScript function).
699  */
700 Roo.apply(Function.prototype, {
701      /**
702      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
703      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
704      * Will create a function that is bound to those 2 args.
705      * @return {Function} The new function
706     */
707     createCallback : function(/*args...*/){
708         // make args available, in function below
709         var args = arguments;
710         var method = this;
711         return function() {
712             return method.apply(window, args);
713         };
714     },
715
716     /**
717      * Creates a delegate (callback) that sets the scope to obj.
718      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
719      * Will create a function that is automatically scoped to this.
720      * @param {Object} obj (optional) The object for which the scope is set
721      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
722      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
723      *                                             if a number the args are inserted at the specified position
724      * @return {Function} The new function
725      */
726     createDelegate : function(obj, args, appendArgs){
727         var method = this;
728         return function() {
729             var callArgs = args || arguments;
730             if(appendArgs === true){
731                 callArgs = Array.prototype.slice.call(arguments, 0);
732                 callArgs = callArgs.concat(args);
733             }else if(typeof appendArgs == "number"){
734                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
735                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
736                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
737             }
738             return method.apply(obj || window, callArgs);
739         };
740     },
741
742     /**
743      * Calls this function after the number of millseconds specified.
744      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
745      * @param {Object} obj (optional) The object for which the scope is set
746      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
747      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
748      *                                             if a number the args are inserted at the specified position
749      * @return {Number} The timeout id that can be used with clearTimeout
750      */
751     defer : function(millis, obj, args, appendArgs){
752         var fn = this.createDelegate(obj, args, appendArgs);
753         if(millis){
754             return setTimeout(fn, millis);
755         }
756         fn();
757         return 0;
758     },
759     /**
760      * Create a combined function call sequence of the original function + the passed function.
761      * The resulting function returns the results of the original function.
762      * The passed fcn is called with the parameters of the original function
763      * @param {Function} fcn The function to sequence
764      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
765      * @return {Function} The new function
766      */
767     createSequence : function(fcn, scope){
768         if(typeof fcn != "function"){
769             return this;
770         }
771         var method = this;
772         return function() {
773             var retval = method.apply(this || window, arguments);
774             fcn.apply(scope || this || window, arguments);
775             return retval;
776         };
777     },
778
779     /**
780      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
781      * The resulting function returns the results of the original function.
782      * The passed fcn is called with the parameters of the original function.
783      * @addon
784      * @param {Function} fcn The function to call before the original
785      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
786      * @return {Function} The new function
787      */
788     createInterceptor : function(fcn, scope){
789         if(typeof fcn != "function"){
790             return this;
791         }
792         var method = this;
793         return function() {
794             fcn.target = this;
795             fcn.method = method;
796             if(fcn.apply(scope || this || window, arguments) === false){
797                 return;
798             }
799             return method.apply(this || window, arguments);
800         };
801     }
802 });
803 /*
804  * Based on:
805  * Ext JS Library 1.1.1
806  * Copyright(c) 2006-2007, Ext JS, LLC.
807  *
808  * Originally Released Under LGPL - original licence link has changed is not relivant.
809  *
810  * Fork - LGPL
811  * <script type="text/javascript">
812  */
813
814 Roo.applyIf(String, {
815     
816     /** @scope String */
817     
818     /**
819      * Escapes the passed string for ' and \
820      * @param {String} string The string to escape
821      * @return {String} The escaped string
822      * @static
823      */
824     escape : function(string) {
825         return string.replace(/('|\\)/g, "\\$1");
826     },
827
828     /**
829      * Pads the left side of a string with a specified character.  This is especially useful
830      * for normalizing number and date strings.  Example usage:
831      * <pre><code>
832 var s = String.leftPad('123', 5, '0');
833 // s now contains the string: '00123'
834 </code></pre>
835      * @param {String} string The original string
836      * @param {Number} size The total length of the output string
837      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
838      * @return {String} The padded string
839      * @static
840      */
841     leftPad : function (val, size, ch) {
842         var result = new String(val);
843         if(ch === null || ch === undefined || ch === '') {
844             ch = " ";
845         }
846         while (result.length < size) {
847             result = ch + result;
848         }
849         return result;
850     },
851
852     /**
853      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
854      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
855      * <pre><code>
856 var cls = 'my-class', text = 'Some text';
857 var s = String.format('<div class="{0}">{1}</div>', cls, text);
858 // s now contains the string: '<div class="my-class">Some text</div>'
859 </code></pre>
860      * @param {String} string The tokenized string to be formatted
861      * @param {String} value1 The value to replace token {0}
862      * @param {String} value2 Etc...
863      * @return {String} The formatted string
864      * @static
865      */
866     format : function(format){
867         var args = Array.prototype.slice.call(arguments, 1);
868         return format.replace(/\{(\d+)\}/g, function(m, i){
869             return Roo.util.Format.htmlEncode(args[i]);
870         });
871     }
872 });
873
874 /**
875  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
876  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
877  * they are already different, the first value passed in is returned.  Note that this method returns the new value
878  * but does not change the current string.
879  * <pre><code>
880 // alternate sort directions
881 sort = sort.toggle('ASC', 'DESC');
882
883 // instead of conditional logic:
884 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
885 </code></pre>
886  * @param {String} value The value to compare to the current string
887  * @param {String} other The new value to use if the string already equals the first value passed in
888  * @return {String} The new value
889  */
890  
891 String.prototype.toggle = function(value, other){
892     return this == value ? other : value;
893 };/*
894  * Based on:
895  * Ext JS Library 1.1.1
896  * Copyright(c) 2006-2007, Ext JS, LLC.
897  *
898  * Originally Released Under LGPL - original licence link has changed is not relivant.
899  *
900  * Fork - LGPL
901  * <script type="text/javascript">
902  */
903
904  /**
905  * @class Number
906  */
907 Roo.applyIf(Number.prototype, {
908     /**
909      * Checks whether or not the current number is within a desired range.  If the number is already within the
910      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
911      * exceeded.  Note that this method returns the constrained value but does not change the current number.
912      * @param {Number} min The minimum number in the range
913      * @param {Number} max The maximum number in the range
914      * @return {Number} The constrained value if outside the range, otherwise the current value
915      */
916     constrain : function(min, max){
917         return Math.min(Math.max(this, min), max);
918     }
919 });/*
920  * Based on:
921  * Ext JS Library 1.1.1
922  * Copyright(c) 2006-2007, Ext JS, LLC.
923  *
924  * Originally Released Under LGPL - original licence link has changed is not relivant.
925  *
926  * Fork - LGPL
927  * <script type="text/javascript">
928  */
929  /**
930  * @class Array
931  */
932 Roo.applyIf(Array.prototype, {
933     /**
934      * 
935      * Checks whether or not the specified object exists in the array.
936      * @param {Object} o The object to check for
937      * @return {Number} The index of o in the array (or -1 if it is not found)
938      */
939     indexOf : function(o){
940        for (var i = 0, len = this.length; i < len; i++){
941               if(this[i] == o) return i;
942        }
943            return -1;
944     },
945
946     /**
947      * Removes the specified object from the array.  If the object is not found nothing happens.
948      * @param {Object} o The object to remove
949      */
950     remove : function(o){
951        var index = this.indexOf(o);
952        if(index != -1){
953            this.splice(index, 1);
954        }
955     },
956     /**
957      * Map (JS 1.6 compatibility)
958      * @param {Function} function  to call
959      */
960     map : function(fun )
961     {
962         var len = this.length >>> 0;
963         if (typeof fun != "function")
964             throw new TypeError();
965
966         var res = new Array(len);
967         var thisp = arguments[1];
968         for (var i = 0; i < len; i++)
969         {
970             if (i in this)
971                 res[i] = fun.call(thisp, this[i], i, this);
972         }
973
974         return res;
975     }
976     
977 });
978
979
980  /*
981  * Based on:
982  * Ext JS Library 1.1.1
983  * Copyright(c) 2006-2007, Ext JS, LLC.
984  *
985  * Originally Released Under LGPL - original licence link has changed is not relivant.
986  *
987  * Fork - LGPL
988  * <script type="text/javascript">
989  */
990
991 /**
992  * @class Date
993  *
994  * The date parsing and format syntax is a subset of
995  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
996  * supported will provide results equivalent to their PHP versions.
997  *
998  * Following is the list of all currently supported formats:
999  *<pre>
1000 Sample date:
1001 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1002
1003 Format  Output      Description
1004 ------  ----------  --------------------------------------------------------------
1005   d      10         Day of the month, 2 digits with leading zeros
1006   D      Wed        A textual representation of a day, three letters
1007   j      10         Day of the month without leading zeros
1008   l      Wednesday  A full textual representation of the day of the week
1009   S      th         English ordinal day of month suffix, 2 chars (use with j)
1010   w      3          Numeric representation of the day of the week
1011   z      9          The julian date, or day of the year (0-365)
1012   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1013   F      January    A full textual representation of the month
1014   m      01         Numeric representation of a month, with leading zeros
1015   M      Jan        Month name abbreviation, three letters
1016   n      1          Numeric representation of a month, without leading zeros
1017   t      31         Number of days in the given month
1018   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1019   Y      2007       A full numeric representation of a year, 4 digits
1020   y      07         A two digit representation of a year
1021   a      pm         Lowercase Ante meridiem and Post meridiem
1022   A      PM         Uppercase Ante meridiem and Post meridiem
1023   g      3          12-hour format of an hour without leading zeros
1024   G      15         24-hour format of an hour without leading zeros
1025   h      03         12-hour format of an hour with leading zeros
1026   H      15         24-hour format of an hour with leading zeros
1027   i      05         Minutes with leading zeros
1028   s      01         Seconds, with leading zeros
1029   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1030   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1031   T      CST        Timezone setting of the machine running the code
1032   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1033 </pre>
1034  *
1035  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1036  * <pre><code>
1037 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1038 document.write(dt.format('Y-m-d'));                         //2007-01-10
1039 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1040 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1041  </code></pre>
1042  *
1043  * Here are some standard date/time patterns that you might find helpful.  They
1044  * are not part of the source of Date.js, but to use them you can simply copy this
1045  * block of code into any script that is included after Date.js and they will also become
1046  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1047  * <pre><code>
1048 Date.patterns = {
1049     ISO8601Long:"Y-m-d H:i:s",
1050     ISO8601Short:"Y-m-d",
1051     ShortDate: "n/j/Y",
1052     LongDate: "l, F d, Y",
1053     FullDateTime: "l, F d, Y g:i:s A",
1054     MonthDay: "F d",
1055     ShortTime: "g:i A",
1056     LongTime: "g:i:s A",
1057     SortableDateTime: "Y-m-d\\TH:i:s",
1058     UniversalSortableDateTime: "Y-m-d H:i:sO",
1059     YearMonth: "F, Y"
1060 };
1061 </code></pre>
1062  *
1063  * Example usage:
1064  * <pre><code>
1065 var dt = new Date();
1066 document.write(dt.format(Date.patterns.ShortDate));
1067  </code></pre>
1068  */
1069
1070 /*
1071  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1072  * They generate precompiled functions from date formats instead of parsing and
1073  * processing the pattern every time you format a date.  These functions are available
1074  * on every Date object (any javascript function).
1075  *
1076  * The original article and download are here:
1077  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1078  *
1079  */
1080  
1081  
1082  // was in core
1083 /**
1084  Returns the number of milliseconds between this date and date
1085  @param {Date} date (optional) Defaults to now
1086  @return {Number} The diff in milliseconds
1087  @member Date getElapsed
1088  */
1089 Date.prototype.getElapsed = function(date) {
1090         return Math.abs((date || new Date()).getTime()-this.getTime());
1091 };
1092 // was in date file..
1093
1094
1095 // private
1096 Date.parseFunctions = {count:0};
1097 // private
1098 Date.parseRegexes = [];
1099 // private
1100 Date.formatFunctions = {count:0};
1101
1102 // private
1103 Date.prototype.dateFormat = function(format) {
1104     if (Date.formatFunctions[format] == null) {
1105         Date.createNewFormat(format);
1106     }
1107     var func = Date.formatFunctions[format];
1108     return this[func]();
1109 };
1110
1111
1112 /**
1113  * Formats a date given the supplied format string
1114  * @param {String} format The format string
1115  * @return {String} The formatted date
1116  * @method
1117  */
1118 Date.prototype.format = Date.prototype.dateFormat;
1119
1120 // private
1121 Date.createNewFormat = function(format) {
1122     var funcName = "format" + Date.formatFunctions.count++;
1123     Date.formatFunctions[format] = funcName;
1124     var code = "Date.prototype." + funcName + " = function(){return ";
1125     var special = false;
1126     var ch = '';
1127     for (var i = 0; i < format.length; ++i) {
1128         ch = format.charAt(i);
1129         if (!special && ch == "\\") {
1130             special = true;
1131         }
1132         else if (special) {
1133             special = false;
1134             code += "'" + String.escape(ch) + "' + ";
1135         }
1136         else {
1137             code += Date.getFormatCode(ch);
1138         }
1139     }
1140     /** eval:var:zzzzzzzzzzzzz */
1141     eval(code.substring(0, code.length - 3) + ";}");
1142 };
1143
1144 // private
1145 Date.getFormatCode = function(character) {
1146     switch (character) {
1147     case "d":
1148         return "String.leftPad(this.getDate(), 2, '0') + ";
1149     case "D":
1150         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1151     case "j":
1152         return "this.getDate() + ";
1153     case "l":
1154         return "Date.dayNames[this.getDay()] + ";
1155     case "S":
1156         return "this.getSuffix() + ";
1157     case "w":
1158         return "this.getDay() + ";
1159     case "z":
1160         return "this.getDayOfYear() + ";
1161     case "W":
1162         return "this.getWeekOfYear() + ";
1163     case "F":
1164         return "Date.monthNames[this.getMonth()] + ";
1165     case "m":
1166         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1167     case "M":
1168         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1169     case "n":
1170         return "(this.getMonth() + 1) + ";
1171     case "t":
1172         return "this.getDaysInMonth() + ";
1173     case "L":
1174         return "(this.isLeapYear() ? 1 : 0) + ";
1175     case "Y":
1176         return "this.getFullYear() + ";
1177     case "y":
1178         return "('' + this.getFullYear()).substring(2, 4) + ";
1179     case "a":
1180         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1181     case "A":
1182         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1183     case "g":
1184         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1185     case "G":
1186         return "this.getHours() + ";
1187     case "h":
1188         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1189     case "H":
1190         return "String.leftPad(this.getHours(), 2, '0') + ";
1191     case "i":
1192         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1193     case "s":
1194         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1195     case "O":
1196         return "this.getGMTOffset() + ";
1197     case "P":
1198         return "this.getGMTColonOffset() + ";
1199     case "T":
1200         return "this.getTimezone() + ";
1201     case "Z":
1202         return "(this.getTimezoneOffset() * -60) + ";
1203     default:
1204         return "'" + String.escape(character) + "' + ";
1205     }
1206 };
1207
1208 /**
1209  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1210  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1211  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1212  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1213  * string or the parse operation will fail.
1214  * Example Usage:
1215 <pre><code>
1216 //dt = Fri May 25 2007 (current date)
1217 var dt = new Date();
1218
1219 //dt = Thu May 25 2006 (today's month/day in 2006)
1220 dt = Date.parseDate("2006", "Y");
1221
1222 //dt = Sun Jan 15 2006 (all date parts specified)
1223 dt = Date.parseDate("2006-1-15", "Y-m-d");
1224
1225 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1226 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1227 </code></pre>
1228  * @param {String} input The unparsed date as a string
1229  * @param {String} format The format the date is in
1230  * @return {Date} The parsed date
1231  * @static
1232  */
1233 Date.parseDate = function(input, format) {
1234     if (Date.parseFunctions[format] == null) {
1235         Date.createParser(format);
1236     }
1237     var func = Date.parseFunctions[format];
1238     return Date[func](input);
1239 };
1240 /**
1241  * @private
1242  */
1243
1244 Date.createParser = function(format) {
1245     var funcName = "parse" + Date.parseFunctions.count++;
1246     var regexNum = Date.parseRegexes.length;
1247     var currentGroup = 1;
1248     Date.parseFunctions[format] = funcName;
1249
1250     var code = "Date." + funcName + " = function(input){\n"
1251         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1252         + "var d = new Date();\n"
1253         + "y = d.getFullYear();\n"
1254         + "m = d.getMonth();\n"
1255         + "d = d.getDate();\n"
1256         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1257         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1258         + "if (results && results.length > 0) {";
1259     var regex = "";
1260
1261     var special = false;
1262     var ch = '';
1263     for (var i = 0; i < format.length; ++i) {
1264         ch = format.charAt(i);
1265         if (!special && ch == "\\") {
1266             special = true;
1267         }
1268         else if (special) {
1269             special = false;
1270             regex += String.escape(ch);
1271         }
1272         else {
1273             var obj = Date.formatCodeToRegex(ch, currentGroup);
1274             currentGroup += obj.g;
1275             regex += obj.s;
1276             if (obj.g && obj.c) {
1277                 code += obj.c;
1278             }
1279         }
1280     }
1281
1282     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1283         + "{v = new Date(y, m, d, h, i, s);}\n"
1284         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1285         + "{v = new Date(y, m, d, h, i);}\n"
1286         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1287         + "{v = new Date(y, m, d, h);}\n"
1288         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1289         + "{v = new Date(y, m, d);}\n"
1290         + "else if (y >= 0 && m >= 0)\n"
1291         + "{v = new Date(y, m);}\n"
1292         + "else if (y >= 0)\n"
1293         + "{v = new Date(y);}\n"
1294         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1295         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1296         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1297         + ";}";
1298
1299     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1300     /** eval:var:zzzzzzzzzzzzz */
1301     eval(code);
1302 };
1303
1304 // private
1305 Date.formatCodeToRegex = function(character, currentGroup) {
1306     switch (character) {
1307     case "D":
1308         return {g:0,
1309         c:null,
1310         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1311     case "j":
1312         return {g:1,
1313             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1314             s:"(\\d{1,2})"}; // day of month without leading zeroes
1315     case "d":
1316         return {g:1,
1317             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1318             s:"(\\d{2})"}; // day of month with leading zeroes
1319     case "l":
1320         return {g:0,
1321             c:null,
1322             s:"(?:" + Date.dayNames.join("|") + ")"};
1323     case "S":
1324         return {g:0,
1325             c:null,
1326             s:"(?:st|nd|rd|th)"};
1327     case "w":
1328         return {g:0,
1329             c:null,
1330             s:"\\d"};
1331     case "z":
1332         return {g:0,
1333             c:null,
1334             s:"(?:\\d{1,3})"};
1335     case "W":
1336         return {g:0,
1337             c:null,
1338             s:"(?:\\d{2})"};
1339     case "F":
1340         return {g:1,
1341             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1342             s:"(" + Date.monthNames.join("|") + ")"};
1343     case "M":
1344         return {g:1,
1345             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1346             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1347     case "n":
1348         return {g:1,
1349             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1350             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1351     case "m":
1352         return {g:1,
1353             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1354             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1355     case "t":
1356         return {g:0,
1357             c:null,
1358             s:"\\d{1,2}"};
1359     case "L":
1360         return {g:0,
1361             c:null,
1362             s:"(?:1|0)"};
1363     case "Y":
1364         return {g:1,
1365             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{4})"};
1367     case "y":
1368         return {g:1,
1369             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1370                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1371             s:"(\\d{1,2})"};
1372     case "a":
1373         return {g:1,
1374             c:"if (results[" + currentGroup + "] == 'am') {\n"
1375                 + "if (h == 12) { h = 0; }\n"
1376                 + "} else { if (h < 12) { h += 12; }}",
1377             s:"(am|pm)"};
1378     case "A":
1379         return {g:1,
1380             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1381                 + "if (h == 12) { h = 0; }\n"
1382                 + "} else { if (h < 12) { h += 12; }}",
1383             s:"(AM|PM)"};
1384     case "g":
1385     case "G":
1386         return {g:1,
1387             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1389     case "h":
1390     case "H":
1391         return {g:1,
1392             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1394     case "i":
1395         return {g:1,
1396             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1397             s:"(\\d{2})"};
1398     case "s":
1399         return {g:1,
1400             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1401             s:"(\\d{2})"};
1402     case "O":
1403         return {g:1,
1404             c:[
1405                 "o = results[", currentGroup, "];\n",
1406                 "var sn = o.substring(0,1);\n", // get + / - sign
1407                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1408                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1409                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1410                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1411             ].join(""),
1412             s:"([+\-]\\d{2,4})"};
1413     
1414     
1415     case "P":
1416         return {g:1,
1417                 c:[
1418                    "o = results[", currentGroup, "];\n",
1419                    "var sn = o.substring(0,1);\n",
1420                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1421                    "var mn = o.substring(4,6) % 60;\n",
1422                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1423                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1424             ].join(""),
1425             s:"([+\-]\\d{4})"};
1426     case "T":
1427         return {g:0,
1428             c:null,
1429             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1430     case "Z":
1431         return {g:1,
1432             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1433                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1434             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1435     default:
1436         return {g:0,
1437             c:null,
1438             s:String.escape(character)};
1439     }
1440 };
1441
1442 /**
1443  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1444  * @return {String} The abbreviated timezone name (e.g. 'CST')
1445  */
1446 Date.prototype.getTimezone = function() {
1447     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1448 };
1449
1450 /**
1451  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1452  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1453  */
1454 Date.prototype.getGMTOffset = function() {
1455     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1456         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1457         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1458 };
1459
1460 /**
1461  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1462  * @return {String} 2-characters representing hours and 2-characters representing minutes
1463  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1464  */
1465 Date.prototype.getGMTColonOffset = function() {
1466         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1467                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1468                 + ":"
1469                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1470 }
1471
1472 /**
1473  * Get the numeric day number of the year, adjusted for leap year.
1474  * @return {Number} 0 through 364 (365 in leap years)
1475  */
1476 Date.prototype.getDayOfYear = function() {
1477     var num = 0;
1478     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1479     for (var i = 0; i < this.getMonth(); ++i) {
1480         num += Date.daysInMonth[i];
1481     }
1482     return num + this.getDate() - 1;
1483 };
1484
1485 /**
1486  * Get the string representation of the numeric week number of the year
1487  * (equivalent to the format specifier 'W').
1488  * @return {String} '00' through '52'
1489  */
1490 Date.prototype.getWeekOfYear = function() {
1491     // Skip to Thursday of this week
1492     var now = this.getDayOfYear() + (4 - this.getDay());
1493     // Find the first Thursday of the year
1494     var jan1 = new Date(this.getFullYear(), 0, 1);
1495     var then = (7 - jan1.getDay() + 4);
1496     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1497 };
1498
1499 /**
1500  * Whether or not the current date is in a leap year.
1501  * @return {Boolean} True if the current date is in a leap year, else false
1502  */
1503 Date.prototype.isLeapYear = function() {
1504     var year = this.getFullYear();
1505     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1506 };
1507
1508 /**
1509  * Get the first day of the current month, adjusted for leap year.  The returned value
1510  * is the numeric day index within the week (0-6) which can be used in conjunction with
1511  * the {@link #monthNames} array to retrieve the textual day name.
1512  * Example:
1513  *<pre><code>
1514 var dt = new Date('1/10/2007');
1515 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1516 </code></pre>
1517  * @return {Number} The day number (0-6)
1518  */
1519 Date.prototype.getFirstDayOfMonth = function() {
1520     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1521     return (day < 0) ? (day + 7) : day;
1522 };
1523
1524 /**
1525  * Get the last day of the current month, adjusted for leap year.  The returned value
1526  * is the numeric day index within the week (0-6) which can be used in conjunction with
1527  * the {@link #monthNames} array to retrieve the textual day name.
1528  * Example:
1529  *<pre><code>
1530 var dt = new Date('1/10/2007');
1531 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1532 </code></pre>
1533  * @return {Number} The day number (0-6)
1534  */
1535 Date.prototype.getLastDayOfMonth = function() {
1536     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1537     return (day < 0) ? (day + 7) : day;
1538 };
1539
1540
1541 /**
1542  * Get the first date of this date's month
1543  * @return {Date}
1544  */
1545 Date.prototype.getFirstDateOfMonth = function() {
1546     return new Date(this.getFullYear(), this.getMonth(), 1);
1547 };
1548
1549 /**
1550  * Get the last date of this date's month
1551  * @return {Date}
1552  */
1553 Date.prototype.getLastDateOfMonth = function() {
1554     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1555 };
1556 /**
1557  * Get the number of days in the current month, adjusted for leap year.
1558  * @return {Number} The number of days in the month
1559  */
1560 Date.prototype.getDaysInMonth = function() {
1561     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1562     return Date.daysInMonth[this.getMonth()];
1563 };
1564
1565 /**
1566  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1567  * @return {String} 'st, 'nd', 'rd' or 'th'
1568  */
1569 Date.prototype.getSuffix = function() {
1570     switch (this.getDate()) {
1571         case 1:
1572         case 21:
1573         case 31:
1574             return "st";
1575         case 2:
1576         case 22:
1577             return "nd";
1578         case 3:
1579         case 23:
1580             return "rd";
1581         default:
1582             return "th";
1583     }
1584 };
1585
1586 // private
1587 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1588
1589 /**
1590  * An array of textual month names.
1591  * Override these values for international dates, for example...
1592  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1593  * @type Array
1594  * @static
1595  */
1596 Date.monthNames =
1597    ["January",
1598     "February",
1599     "March",
1600     "April",
1601     "May",
1602     "June",
1603     "July",
1604     "August",
1605     "September",
1606     "October",
1607     "November",
1608     "December"];
1609
1610 /**
1611  * An array of textual day names.
1612  * Override these values for international dates, for example...
1613  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1614  * @type Array
1615  * @static
1616  */
1617 Date.dayNames =
1618    ["Sunday",
1619     "Monday",
1620     "Tuesday",
1621     "Wednesday",
1622     "Thursday",
1623     "Friday",
1624     "Saturday"];
1625
1626 // private
1627 Date.y2kYear = 50;
1628 // private
1629 Date.monthNumbers = {
1630     Jan:0,
1631     Feb:1,
1632     Mar:2,
1633     Apr:3,
1634     May:4,
1635     Jun:5,
1636     Jul:6,
1637     Aug:7,
1638     Sep:8,
1639     Oct:9,
1640     Nov:10,
1641     Dec:11};
1642
1643 /**
1644  * Creates and returns a new Date instance with the exact same date value as the called instance.
1645  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1646  * variable will also be changed.  When the intention is to create a new variable that will not
1647  * modify the original instance, you should create a clone.
1648  *
1649  * Example of correctly cloning a date:
1650  * <pre><code>
1651 //wrong way:
1652 var orig = new Date('10/1/2006');
1653 var copy = orig;
1654 copy.setDate(5);
1655 document.write(orig);  //returns 'Thu Oct 05 2006'!
1656
1657 //correct way:
1658 var orig = new Date('10/1/2006');
1659 var copy = orig.clone();
1660 copy.setDate(5);
1661 document.write(orig);  //returns 'Thu Oct 01 2006'
1662 </code></pre>
1663  * @return {Date} The new Date instance
1664  */
1665 Date.prototype.clone = function() {
1666         return new Date(this.getTime());
1667 };
1668
1669 /**
1670  * Clears any time information from this date
1671  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1672  @return {Date} this or the clone
1673  */
1674 Date.prototype.clearTime = function(clone){
1675     if(clone){
1676         return this.clone().clearTime();
1677     }
1678     this.setHours(0);
1679     this.setMinutes(0);
1680     this.setSeconds(0);
1681     this.setMilliseconds(0);
1682     return this;
1683 };
1684
1685 // private
1686 // safari setMonth is broken
1687 if(Roo.isSafari){
1688     Date.brokenSetMonth = Date.prototype.setMonth;
1689         Date.prototype.setMonth = function(num){
1690                 if(num <= -1){
1691                         var n = Math.ceil(-num);
1692                         var back_year = Math.ceil(n/12);
1693                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1694                         this.setFullYear(this.getFullYear() - back_year);
1695                         return Date.brokenSetMonth.call(this, month);
1696                 } else {
1697                         return Date.brokenSetMonth.apply(this, arguments);
1698                 }
1699         };
1700 }
1701
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MILLI = "ms";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.SECOND = "s";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.MINUTE = "mi";
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.HOUR = "h";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.DAY = "d";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MONTH = "mo";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.YEAR = "y";
1730
1731 /**
1732  * Provides a convenient method of performing basic date arithmetic.  This method
1733  * does not modify the Date instance being called - it creates and returns
1734  * a new Date instance containing the resulting date value.
1735  *
1736  * Examples:
1737  * <pre><code>
1738 //Basic usage:
1739 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1740 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1741
1742 //Negative values will subtract correctly:
1743 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1744 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1745
1746 //You can even chain several calls together in one line!
1747 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1748 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1749  </code></pre>
1750  *
1751  * @param {String} interval   A valid date interval enum value
1752  * @param {Number} value      The amount to add to the current date
1753  * @return {Date} The new Date instance
1754  */
1755 Date.prototype.add = function(interval, value){
1756   var d = this.clone();
1757   if (!interval || value === 0) return d;
1758   switch(interval.toLowerCase()){
1759     case Date.MILLI:
1760       d.setMilliseconds(this.getMilliseconds() + value);
1761       break;
1762     case Date.SECOND:
1763       d.setSeconds(this.getSeconds() + value);
1764       break;
1765     case Date.MINUTE:
1766       d.setMinutes(this.getMinutes() + value);
1767       break;
1768     case Date.HOUR:
1769       d.setHours(this.getHours() + value);
1770       break;
1771     case Date.DAY:
1772       d.setDate(this.getDate() + value);
1773       break;
1774     case Date.MONTH:
1775       var day = this.getDate();
1776       if(day > 28){
1777           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1778       }
1779       d.setDate(day);
1780       d.setMonth(this.getMonth() + value);
1781       break;
1782     case Date.YEAR:
1783       d.setFullYear(this.getFullYear() + value);
1784       break;
1785   }
1786   return d;
1787 };
1788 /*
1789  * Based on:
1790  * Ext JS Library 1.1.1
1791  * Copyright(c) 2006-2007, Ext JS, LLC.
1792  *
1793  * Originally Released Under LGPL - original licence link has changed is not relivant.
1794  *
1795  * Fork - LGPL
1796  * <script type="text/javascript">
1797  */
1798
1799 /**
1800  * @class Roo.lib.Dom
1801  * @static
1802  * 
1803  * Dom utils (from YIU afaik)
1804  * 
1805  **/
1806 Roo.lib.Dom = {
1807     /**
1808      * Get the view width
1809      * @param {Boolean} full True will get the full document, otherwise it's the view width
1810      * @return {Number} The width
1811      */
1812      
1813     getViewWidth : function(full) {
1814         return full ? this.getDocumentWidth() : this.getViewportWidth();
1815     },
1816     /**
1817      * Get the view height
1818      * @param {Boolean} full True will get the full document, otherwise it's the view height
1819      * @return {Number} The height
1820      */
1821     getViewHeight : function(full) {
1822         return full ? this.getDocumentHeight() : this.getViewportHeight();
1823     },
1824
1825     getDocumentHeight: function() {
1826         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1827         return Math.max(scrollHeight, this.getViewportHeight());
1828     },
1829
1830     getDocumentWidth: function() {
1831         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1832         return Math.max(scrollWidth, this.getViewportWidth());
1833     },
1834
1835     getViewportHeight: function() {
1836         var height = self.innerHeight;
1837         var mode = document.compatMode;
1838
1839         if ((mode || Roo.isIE) && !Roo.isOpera) {
1840             height = (mode == "CSS1Compat") ?
1841                      document.documentElement.clientHeight :
1842                      document.body.clientHeight;
1843         }
1844
1845         return height;
1846     },
1847
1848     getViewportWidth: function() {
1849         var width = self.innerWidth;
1850         var mode = document.compatMode;
1851
1852         if (mode || Roo.isIE) {
1853             width = (mode == "CSS1Compat") ?
1854                     document.documentElement.clientWidth :
1855                     document.body.clientWidth;
1856         }
1857         return width;
1858     },
1859
1860     isAncestor : function(p, c) {
1861         p = Roo.getDom(p);
1862         c = Roo.getDom(c);
1863         if (!p || !c) {
1864             return false;
1865         }
1866
1867         if (p.contains && !Roo.isSafari) {
1868             return p.contains(c);
1869         } else if (p.compareDocumentPosition) {
1870             return !!(p.compareDocumentPosition(c) & 16);
1871         } else {
1872             var parent = c.parentNode;
1873             while (parent) {
1874                 if (parent == p) {
1875                     return true;
1876                 }
1877                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1878                     return false;
1879                 }
1880                 parent = parent.parentNode;
1881             }
1882             return false;
1883         }
1884     },
1885
1886     getRegion : function(el) {
1887         return Roo.lib.Region.getRegion(el);
1888     },
1889
1890     getY : function(el) {
1891         return this.getXY(el)[1];
1892     },
1893
1894     getX : function(el) {
1895         return this.getXY(el)[0];
1896     },
1897
1898     getXY : function(el) {
1899         var p, pe, b, scroll, bd = document.body;
1900         el = Roo.getDom(el);
1901         var fly = Roo.lib.AnimBase.fly;
1902         if (el.getBoundingClientRect) {
1903             b = el.getBoundingClientRect();
1904             scroll = fly(document).getScroll();
1905             return [b.left + scroll.left, b.top + scroll.top];
1906         }
1907         var x = 0, y = 0;
1908
1909         p = el;
1910
1911         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1912
1913         while (p) {
1914
1915             x += p.offsetLeft;
1916             y += p.offsetTop;
1917
1918             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1919                 hasAbsolute = true;
1920             }
1921
1922             if (Roo.isGecko) {
1923                 pe = fly(p);
1924
1925                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1926                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1927
1928
1929                 x += bl;
1930                 y += bt;
1931
1932
1933                 if (p != el && pe.getStyle('overflow') != 'visible') {
1934                     x += bl;
1935                     y += bt;
1936                 }
1937             }
1938             p = p.offsetParent;
1939         }
1940
1941         if (Roo.isSafari && hasAbsolute) {
1942             x -= bd.offsetLeft;
1943             y -= bd.offsetTop;
1944         }
1945
1946         if (Roo.isGecko && !hasAbsolute) {
1947             var dbd = fly(bd);
1948             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1949             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1950         }
1951
1952         p = el.parentNode;
1953         while (p && p != bd) {
1954             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1955                 x -= p.scrollLeft;
1956                 y -= p.scrollTop;
1957             }
1958             p = p.parentNode;
1959         }
1960         return [x, y];
1961     },
1962  
1963   
1964
1965
1966     setXY : function(el, xy) {
1967         el = Roo.fly(el, '_setXY');
1968         el.position();
1969         var pts = el.translatePoints(xy);
1970         if (xy[0] !== false) {
1971             el.dom.style.left = pts.left + "px";
1972         }
1973         if (xy[1] !== false) {
1974             el.dom.style.top = pts.top + "px";
1975         }
1976     },
1977
1978     setX : function(el, x) {
1979         this.setXY(el, [x, false]);
1980     },
1981
1982     setY : function(el, y) {
1983         this.setXY(el, [false, y]);
1984     }
1985 };
1986 /*
1987  * Portions of this file are based on pieces of Yahoo User Interface Library
1988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1989  * YUI licensed under the BSD License:
1990  * http://developer.yahoo.net/yui/license.txt
1991  * <script type="text/javascript">
1992  *
1993  */
1994
1995 Roo.lib.Event = function() {
1996     var loadComplete = false;
1997     var listeners = [];
1998     var unloadListeners = [];
1999     var retryCount = 0;
2000     var onAvailStack = [];
2001     var counter = 0;
2002     var lastError = null;
2003
2004     return {
2005         POLL_RETRYS: 200,
2006         POLL_INTERVAL: 20,
2007         EL: 0,
2008         TYPE: 1,
2009         FN: 2,
2010         WFN: 3,
2011         OBJ: 3,
2012         ADJ_SCOPE: 4,
2013         _interval: null,
2014
2015         startInterval: function() {
2016             if (!this._interval) {
2017                 var self = this;
2018                 var callback = function() {
2019                     self._tryPreloadAttach();
2020                 };
2021                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2022
2023             }
2024         },
2025
2026         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2027             onAvailStack.push({ id:         p_id,
2028                 fn:         p_fn,
2029                 obj:        p_obj,
2030                 override:   p_override,
2031                 checkReady: false    });
2032
2033             retryCount = this.POLL_RETRYS;
2034             this.startInterval();
2035         },
2036
2037
2038         addListener: function(el, eventName, fn) {
2039             el = Roo.getDom(el);
2040             if (!el || !fn) {
2041                 return false;
2042             }
2043
2044             if ("unload" == eventName) {
2045                 unloadListeners[unloadListeners.length] =
2046                 [el, eventName, fn];
2047                 return true;
2048             }
2049
2050             var wrappedFn = function(e) {
2051                 return fn(Roo.lib.Event.getEvent(e));
2052             };
2053
2054             var li = [el, eventName, fn, wrappedFn];
2055
2056             var index = listeners.length;
2057             listeners[index] = li;
2058
2059             this.doAdd(el, eventName, wrappedFn, false);
2060             return true;
2061
2062         },
2063
2064
2065         removeListener: function(el, eventName, fn) {
2066             var i, len;
2067
2068             el = Roo.getDom(el);
2069
2070             if(!fn) {
2071                 return this.purgeElement(el, false, eventName);
2072             }
2073
2074
2075             if ("unload" == eventName) {
2076
2077                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2078                     var li = unloadListeners[i];
2079                     if (li &&
2080                         li[0] == el &&
2081                         li[1] == eventName &&
2082                         li[2] == fn) {
2083                         unloadListeners.splice(i, 1);
2084                         return true;
2085                     }
2086                 }
2087
2088                 return false;
2089             }
2090
2091             var cacheItem = null;
2092
2093
2094             var index = arguments[3];
2095
2096             if ("undefined" == typeof index) {
2097                 index = this._getCacheIndex(el, eventName, fn);
2098             }
2099
2100             if (index >= 0) {
2101                 cacheItem = listeners[index];
2102             }
2103
2104             if (!el || !cacheItem) {
2105                 return false;
2106             }
2107
2108             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2109
2110             delete listeners[index][this.WFN];
2111             delete listeners[index][this.FN];
2112             listeners.splice(index, 1);
2113
2114             return true;
2115
2116         },
2117
2118
2119         getTarget: function(ev, resolveTextNode) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var t = ev.target || ev.srcElement;
2123             return this.resolveTextNode(t);
2124         },
2125
2126
2127         resolveTextNode: function(node) {
2128             if (Roo.isSafari && node && 3 == node.nodeType) {
2129                 return node.parentNode;
2130             } else {
2131                 return node;
2132             }
2133         },
2134
2135
2136         getPageX: function(ev) {
2137             ev = ev.browserEvent || ev;
2138             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2139             var x = ev.pageX;
2140             if (!x && 0 !== x) {
2141                 x = ev.clientX || 0;
2142
2143                 if (Roo.isIE) {
2144                     x += this.getScroll()[1];
2145                 }
2146             }
2147
2148             return x;
2149         },
2150
2151
2152         getPageY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             var y = ev.pageY;
2156             if (!y && 0 !== y) {
2157                 y = ev.clientY || 0;
2158
2159                 if (Roo.isIE) {
2160                     y += this.getScroll()[0];
2161                 }
2162             }
2163
2164
2165             return y;
2166         },
2167
2168
2169         getXY: function(ev) {
2170             ev = ev.browserEvent || ev;
2171             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2172             return [this.getPageX(ev), this.getPageY(ev)];
2173         },
2174
2175
2176         getRelatedTarget: function(ev) {
2177             ev = ev.browserEvent || ev;
2178             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2179             var t = ev.relatedTarget;
2180             if (!t) {
2181                 if (ev.type == "mouseout") {
2182                     t = ev.toElement;
2183                 } else if (ev.type == "mouseover") {
2184                     t = ev.fromElement;
2185                 }
2186             }
2187
2188             return this.resolveTextNode(t);
2189         },
2190
2191
2192         getTime: function(ev) {
2193             ev = ev.browserEvent || ev;
2194             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2195             if (!ev.time) {
2196                 var t = new Date().getTime();
2197                 try {
2198                     ev.time = t;
2199                 } catch(ex) {
2200                     this.lastError = ex;
2201                     return t;
2202                 }
2203             }
2204
2205             return ev.time;
2206         },
2207
2208
2209         stopEvent: function(ev) {
2210             this.stopPropagation(ev);
2211             this.preventDefault(ev);
2212         },
2213
2214
2215         stopPropagation: function(ev) {
2216             ev = ev.browserEvent || ev;
2217             if (ev.stopPropagation) {
2218                 ev.stopPropagation();
2219             } else {
2220                 ev.cancelBubble = true;
2221             }
2222         },
2223
2224
2225         preventDefault: function(ev) {
2226             ev = ev.browserEvent || ev;
2227             if(ev.preventDefault) {
2228                 ev.preventDefault();
2229             } else {
2230                 ev.returnValue = false;
2231             }
2232         },
2233
2234
2235         getEvent: function(e) {
2236             var ev = e || window.event;
2237             if (!ev) {
2238                 var c = this.getEvent.caller;
2239                 while (c) {
2240                     ev = c.arguments[0];
2241                     if (ev && Event == ev.constructor) {
2242                         break;
2243                     }
2244                     c = c.caller;
2245                 }
2246             }
2247             return ev;
2248         },
2249
2250
2251         getCharCode: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             return ev.charCode || ev.keyCode || 0;
2254         },
2255
2256
2257         _getCacheIndex: function(el, eventName, fn) {
2258             for (var i = 0,len = listeners.length; i < len; ++i) {
2259                 var li = listeners[i];
2260                 if (li &&
2261                     li[this.FN] == fn &&
2262                     li[this.EL] == el &&
2263                     li[this.TYPE] == eventName) {
2264                     return i;
2265                 }
2266             }
2267
2268             return -1;
2269         },
2270
2271
2272         elCache: {},
2273
2274
2275         getEl: function(id) {
2276             return document.getElementById(id);
2277         },
2278
2279
2280         clearCache: function() {
2281         },
2282
2283
2284         _load: function(e) {
2285             loadComplete = true;
2286             var EU = Roo.lib.Event;
2287
2288
2289             if (Roo.isIE) {
2290                 EU.doRemove(window, "load", EU._load);
2291             }
2292         },
2293
2294
2295         _tryPreloadAttach: function() {
2296
2297             if (this.locked) {
2298                 return false;
2299             }
2300
2301             this.locked = true;
2302
2303
2304             var tryAgain = !loadComplete;
2305             if (!tryAgain) {
2306                 tryAgain = (retryCount > 0);
2307             }
2308
2309
2310             var notAvail = [];
2311             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2312                 var item = onAvailStack[i];
2313                 if (item) {
2314                     var el = this.getEl(item.id);
2315
2316                     if (el) {
2317                         if (!item.checkReady ||
2318                             loadComplete ||
2319                             el.nextSibling ||
2320                             (document && document.body)) {
2321
2322                             var scope = el;
2323                             if (item.override) {
2324                                 if (item.override === true) {
2325                                     scope = item.obj;
2326                                 } else {
2327                                     scope = item.override;
2328                                 }
2329                             }
2330                             item.fn.call(scope, item.obj);
2331                             onAvailStack[i] = null;
2332                         }
2333                     } else {
2334                         notAvail.push(item);
2335                     }
2336                 }
2337             }
2338
2339             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2340
2341             if (tryAgain) {
2342
2343                 this.startInterval();
2344             } else {
2345                 clearInterval(this._interval);
2346                 this._interval = null;
2347             }
2348
2349             this.locked = false;
2350
2351             return true;
2352
2353         },
2354
2355
2356         purgeElement: function(el, recurse, eventName) {
2357             var elListeners = this.getListeners(el, eventName);
2358             if (elListeners) {
2359                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2360                     var l = elListeners[i];
2361                     this.removeListener(el, l.type, l.fn);
2362                 }
2363             }
2364
2365             if (recurse && el && el.childNodes) {
2366                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2367                     this.purgeElement(el.childNodes[i], recurse, eventName);
2368                 }
2369             }
2370         },
2371
2372
2373         getListeners: function(el, eventName) {
2374             var results = [], searchLists;
2375             if (!eventName) {
2376                 searchLists = [listeners, unloadListeners];
2377             } else if (eventName == "unload") {
2378                 searchLists = [unloadListeners];
2379             } else {
2380                 searchLists = [listeners];
2381             }
2382
2383             for (var j = 0; j < searchLists.length; ++j) {
2384                 var searchList = searchLists[j];
2385                 if (searchList && searchList.length > 0) {
2386                     for (var i = 0,len = searchList.length; i < len; ++i) {
2387                         var l = searchList[i];
2388                         if (l && l[this.EL] === el &&
2389                             (!eventName || eventName === l[this.TYPE])) {
2390                             results.push({
2391                                 type:   l[this.TYPE],
2392                                 fn:     l[this.FN],
2393                                 obj:    l[this.OBJ],
2394                                 adjust: l[this.ADJ_SCOPE],
2395                                 index:  i
2396                             });
2397                         }
2398                     }
2399                 }
2400             }
2401
2402             return (results.length) ? results : null;
2403         },
2404
2405
2406         _unload: function(e) {
2407
2408             var EU = Roo.lib.Event, i, j, l, len, index;
2409
2410             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2411                 l = unloadListeners[i];
2412                 if (l) {
2413                     var scope = window;
2414                     if (l[EU.ADJ_SCOPE]) {
2415                         if (l[EU.ADJ_SCOPE] === true) {
2416                             scope = l[EU.OBJ];
2417                         } else {
2418                             scope = l[EU.ADJ_SCOPE];
2419                         }
2420                     }
2421                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2422                     unloadListeners[i] = null;
2423                     l = null;
2424                     scope = null;
2425                 }
2426             }
2427
2428             unloadListeners = null;
2429
2430             if (listeners && listeners.length > 0) {
2431                 j = listeners.length;
2432                 while (j) {
2433                     index = j - 1;
2434                     l = listeners[index];
2435                     if (l) {
2436                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2437                                 l[EU.FN], index);
2438                     }
2439                     j = j - 1;
2440                 }
2441                 l = null;
2442
2443                 EU.clearCache();
2444             }
2445
2446             EU.doRemove(window, "unload", EU._unload);
2447
2448         },
2449
2450
2451         getScroll: function() {
2452             var dd = document.documentElement, db = document.body;
2453             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2454                 return [dd.scrollTop, dd.scrollLeft];
2455             } else if (db) {
2456                 return [db.scrollTop, db.scrollLeft];
2457             } else {
2458                 return [0, 0];
2459             }
2460         },
2461
2462
2463         doAdd: function () {
2464             if (window.addEventListener) {
2465                 return function(el, eventName, fn, capture) {
2466                     el.addEventListener(eventName, fn, (capture));
2467                 };
2468             } else if (window.attachEvent) {
2469                 return function(el, eventName, fn, capture) {
2470                     el.attachEvent("on" + eventName, fn);
2471                 };
2472             } else {
2473                 return function() {
2474                 };
2475             }
2476         }(),
2477
2478
2479         doRemove: function() {
2480             if (window.removeEventListener) {
2481                 return function (el, eventName, fn, capture) {
2482                     el.removeEventListener(eventName, fn, (capture));
2483                 };
2484             } else if (window.detachEvent) {
2485                 return function (el, eventName, fn) {
2486                     el.detachEvent("on" + eventName, fn);
2487                 };
2488             } else {
2489                 return function() {
2490                 };
2491             }
2492         }()
2493     };
2494     
2495 }();
2496 (function() {     
2497    
2498     var E = Roo.lib.Event;
2499     E.on = E.addListener;
2500     E.un = E.removeListener;
2501
2502     if (document && document.body) {
2503         E._load();
2504     } else {
2505         E.doAdd(window, "load", E._load);
2506     }
2507     E.doAdd(window, "unload", E._unload);
2508     E._tryPreloadAttach();
2509 })();
2510
2511 /*
2512  * Portions of this file are based on pieces of Yahoo User Interface Library
2513  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2514  * YUI licensed under the BSD License:
2515  * http://developer.yahoo.net/yui/license.txt
2516  * <script type="text/javascript">
2517  *
2518  */
2519
2520 (function() {
2521     /**
2522      * @class Roo.lib.Ajax
2523      *
2524      */
2525     Roo.lib.Ajax = {
2526         /**
2527          * @static 
2528          */
2529         request : function(method, uri, cb, data, options) {
2530             if(options){
2531                 var hs = options.headers;
2532                 if(hs){
2533                     for(var h in hs){
2534                         if(hs.hasOwnProperty(h)){
2535                             this.initHeader(h, hs[h], false);
2536                         }
2537                     }
2538                 }
2539                 if(options.xmlData){
2540                     this.initHeader('Content-Type', 'text/xml', false);
2541                     method = 'POST';
2542                     data = options.xmlData;
2543                 }
2544             }
2545
2546             return this.asyncRequest(method, uri, cb, data);
2547         },
2548
2549         serializeForm : function(form) {
2550             if(typeof form == 'string') {
2551                 form = (document.getElementById(form) || document.forms[form]);
2552             }
2553
2554             var el, name, val, disabled, data = '', hasSubmit = false;
2555             for (var i = 0; i < form.elements.length; i++) {
2556                 el = form.elements[i];
2557                 disabled = form.elements[i].disabled;
2558                 name = form.elements[i].name;
2559                 val = form.elements[i].value;
2560
2561                 if (!disabled && name){
2562                     switch (el.type)
2563                             {
2564                         case 'select-one':
2565                         case 'select-multiple':
2566                             for (var j = 0; j < el.options.length; j++) {
2567                                 if (el.options[j].selected) {
2568                                     if (Roo.isIE) {
2569                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2570                                     }
2571                                     else {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                 }
2575                             }
2576                             break;
2577                         case 'radio':
2578                         case 'checkbox':
2579                             if (el.checked) {
2580                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2581                             }
2582                             break;
2583                         case 'file':
2584
2585                         case undefined:
2586
2587                         case 'reset':
2588
2589                         case 'button':
2590
2591                             break;
2592                         case 'submit':
2593                             if(hasSubmit == false) {
2594                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2595                                 hasSubmit = true;
2596                             }
2597                             break;
2598                         default:
2599                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2600                             break;
2601                     }
2602                 }
2603             }
2604             data = data.substr(0, data.length - 1);
2605             return data;
2606         },
2607
2608         headers:{},
2609
2610         hasHeaders:false,
2611
2612         useDefaultHeader:true,
2613
2614         defaultPostHeader:'application/x-www-form-urlencoded',
2615
2616         useDefaultXhrHeader:true,
2617
2618         defaultXhrHeader:'XMLHttpRequest',
2619
2620         hasDefaultHeaders:true,
2621
2622         defaultHeaders:{},
2623
2624         poll:{},
2625
2626         timeout:{},
2627
2628         pollInterval:50,
2629
2630         transactionId:0,
2631
2632         setProgId:function(id)
2633         {
2634             this.activeX.unshift(id);
2635         },
2636
2637         setDefaultPostHeader:function(b)
2638         {
2639             this.useDefaultHeader = b;
2640         },
2641
2642         setDefaultXhrHeader:function(b)
2643         {
2644             this.useDefaultXhrHeader = b;
2645         },
2646
2647         setPollingInterval:function(i)
2648         {
2649             if (typeof i == 'number' && isFinite(i)) {
2650                 this.pollInterval = i;
2651             }
2652         },
2653
2654         createXhrObject:function(transactionId)
2655         {
2656             var obj,http;
2657             try
2658             {
2659
2660                 http = new XMLHttpRequest();
2661
2662                 obj = { conn:http, tId:transactionId };
2663             }
2664             catch(e)
2665             {
2666                 for (var i = 0; i < this.activeX.length; ++i) {
2667                     try
2668                     {
2669
2670                         http = new ActiveXObject(this.activeX[i]);
2671
2672                         obj = { conn:http, tId:transactionId };
2673                         break;
2674                     }
2675                     catch(e) {
2676                     }
2677                 }
2678             }
2679             finally
2680             {
2681                 return obj;
2682             }
2683         },
2684
2685         getConnectionObject:function()
2686         {
2687             var o;
2688             var tId = this.transactionId;
2689
2690             try
2691             {
2692                 o = this.createXhrObject(tId);
2693                 if (o) {
2694                     this.transactionId++;
2695                 }
2696             }
2697             catch(e) {
2698             }
2699             finally
2700             {
2701                 return o;
2702             }
2703         },
2704
2705         asyncRequest:function(method, uri, callback, postData)
2706         {
2707             var o = this.getConnectionObject();
2708
2709             if (!o) {
2710                 return null;
2711             }
2712             else {
2713                 o.conn.open(method, uri, true);
2714
2715                 if (this.useDefaultXhrHeader) {
2716                     if (!this.defaultHeaders['X-Requested-With']) {
2717                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2718                     }
2719                 }
2720
2721                 if(postData && this.useDefaultHeader){
2722                     this.initHeader('Content-Type', this.defaultPostHeader);
2723                 }
2724
2725                  if (this.hasDefaultHeaders || this.hasHeaders) {
2726                     this.setHeader(o);
2727                 }
2728
2729                 this.handleReadyState(o, callback);
2730                 o.conn.send(postData || null);
2731
2732                 return o;
2733             }
2734         },
2735
2736         handleReadyState:function(o, callback)
2737         {
2738             var oConn = this;
2739
2740             if (callback && callback.timeout) {
2741                 
2742                 this.timeout[o.tId] = window.setTimeout(function() {
2743                     oConn.abort(o, callback, true);
2744                 }, callback.timeout);
2745             }
2746
2747             this.poll[o.tId] = window.setInterval(
2748                     function() {
2749                         if (o.conn && o.conn.readyState == 4) {
2750                             window.clearInterval(oConn.poll[o.tId]);
2751                             delete oConn.poll[o.tId];
2752
2753                             if(callback && callback.timeout) {
2754                                 window.clearTimeout(oConn.timeout[o.tId]);
2755                                 delete oConn.timeout[o.tId];
2756                             }
2757
2758                             oConn.handleTransactionResponse(o, callback);
2759                         }
2760                     }
2761                     , this.pollInterval);
2762         },
2763
2764         handleTransactionResponse:function(o, callback, isAbort)
2765         {
2766
2767             if (!callback) {
2768                 this.releaseObject(o);
2769                 return;
2770             }
2771
2772             var httpStatus, responseObject;
2773
2774             try
2775             {
2776                 if (o.conn.status !== undefined && o.conn.status != 0) {
2777                     httpStatus = o.conn.status;
2778                 }
2779                 else {
2780                     httpStatus = 13030;
2781                 }
2782             }
2783             catch(e) {
2784
2785
2786                 httpStatus = 13030;
2787             }
2788
2789             if (httpStatus >= 200 && httpStatus < 300) {
2790                 responseObject = this.createResponseObject(o, callback.argument);
2791                 if (callback.success) {
2792                     if (!callback.scope) {
2793                         callback.success(responseObject);
2794                     }
2795                     else {
2796
2797
2798                         callback.success.apply(callback.scope, [responseObject]);
2799                     }
2800                 }
2801             }
2802             else {
2803                 switch (httpStatus) {
2804
2805                     case 12002:
2806                     case 12029:
2807                     case 12030:
2808                     case 12031:
2809                     case 12152:
2810                     case 13030:
2811                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2812                         if (callback.failure) {
2813                             if (!callback.scope) {
2814                                 callback.failure(responseObject);
2815                             }
2816                             else {
2817                                 callback.failure.apply(callback.scope, [responseObject]);
2818                             }
2819                         }
2820                         break;
2821                     default:
2822                         responseObject = this.createResponseObject(o, callback.argument);
2823                         if (callback.failure) {
2824                             if (!callback.scope) {
2825                                 callback.failure(responseObject);
2826                             }
2827                             else {
2828                                 callback.failure.apply(callback.scope, [responseObject]);
2829                             }
2830                         }
2831                 }
2832             }
2833
2834             this.releaseObject(o);
2835             responseObject = null;
2836         },
2837
2838         createResponseObject:function(o, callbackArg)
2839         {
2840             var obj = {};
2841             var headerObj = {};
2842
2843             try
2844             {
2845                 var headerStr = o.conn.getAllResponseHeaders();
2846                 var header = headerStr.split('\n');
2847                 for (var i = 0; i < header.length; i++) {
2848                     var delimitPos = header[i].indexOf(':');
2849                     if (delimitPos != -1) {
2850                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2851                     }
2852                 }
2853             }
2854             catch(e) {
2855             }
2856
2857             obj.tId = o.tId;
2858             obj.status = o.conn.status;
2859             obj.statusText = o.conn.statusText;
2860             obj.getResponseHeader = headerObj;
2861             obj.getAllResponseHeaders = headerStr;
2862             obj.responseText = o.conn.responseText;
2863             obj.responseXML = o.conn.responseXML;
2864
2865             if (typeof callbackArg !== undefined) {
2866                 obj.argument = callbackArg;
2867             }
2868
2869             return obj;
2870         },
2871
2872         createExceptionObject:function(tId, callbackArg, isAbort)
2873         {
2874             var COMM_CODE = 0;
2875             var COMM_ERROR = 'communication failure';
2876             var ABORT_CODE = -1;
2877             var ABORT_ERROR = 'transaction aborted';
2878
2879             var obj = {};
2880
2881             obj.tId = tId;
2882             if (isAbort) {
2883                 obj.status = ABORT_CODE;
2884                 obj.statusText = ABORT_ERROR;
2885             }
2886             else {
2887                 obj.status = COMM_CODE;
2888                 obj.statusText = COMM_ERROR;
2889             }
2890
2891             if (callbackArg) {
2892                 obj.argument = callbackArg;
2893             }
2894
2895             return obj;
2896         },
2897
2898         initHeader:function(label, value, isDefault)
2899         {
2900             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2901
2902             if (headerObj[label] === undefined) {
2903                 headerObj[label] = value;
2904             }
2905             else {
2906
2907
2908                 headerObj[label] = value + "," + headerObj[label];
2909             }
2910
2911             if (isDefault) {
2912                 this.hasDefaultHeaders = true;
2913             }
2914             else {
2915                 this.hasHeaders = true;
2916             }
2917         },
2918
2919
2920         setHeader:function(o)
2921         {
2922             if (this.hasDefaultHeaders) {
2923                 for (var prop in this.defaultHeaders) {
2924                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2925                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2926                     }
2927                 }
2928             }
2929
2930             if (this.hasHeaders) {
2931                 for (var prop in this.headers) {
2932                     if (this.headers.hasOwnProperty(prop)) {
2933                         o.conn.setRequestHeader(prop, this.headers[prop]);
2934                     }
2935                 }
2936                 this.headers = {};
2937                 this.hasHeaders = false;
2938             }
2939         },
2940
2941         resetDefaultHeaders:function() {
2942             delete this.defaultHeaders;
2943             this.defaultHeaders = {};
2944             this.hasDefaultHeaders = false;
2945         },
2946
2947         abort:function(o, callback, isTimeout)
2948         {
2949             if(this.isCallInProgress(o)) {
2950                 o.conn.abort();
2951                 window.clearInterval(this.poll[o.tId]);
2952                 delete this.poll[o.tId];
2953                 if (isTimeout) {
2954                     delete this.timeout[o.tId];
2955                 }
2956
2957                 this.handleTransactionResponse(o, callback, true);
2958
2959                 return true;
2960             }
2961             else {
2962                 return false;
2963             }
2964         },
2965
2966
2967         isCallInProgress:function(o)
2968         {
2969             if (o && o.conn) {
2970                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2971             }
2972             else {
2973
2974                 return false;
2975             }
2976         },
2977
2978
2979         releaseObject:function(o)
2980         {
2981
2982             o.conn = null;
2983
2984             o = null;
2985         },
2986
2987         activeX:[
2988         'MSXML2.XMLHTTP.3.0',
2989         'MSXML2.XMLHTTP',
2990         'Microsoft.XMLHTTP'
2991         ]
2992
2993
2994     };
2995 })();/*
2996  * Portions of this file are based on pieces of Yahoo User Interface Library
2997  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2998  * YUI licensed under the BSD License:
2999  * http://developer.yahoo.net/yui/license.txt
3000  * <script type="text/javascript">
3001  *
3002  */
3003
3004 Roo.lib.Region = function(t, r, b, l) {
3005     this.top = t;
3006     this[1] = t;
3007     this.right = r;
3008     this.bottom = b;
3009     this.left = l;
3010     this[0] = l;
3011 };
3012
3013
3014 Roo.lib.Region.prototype = {
3015     contains : function(region) {
3016         return ( region.left >= this.left &&
3017                  region.right <= this.right &&
3018                  region.top >= this.top &&
3019                  region.bottom <= this.bottom    );
3020
3021     },
3022
3023     getArea : function() {
3024         return ( (this.bottom - this.top) * (this.right - this.left) );
3025     },
3026
3027     intersect : function(region) {
3028         var t = Math.max(this.top, region.top);
3029         var r = Math.min(this.right, region.right);
3030         var b = Math.min(this.bottom, region.bottom);
3031         var l = Math.max(this.left, region.left);
3032
3033         if (b >= t && r >= l) {
3034             return new Roo.lib.Region(t, r, b, l);
3035         } else {
3036             return null;
3037         }
3038     },
3039     union : function(region) {
3040         var t = Math.min(this.top, region.top);
3041         var r = Math.max(this.right, region.right);
3042         var b = Math.max(this.bottom, region.bottom);
3043         var l = Math.min(this.left, region.left);
3044
3045         return new Roo.lib.Region(t, r, b, l);
3046     },
3047
3048     adjust : function(t, l, b, r) {
3049         this.top += t;
3050         this.left += l;
3051         this.right += r;
3052         this.bottom += b;
3053         return this;
3054     }
3055 };
3056
3057 Roo.lib.Region.getRegion = function(el) {
3058     var p = Roo.lib.Dom.getXY(el);
3059
3060     var t = p[1];
3061     var r = p[0] + el.offsetWidth;
3062     var b = p[1] + el.offsetHeight;
3063     var l = p[0];
3064
3065     return new Roo.lib.Region(t, r, b, l);
3066 };
3067 /*
3068  * Portions of this file are based on pieces of Yahoo User Interface Library
3069  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3070  * YUI licensed under the BSD License:
3071  * http://developer.yahoo.net/yui/license.txt
3072  * <script type="text/javascript">
3073  *
3074  */
3075 //@@dep Roo.lib.Region
3076
3077
3078 Roo.lib.Point = function(x, y) {
3079     if (x instanceof Array) {
3080         y = x[1];
3081         x = x[0];
3082     }
3083     this.x = this.right = this.left = this[0] = x;
3084     this.y = this.top = this.bottom = this[1] = y;
3085 };
3086
3087 Roo.lib.Point.prototype = new Roo.lib.Region();
3088 /*
3089  * Portions of this file are based on pieces of Yahoo User Interface Library
3090  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3091  * YUI licensed under the BSD License:
3092  * http://developer.yahoo.net/yui/license.txt
3093  * <script type="text/javascript">
3094  *
3095  */
3096  
3097 (function() {   
3098
3099     Roo.lib.Anim = {
3100         scroll : function(el, args, duration, easing, cb, scope) {
3101             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3102         },
3103
3104         motion : function(el, args, duration, easing, cb, scope) {
3105             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3106         },
3107
3108         color : function(el, args, duration, easing, cb, scope) {
3109             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3110         },
3111
3112         run : function(el, args, duration, easing, cb, scope, type) {
3113             type = type || Roo.lib.AnimBase;
3114             if (typeof easing == "string") {
3115                 easing = Roo.lib.Easing[easing];
3116             }
3117             var anim = new type(el, args, duration, easing);
3118             anim.animateX(function() {
3119                 Roo.callback(cb, scope);
3120             });
3121             return anim;
3122         }
3123     };
3124 })();/*
3125  * Portions of this file are based on pieces of Yahoo User Interface Library
3126  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3127  * YUI licensed under the BSD License:
3128  * http://developer.yahoo.net/yui/license.txt
3129  * <script type="text/javascript">
3130  *
3131  */
3132
3133 (function() {    
3134     var libFlyweight;
3135     
3136     function fly(el) {
3137         if (!libFlyweight) {
3138             libFlyweight = new Roo.Element.Flyweight();
3139         }
3140         libFlyweight.dom = el;
3141         return libFlyweight;
3142     }
3143
3144     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3145     
3146    
3147     
3148     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3149         if (el) {
3150             this.init(el, attributes, duration, method);
3151         }
3152     };
3153
3154     Roo.lib.AnimBase.fly = fly;
3155     
3156     
3157     
3158     Roo.lib.AnimBase.prototype = {
3159
3160         toString: function() {
3161             var el = this.getEl();
3162             var id = el.id || el.tagName;
3163             return ("Anim " + id);
3164         },
3165
3166         patterns: {
3167             noNegatives:        /width|height|opacity|padding/i,
3168             offsetAttribute:  /^((width|height)|(top|left))$/,
3169             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3170             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3171         },
3172
3173
3174         doMethod: function(attr, start, end) {
3175             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3176         },
3177
3178
3179         setAttribute: function(attr, val, unit) {
3180             if (this.patterns.noNegatives.test(attr)) {
3181                 val = (val > 0) ? val : 0;
3182             }
3183
3184             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3185         },
3186
3187
3188         getAttribute: function(attr) {
3189             var el = this.getEl();
3190             var val = fly(el).getStyle(attr);
3191
3192             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3193                 return parseFloat(val);
3194             }
3195
3196             var a = this.patterns.offsetAttribute.exec(attr) || [];
3197             var pos = !!( a[3] );
3198             var box = !!( a[2] );
3199
3200
3201             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3202                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3203             } else {
3204                 val = 0;
3205             }
3206
3207             return val;
3208         },
3209
3210
3211         getDefaultUnit: function(attr) {
3212             if (this.patterns.defaultUnit.test(attr)) {
3213                 return 'px';
3214             }
3215
3216             return '';
3217         },
3218
3219         animateX : function(callback, scope) {
3220             var f = function() {
3221                 this.onComplete.removeListener(f);
3222                 if (typeof callback == "function") {
3223                     callback.call(scope || this, this);
3224                 }
3225             };
3226             this.onComplete.addListener(f, this);
3227             this.animate();
3228         },
3229
3230
3231         setRuntimeAttribute: function(attr) {
3232             var start;
3233             var end;
3234             var attributes = this.attributes;
3235
3236             this.runtimeAttributes[attr] = {};
3237
3238             var isset = function(prop) {
3239                 return (typeof prop !== 'undefined');
3240             };
3241
3242             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3243                 return false;
3244             }
3245
3246             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3247
3248
3249             if (isset(attributes[attr]['to'])) {
3250                 end = attributes[attr]['to'];
3251             } else if (isset(attributes[attr]['by'])) {
3252                 if (start.constructor == Array) {
3253                     end = [];
3254                     for (var i = 0, len = start.length; i < len; ++i) {
3255                         end[i] = start[i] + attributes[attr]['by'][i];
3256                     }
3257                 } else {
3258                     end = start + attributes[attr]['by'];
3259                 }
3260             }
3261
3262             this.runtimeAttributes[attr].start = start;
3263             this.runtimeAttributes[attr].end = end;
3264
3265
3266             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3267         },
3268
3269
3270         init: function(el, attributes, duration, method) {
3271
3272             var isAnimated = false;
3273
3274
3275             var startTime = null;
3276
3277
3278             var actualFrames = 0;
3279
3280
3281             el = Roo.getDom(el);
3282
3283
3284             this.attributes = attributes || {};
3285
3286
3287             this.duration = duration || 1;
3288
3289
3290             this.method = method || Roo.lib.Easing.easeNone;
3291
3292
3293             this.useSeconds = true;
3294
3295
3296             this.currentFrame = 0;
3297
3298
3299             this.totalFrames = Roo.lib.AnimMgr.fps;
3300
3301
3302             this.getEl = function() {
3303                 return el;
3304             };
3305
3306
3307             this.isAnimated = function() {
3308                 return isAnimated;
3309             };
3310
3311
3312             this.getStartTime = function() {
3313                 return startTime;
3314             };
3315
3316             this.runtimeAttributes = {};
3317
3318
3319             this.animate = function() {
3320                 if (this.isAnimated()) {
3321                     return false;
3322                 }
3323
3324                 this.currentFrame = 0;
3325
3326                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3327
3328                 Roo.lib.AnimMgr.registerElement(this);
3329             };
3330
3331
3332             this.stop = function(finish) {
3333                 if (finish) {
3334                     this.currentFrame = this.totalFrames;
3335                     this._onTween.fire();
3336                 }
3337                 Roo.lib.AnimMgr.stop(this);
3338             };
3339
3340             var onStart = function() {
3341                 this.onStart.fire();
3342
3343                 this.runtimeAttributes = {};
3344                 for (var attr in this.attributes) {
3345                     this.setRuntimeAttribute(attr);
3346                 }
3347
3348                 isAnimated = true;
3349                 actualFrames = 0;
3350                 startTime = new Date();
3351             };
3352
3353
3354             var onTween = function() {
3355                 var data = {
3356                     duration: new Date() - this.getStartTime(),
3357                     currentFrame: this.currentFrame
3358                 };
3359
3360                 data.toString = function() {
3361                     return (
3362                             'duration: ' + data.duration +
3363                             ', currentFrame: ' + data.currentFrame
3364                             );
3365                 };
3366
3367                 this.onTween.fire(data);
3368
3369                 var runtimeAttributes = this.runtimeAttributes;
3370
3371                 for (var attr in runtimeAttributes) {
3372                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3373                 }
3374
3375                 actualFrames += 1;
3376             };
3377
3378             var onComplete = function() {
3379                 var actual_duration = (new Date() - startTime) / 1000 ;
3380
3381                 var data = {
3382                     duration: actual_duration,
3383                     frames: actualFrames,
3384                     fps: actualFrames / actual_duration
3385                 };
3386
3387                 data.toString = function() {
3388                     return (
3389                             'duration: ' + data.duration +
3390                             ', frames: ' + data.frames +
3391                             ', fps: ' + data.fps
3392                             );
3393                 };
3394
3395                 isAnimated = false;
3396                 actualFrames = 0;
3397                 this.onComplete.fire(data);
3398             };
3399
3400
3401             this._onStart = new Roo.util.Event(this);
3402             this.onStart = new Roo.util.Event(this);
3403             this.onTween = new Roo.util.Event(this);
3404             this._onTween = new Roo.util.Event(this);
3405             this.onComplete = new Roo.util.Event(this);
3406             this._onComplete = new Roo.util.Event(this);
3407             this._onStart.addListener(onStart);
3408             this._onTween.addListener(onTween);
3409             this._onComplete.addListener(onComplete);
3410         }
3411     };
3412 })();
3413 /*
3414  * Portions of this file are based on pieces of Yahoo User Interface Library
3415  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3416  * YUI licensed under the BSD License:
3417  * http://developer.yahoo.net/yui/license.txt
3418  * <script type="text/javascript">
3419  *
3420  */
3421
3422 Roo.lib.AnimMgr = new function() {
3423
3424     var thread = null;
3425
3426
3427     var queue = [];
3428
3429
3430     var tweenCount = 0;
3431
3432
3433     this.fps = 1000;
3434
3435
3436     this.delay = 1;
3437
3438
3439     this.registerElement = function(tween) {
3440         queue[queue.length] = tween;
3441         tweenCount += 1;
3442         tween._onStart.fire();
3443         this.start();
3444     };
3445
3446
3447     this.unRegister = function(tween, index) {
3448         tween._onComplete.fire();
3449         index = index || getIndex(tween);
3450         if (index != -1) {
3451             queue.splice(index, 1);
3452         }
3453
3454         tweenCount -= 1;
3455         if (tweenCount <= 0) {
3456             this.stop();
3457         }
3458     };
3459
3460
3461     this.start = function() {
3462         if (thread === null) {
3463             thread = setInterval(this.run, this.delay);
3464         }
3465     };
3466
3467
3468     this.stop = function(tween) {
3469         if (!tween) {
3470             clearInterval(thread);
3471
3472             for (var i = 0, len = queue.length; i < len; ++i) {
3473                 if (queue[0].isAnimated()) {
3474                     this.unRegister(queue[0], 0);
3475                 }
3476             }
3477
3478             queue = [];
3479             thread = null;
3480             tweenCount = 0;
3481         }
3482         else {
3483             this.unRegister(tween);
3484         }
3485     };
3486
3487
3488     this.run = function() {
3489         for (var i = 0, len = queue.length; i < len; ++i) {
3490             var tween = queue[i];
3491             if (!tween || !tween.isAnimated()) {
3492                 continue;
3493             }
3494
3495             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3496             {
3497                 tween.currentFrame += 1;
3498
3499                 if (tween.useSeconds) {
3500                     correctFrame(tween);
3501                 }
3502                 tween._onTween.fire();
3503             }
3504             else {
3505                 Roo.lib.AnimMgr.stop(tween, i);
3506             }
3507         }
3508     };
3509
3510     var getIndex = function(anim) {
3511         for (var i = 0, len = queue.length; i < len; ++i) {
3512             if (queue[i] == anim) {
3513                 return i;
3514             }
3515         }
3516         return -1;
3517     };
3518
3519
3520     var correctFrame = function(tween) {
3521         var frames = tween.totalFrames;
3522         var frame = tween.currentFrame;
3523         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3524         var elapsed = (new Date() - tween.getStartTime());
3525         var tweak = 0;
3526
3527         if (elapsed < tween.duration * 1000) {
3528             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3529         } else {
3530             tweak = frames - (frame + 1);
3531         }
3532         if (tweak > 0 && isFinite(tweak)) {
3533             if (tween.currentFrame + tweak >= frames) {
3534                 tweak = frames - (frame + 1);
3535             }
3536
3537             tween.currentFrame += tweak;
3538         }
3539     };
3540 };
3541
3542     /*
3543  * Portions of this file are based on pieces of Yahoo User Interface Library
3544  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3545  * YUI licensed under the BSD License:
3546  * http://developer.yahoo.net/yui/license.txt
3547  * <script type="text/javascript">
3548  *
3549  */
3550 Roo.lib.Bezier = new function() {
3551
3552         this.getPosition = function(points, t) {
3553             var n = points.length;
3554             var tmp = [];
3555
3556             for (var i = 0; i < n; ++i) {
3557                 tmp[i] = [points[i][0], points[i][1]];
3558             }
3559
3560             for (var j = 1; j < n; ++j) {
3561                 for (i = 0; i < n - j; ++i) {
3562                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3563                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3564                 }
3565             }
3566
3567             return [ tmp[0][0], tmp[0][1] ];
3568
3569         };
3570     };/*
3571  * Portions of this file are based on pieces of Yahoo User Interface Library
3572  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3573  * YUI licensed under the BSD License:
3574  * http://developer.yahoo.net/yui/license.txt
3575  * <script type="text/javascript">
3576  *
3577  */
3578 (function() {
3579
3580     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3581         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3582     };
3583
3584     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3585
3586     var fly = Roo.lib.AnimBase.fly;
3587     var Y = Roo.lib;
3588     var superclass = Y.ColorAnim.superclass;
3589     var proto = Y.ColorAnim.prototype;
3590
3591     proto.toString = function() {
3592         var el = this.getEl();
3593         var id = el.id || el.tagName;
3594         return ("ColorAnim " + id);
3595     };
3596
3597     proto.patterns.color = /color$/i;
3598     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3599     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3600     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3601     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3602
3603
3604     proto.parseColor = function(s) {
3605         if (s.length == 3) {
3606             return s;
3607         }
3608
3609         var c = this.patterns.hex.exec(s);
3610         if (c && c.length == 4) {
3611             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3612         }
3613
3614         c = this.patterns.rgb.exec(s);
3615         if (c && c.length == 4) {
3616             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3617         }
3618
3619         c = this.patterns.hex3.exec(s);
3620         if (c && c.length == 4) {
3621             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3622         }
3623
3624         return null;
3625     };
3626     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3627     proto.getAttribute = function(attr) {
3628         var el = this.getEl();
3629         if (this.patterns.color.test(attr)) {
3630             var val = fly(el).getStyle(attr);
3631
3632             if (this.patterns.transparent.test(val)) {
3633                 var parent = el.parentNode;
3634                 val = fly(parent).getStyle(attr);
3635
3636                 while (parent && this.patterns.transparent.test(val)) {
3637                     parent = parent.parentNode;
3638                     val = fly(parent).getStyle(attr);
3639                     if (parent.tagName.toUpperCase() == 'HTML') {
3640                         val = '#fff';
3641                     }
3642                 }
3643             }
3644         } else {
3645             val = superclass.getAttribute.call(this, attr);
3646         }
3647
3648         return val;
3649     };
3650     proto.getAttribute = function(attr) {
3651         var el = this.getEl();
3652         if (this.patterns.color.test(attr)) {
3653             var val = fly(el).getStyle(attr);
3654
3655             if (this.patterns.transparent.test(val)) {
3656                 var parent = el.parentNode;
3657                 val = fly(parent).getStyle(attr);
3658
3659                 while (parent && this.patterns.transparent.test(val)) {
3660                     parent = parent.parentNode;
3661                     val = fly(parent).getStyle(attr);
3662                     if (parent.tagName.toUpperCase() == 'HTML') {
3663                         val = '#fff';
3664                     }
3665                 }
3666             }
3667         } else {
3668             val = superclass.getAttribute.call(this, attr);
3669         }
3670
3671         return val;
3672     };
3673
3674     proto.doMethod = function(attr, start, end) {
3675         var val;
3676
3677         if (this.patterns.color.test(attr)) {
3678             val = [];
3679             for (var i = 0, len = start.length; i < len; ++i) {
3680                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3681             }
3682
3683             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3684         }
3685         else {
3686             val = superclass.doMethod.call(this, attr, start, end);
3687         }
3688
3689         return val;
3690     };
3691
3692     proto.setRuntimeAttribute = function(attr) {
3693         superclass.setRuntimeAttribute.call(this, attr);
3694
3695         if (this.patterns.color.test(attr)) {
3696             var attributes = this.attributes;
3697             var start = this.parseColor(this.runtimeAttributes[attr].start);
3698             var end = this.parseColor(this.runtimeAttributes[attr].end);
3699
3700             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3701                 end = this.parseColor(attributes[attr].by);
3702
3703                 for (var i = 0, len = start.length; i < len; ++i) {
3704                     end[i] = start[i] + end[i];
3705                 }
3706             }
3707
3708             this.runtimeAttributes[attr].start = start;
3709             this.runtimeAttributes[attr].end = end;
3710         }
3711     };
3712 })();
3713
3714 /*
3715  * Portions of this file are based on pieces of Yahoo User Interface Library
3716  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3717  * YUI licensed under the BSD License:
3718  * http://developer.yahoo.net/yui/license.txt
3719  * <script type="text/javascript">
3720  *
3721  */
3722 Roo.lib.Easing = {
3723
3724
3725     easeNone: function (t, b, c, d) {
3726         return c * t / d + b;
3727     },
3728
3729
3730     easeIn: function (t, b, c, d) {
3731         return c * (t /= d) * t + b;
3732     },
3733
3734
3735     easeOut: function (t, b, c, d) {
3736         return -c * (t /= d) * (t - 2) + b;
3737     },
3738
3739
3740     easeBoth: function (t, b, c, d) {
3741         if ((t /= d / 2) < 1) {
3742             return c / 2 * t * t + b;
3743         }
3744
3745         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3746     },
3747
3748
3749     easeInStrong: function (t, b, c, d) {
3750         return c * (t /= d) * t * t * t + b;
3751     },
3752
3753
3754     easeOutStrong: function (t, b, c, d) {
3755         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3756     },
3757
3758
3759     easeBothStrong: function (t, b, c, d) {
3760         if ((t /= d / 2) < 1) {
3761             return c / 2 * t * t * t * t + b;
3762         }
3763
3764         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3765     },
3766
3767
3768
3769     elasticIn: function (t, b, c, d, a, p) {
3770         if (t == 0) {
3771             return b;
3772         }
3773         if ((t /= d) == 1) {
3774             return b + c;
3775         }
3776         if (!p) {
3777             p = d * .3;
3778         }
3779
3780         if (!a || a < Math.abs(c)) {
3781             a = c;
3782             var s = p / 4;
3783         }
3784         else {
3785             var s = p / (2 * Math.PI) * Math.asin(c / a);
3786         }
3787
3788         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789     },
3790
3791
3792     elasticOut: function (t, b, c, d, a, p) {
3793         if (t == 0) {
3794             return b;
3795         }
3796         if ((t /= d) == 1) {
3797             return b + c;
3798         }
3799         if (!p) {
3800             p = d * .3;
3801         }
3802
3803         if (!a || a < Math.abs(c)) {
3804             a = c;
3805             var s = p / 4;
3806         }
3807         else {
3808             var s = p / (2 * Math.PI) * Math.asin(c / a);
3809         }
3810
3811         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3812     },
3813
3814
3815     elasticBoth: function (t, b, c, d, a, p) {
3816         if (t == 0) {
3817             return b;
3818         }
3819
3820         if ((t /= d / 2) == 2) {
3821             return b + c;
3822         }
3823
3824         if (!p) {
3825             p = d * (.3 * 1.5);
3826         }
3827
3828         if (!a || a < Math.abs(c)) {
3829             a = c;
3830             var s = p / 4;
3831         }
3832         else {
3833             var s = p / (2 * Math.PI) * Math.asin(c / a);
3834         }
3835
3836         if (t < 1) {
3837             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3838                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3839         }
3840         return a * Math.pow(2, -10 * (t -= 1)) *
3841                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3842     },
3843
3844
3845
3846     backIn: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3851     },
3852
3853
3854     backOut: function (t, b, c, d, s) {
3855         if (typeof s == 'undefined') {
3856             s = 1.70158;
3857         }
3858         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3859     },
3860
3861
3862     backBoth: function (t, b, c, d, s) {
3863         if (typeof s == 'undefined') {
3864             s = 1.70158;
3865         }
3866
3867         if ((t /= d / 2 ) < 1) {
3868             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3869         }
3870         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3871     },
3872
3873
3874     bounceIn: function (t, b, c, d) {
3875         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3876     },
3877
3878
3879     bounceOut: function (t, b, c, d) {
3880         if ((t /= d) < (1 / 2.75)) {
3881             return c * (7.5625 * t * t) + b;
3882         } else if (t < (2 / 2.75)) {
3883             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3884         } else if (t < (2.5 / 2.75)) {
3885             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3886         }
3887         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3888     },
3889
3890
3891     bounceBoth: function (t, b, c, d) {
3892         if (t < d / 2) {
3893             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3894         }
3895         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3896     }
3897 };/*
3898  * Portions of this file are based on pieces of Yahoo User Interface Library
3899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3900  * YUI licensed under the BSD License:
3901  * http://developer.yahoo.net/yui/license.txt
3902  * <script type="text/javascript">
3903  *
3904  */
3905     (function() {
3906         Roo.lib.Motion = function(el, attributes, duration, method) {
3907             if (el) {
3908                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3909             }
3910         };
3911
3912         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3913
3914
3915         var Y = Roo.lib;
3916         var superclass = Y.Motion.superclass;
3917         var proto = Y.Motion.prototype;
3918
3919         proto.toString = function() {
3920             var el = this.getEl();
3921             var id = el.id || el.tagName;
3922             return ("Motion " + id);
3923         };
3924
3925         proto.patterns.points = /^points$/i;
3926
3927         proto.setAttribute = function(attr, val, unit) {
3928             if (this.patterns.points.test(attr)) {
3929                 unit = unit || 'px';
3930                 superclass.setAttribute.call(this, 'left', val[0], unit);
3931                 superclass.setAttribute.call(this, 'top', val[1], unit);
3932             } else {
3933                 superclass.setAttribute.call(this, attr, val, unit);
3934             }
3935         };
3936
3937         proto.getAttribute = function(attr) {
3938             if (this.patterns.points.test(attr)) {
3939                 var val = [
3940                         superclass.getAttribute.call(this, 'left'),
3941                         superclass.getAttribute.call(this, 'top')
3942                         ];
3943             } else {
3944                 val = superclass.getAttribute.call(this, attr);
3945             }
3946
3947             return val;
3948         };
3949
3950         proto.doMethod = function(attr, start, end) {
3951             var val = null;
3952
3953             if (this.patterns.points.test(attr)) {
3954                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3955                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3956             } else {
3957                 val = superclass.doMethod.call(this, attr, start, end);
3958             }
3959             return val;
3960         };
3961
3962         proto.setRuntimeAttribute = function(attr) {
3963             if (this.patterns.points.test(attr)) {
3964                 var el = this.getEl();
3965                 var attributes = this.attributes;
3966                 var start;
3967                 var control = attributes['points']['control'] || [];
3968                 var end;
3969                 var i, len;
3970
3971                 if (control.length > 0 && !(control[0] instanceof Array)) {
3972                     control = [control];
3973                 } else {
3974                     var tmp = [];
3975                     for (i = 0,len = control.length; i < len; ++i) {
3976                         tmp[i] = control[i];
3977                     }
3978                     control = tmp;
3979                 }
3980
3981                 Roo.fly(el).position();
3982
3983                 if (isset(attributes['points']['from'])) {
3984                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3985                 }
3986                 else {
3987                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3988                 }
3989
3990                 start = this.getAttribute('points');
3991
3992
3993                 if (isset(attributes['points']['to'])) {
3994                     end = translateValues.call(this, attributes['points']['to'], start);
3995
3996                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3997                     for (i = 0,len = control.length; i < len; ++i) {
3998                         control[i] = translateValues.call(this, control[i], start);
3999                     }
4000
4001
4002                 } else if (isset(attributes['points']['by'])) {
4003                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4004
4005                     for (i = 0,len = control.length; i < len; ++i) {
4006                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4007                     }
4008                 }
4009
4010                 this.runtimeAttributes[attr] = [start];
4011
4012                 if (control.length > 0) {
4013                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4014                 }
4015
4016                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4017             }
4018             else {
4019                 superclass.setRuntimeAttribute.call(this, attr);
4020             }
4021         };
4022
4023         var translateValues = function(val, start) {
4024             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4025             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4026
4027             return val;
4028         };
4029
4030         var isset = function(prop) {
4031             return (typeof prop !== 'undefined');
4032         };
4033     })();
4034 /*
4035  * Portions of this file are based on pieces of Yahoo User Interface Library
4036  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4037  * YUI licensed under the BSD License:
4038  * http://developer.yahoo.net/yui/license.txt
4039  * <script type="text/javascript">
4040  *
4041  */
4042     (function() {
4043         Roo.lib.Scroll = function(el, attributes, duration, method) {
4044             if (el) {
4045                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4046             }
4047         };
4048
4049         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4050
4051
4052         var Y = Roo.lib;
4053         var superclass = Y.Scroll.superclass;
4054         var proto = Y.Scroll.prototype;
4055
4056         proto.toString = function() {
4057             var el = this.getEl();
4058             var id = el.id || el.tagName;
4059             return ("Scroll " + id);
4060         };
4061
4062         proto.doMethod = function(attr, start, end) {
4063             var val = null;
4064
4065             if (attr == 'scroll') {
4066                 val = [
4067                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4068                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4069                         ];
4070
4071             } else {
4072                 val = superclass.doMethod.call(this, attr, start, end);
4073             }
4074             return val;
4075         };
4076
4077         proto.getAttribute = function(attr) {
4078             var val = null;
4079             var el = this.getEl();
4080
4081             if (attr == 'scroll') {
4082                 val = [ el.scrollLeft, el.scrollTop ];
4083             } else {
4084                 val = superclass.getAttribute.call(this, attr);
4085             }
4086
4087             return val;
4088         };
4089
4090         proto.setAttribute = function(attr, val, unit) {
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 el.scrollLeft = val[0];
4095                 el.scrollTop = val[1];
4096             } else {
4097                 superclass.setAttribute.call(this, attr, val, unit);
4098             }
4099         };
4100     })();
4101 /*
4102  * Based on:
4103  * Ext JS Library 1.1.1
4104  * Copyright(c) 2006-2007, Ext JS, LLC.
4105  *
4106  * Originally Released Under LGPL - original licence link has changed is not relivant.
4107  *
4108  * Fork - LGPL
4109  * <script type="text/javascript">
4110  */
4111
4112
4113 // nasty IE9 hack - what a pile of crap that is..
4114
4115  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4116     Range.prototype.createContextualFragment = function (html) {
4117         var doc = window.document;
4118         var container = doc.createElement("div");
4119         container.innerHTML = html;
4120         var frag = doc.createDocumentFragment(), n;
4121         while ((n = container.firstChild)) {
4122             frag.appendChild(n);
4123         }
4124         return frag;
4125     };
4126 }
4127
4128 /**
4129  * @class Roo.DomHelper
4130  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4131  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4132  * @singleton
4133  */
4134 Roo.DomHelper = function(){
4135     var tempTableEl = null;
4136     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4137     var tableRe = /^table|tbody|tr|td$/i;
4138     var xmlns = {};
4139     // build as innerHTML where available
4140     /** @ignore */
4141     var createHtml = function(o){
4142         if(typeof o == 'string'){
4143             return o;
4144         }
4145         var b = "";
4146         if(!o.tag){
4147             o.tag = "div";
4148         }
4149         b += "<" + o.tag;
4150         for(var attr in o){
4151             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4152             if(attr == "style"){
4153                 var s = o["style"];
4154                 if(typeof s == "function"){
4155                     s = s.call();
4156                 }
4157                 if(typeof s == "string"){
4158                     b += ' style="' + s + '"';
4159                 }else if(typeof s == "object"){
4160                     b += ' style="';
4161                     for(var key in s){
4162                         if(typeof s[key] != "function"){
4163                             b += key + ":" + s[key] + ";";
4164                         }
4165                     }
4166                     b += '"';
4167                 }
4168             }else{
4169                 if(attr == "cls"){
4170                     b += ' class="' + o["cls"] + '"';
4171                 }else if(attr == "htmlFor"){
4172                     b += ' for="' + o["htmlFor"] + '"';
4173                 }else{
4174                     b += " " + attr + '="' + o[attr] + '"';
4175                 }
4176             }
4177         }
4178         if(emptyTags.test(o.tag)){
4179             b += "/>";
4180         }else{
4181             b += ">";
4182             var cn = o.children || o.cn;
4183             if(cn){
4184                 //http://bugs.kde.org/show_bug.cgi?id=71506
4185                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4186                     for(var i = 0, len = cn.length; i < len; i++) {
4187                         b += createHtml(cn[i], b);
4188                     }
4189                 }else{
4190                     b += createHtml(cn, b);
4191                 }
4192             }
4193             if(o.html){
4194                 b += o.html;
4195             }
4196             b += "</" + o.tag + ">";
4197         }
4198         return b;
4199     };
4200
4201     // build as dom
4202     /** @ignore */
4203     var createDom = function(o, parentNode){
4204          
4205         // defininition craeted..
4206         var ns = false;
4207         if (o.ns && o.ns != 'html') {
4208                
4209             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4210                 xmlns[o.ns] = o.xmlns;
4211                 ns = o.xmlns;
4212             }
4213             if (typeof(xmlns[o.ns]) == 'undefined') {
4214                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4215             }
4216             ns = xmlns[o.ns];
4217         }
4218         
4219         
4220         if (typeof(o) == 'string') {
4221             return parentNode.appendChild(document.createTextNode(o));
4222         }
4223         o.tag = o.tag || div;
4224         if (o.ns && Roo.isIE) {
4225             ns = false;
4226             o.tag = o.ns + ':' + o.tag;
4227             
4228         }
4229         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4230         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4231         for(var attr in o){
4232             
4233             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4234                     attr == "style" || typeof o[attr] == "function") continue;
4235                     
4236             if(attr=="cls" && Roo.isIE){
4237                 el.className = o["cls"];
4238             }else{
4239                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4240                 else el[attr] = o[attr];
4241             }
4242         }
4243         Roo.DomHelper.applyStyles(el, o.style);
4244         var cn = o.children || o.cn;
4245         if(cn){
4246             //http://bugs.kde.org/show_bug.cgi?id=71506
4247              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4248                 for(var i = 0, len = cn.length; i < len; i++) {
4249                     createDom(cn[i], el);
4250                 }
4251             }else{
4252                 createDom(cn, el);
4253             }
4254         }
4255         if(o.html){
4256             el.innerHTML = o.html;
4257         }
4258         if(parentNode){
4259            parentNode.appendChild(el);
4260         }
4261         return el;
4262     };
4263
4264     var ieTable = function(depth, s, h, e){
4265         tempTableEl.innerHTML = [s, h, e].join('');
4266         var i = -1, el = tempTableEl;
4267         while(++i < depth){
4268             el = el.firstChild;
4269         }
4270         return el;
4271     };
4272
4273     // kill repeat to save bytes
4274     var ts = '<table>',
4275         te = '</table>',
4276         tbs = ts+'<tbody>',
4277         tbe = '</tbody>'+te,
4278         trs = tbs + '<tr>',
4279         tre = '</tr>'+tbe;
4280
4281     /**
4282      * @ignore
4283      * Nasty code for IE's broken table implementation
4284      */
4285     var insertIntoTable = function(tag, where, el, html){
4286         if(!tempTableEl){
4287             tempTableEl = document.createElement('div');
4288         }
4289         var node;
4290         var before = null;
4291         if(tag == 'td'){
4292             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4293                 return;
4294             }
4295             if(where == 'beforebegin'){
4296                 before = el;
4297                 el = el.parentNode;
4298             } else{
4299                 before = el.nextSibling;
4300                 el = el.parentNode;
4301             }
4302             node = ieTable(4, trs, html, tre);
4303         }
4304         else if(tag == 'tr'){
4305             if(where == 'beforebegin'){
4306                 before = el;
4307                 el = el.parentNode;
4308                 node = ieTable(3, tbs, html, tbe);
4309             } else if(where == 'afterend'){
4310                 before = el.nextSibling;
4311                 el = el.parentNode;
4312                 node = ieTable(3, tbs, html, tbe);
4313             } else{ // INTO a TR
4314                 if(where == 'afterbegin'){
4315                     before = el.firstChild;
4316                 }
4317                 node = ieTable(4, trs, html, tre);
4318             }
4319         } else if(tag == 'tbody'){
4320             if(where == 'beforebegin'){
4321                 before = el;
4322                 el = el.parentNode;
4323                 node = ieTable(2, ts, html, te);
4324             } else if(where == 'afterend'){
4325                 before = el.nextSibling;
4326                 el = el.parentNode;
4327                 node = ieTable(2, ts, html, te);
4328             } else{
4329                 if(where == 'afterbegin'){
4330                     before = el.firstChild;
4331                 }
4332                 node = ieTable(3, tbs, html, tbe);
4333             }
4334         } else{ // TABLE
4335             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4336                 return;
4337             }
4338             if(where == 'afterbegin'){
4339                 before = el.firstChild;
4340             }
4341             node = ieTable(2, ts, html, te);
4342         }
4343         el.insertBefore(node, before);
4344         return node;
4345     };
4346
4347     return {
4348     /** True to force the use of DOM instead of html fragments @type Boolean */
4349     useDom : false,
4350
4351     /**
4352      * Returns the markup for the passed Element(s) config
4353      * @param {Object} o The Dom object spec (and children)
4354      * @return {String}
4355      */
4356     markup : function(o){
4357         return createHtml(o);
4358     },
4359
4360     /**
4361      * Applies a style specification to an element
4362      * @param {String/HTMLElement} el The element to apply styles to
4363      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4364      * a function which returns such a specification.
4365      */
4366     applyStyles : function(el, styles){
4367         if(styles){
4368            el = Roo.fly(el);
4369            if(typeof styles == "string"){
4370                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4371                var matches;
4372                while ((matches = re.exec(styles)) != null){
4373                    el.setStyle(matches[1], matches[2]);
4374                }
4375            }else if (typeof styles == "object"){
4376                for (var style in styles){
4377                   el.setStyle(style, styles[style]);
4378                }
4379            }else if (typeof styles == "function"){
4380                 Roo.DomHelper.applyStyles(el, styles.call());
4381            }
4382         }
4383     },
4384
4385     /**
4386      * Inserts an HTML fragment into the Dom
4387      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4388      * @param {HTMLElement} el The context element
4389      * @param {String} html The HTML fragmenet
4390      * @return {HTMLElement} The new node
4391      */
4392     insertHtml : function(where, el, html){
4393         where = where.toLowerCase();
4394         if(el.insertAdjacentHTML){
4395             if(tableRe.test(el.tagName)){
4396                 var rs;
4397                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4398                     return rs;
4399                 }
4400             }
4401             switch(where){
4402                 case "beforebegin":
4403                     el.insertAdjacentHTML('BeforeBegin', html);
4404                     return el.previousSibling;
4405                 case "afterbegin":
4406                     el.insertAdjacentHTML('AfterBegin', html);
4407                     return el.firstChild;
4408                 case "beforeend":
4409                     el.insertAdjacentHTML('BeforeEnd', html);
4410                     return el.lastChild;
4411                 case "afterend":
4412                     el.insertAdjacentHTML('AfterEnd', html);
4413                     return el.nextSibling;
4414             }
4415             throw 'Illegal insertion point -> "' + where + '"';
4416         }
4417         var range = el.ownerDocument.createRange();
4418         var frag;
4419         switch(where){
4420              case "beforebegin":
4421                 range.setStartBefore(el);
4422                 frag = range.createContextualFragment(html);
4423                 el.parentNode.insertBefore(frag, el);
4424                 return el.previousSibling;
4425              case "afterbegin":
4426                 if(el.firstChild){
4427                     range.setStartBefore(el.firstChild);
4428                     frag = range.createContextualFragment(html);
4429                     el.insertBefore(frag, el.firstChild);
4430                     return el.firstChild;
4431                 }else{
4432                     el.innerHTML = html;
4433                     return el.firstChild;
4434                 }
4435             case "beforeend":
4436                 if(el.lastChild){
4437                     range.setStartAfter(el.lastChild);
4438                     frag = range.createContextualFragment(html);
4439                     el.appendChild(frag);
4440                     return el.lastChild;
4441                 }else{
4442                     el.innerHTML = html;
4443                     return el.lastChild;
4444                 }
4445             case "afterend":
4446                 range.setStartAfter(el);
4447                 frag = range.createContextualFragment(html);
4448                 el.parentNode.insertBefore(frag, el.nextSibling);
4449                 return el.nextSibling;
4450             }
4451             throw 'Illegal insertion point -> "' + where + '"';
4452     },
4453
4454     /**
4455      * Creates new Dom element(s) and inserts them before el
4456      * @param {String/HTMLElement/Element} el The context element
4457      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4458      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4459      * @return {HTMLElement/Roo.Element} The new node
4460      */
4461     insertBefore : function(el, o, returnElement){
4462         return this.doInsert(el, o, returnElement, "beforeBegin");
4463     },
4464
4465     /**
4466      * Creates new Dom element(s) and inserts them after el
4467      * @param {String/HTMLElement/Element} el The context element
4468      * @param {Object} o The Dom object spec (and children)
4469      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4470      * @return {HTMLElement/Roo.Element} The new node
4471      */
4472     insertAfter : function(el, o, returnElement){
4473         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4474     },
4475
4476     /**
4477      * Creates new Dom element(s) and inserts them as the first child of el
4478      * @param {String/HTMLElement/Element} el The context element
4479      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4480      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4481      * @return {HTMLElement/Roo.Element} The new node
4482      */
4483     insertFirst : function(el, o, returnElement){
4484         return this.doInsert(el, o, returnElement, "afterBegin");
4485     },
4486
4487     // private
4488     doInsert : function(el, o, returnElement, pos, sibling){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml(pos, el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and appends them to el
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     append : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         var newNode;
4511         if(this.useDom || o.ns){
4512             newNode = createDom(o, null);
4513             el.appendChild(newNode);
4514         }else{
4515             var html = createHtml(o);
4516             newNode = this.insertHtml("beforeEnd", el, html);
4517         }
4518         return returnElement ? Roo.get(newNode, true) : newNode;
4519     },
4520
4521     /**
4522      * Creates new Dom element(s) and overwrites the contents of el with them
4523      * @param {String/HTMLElement/Element} el The context element
4524      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4525      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4526      * @return {HTMLElement/Roo.Element} The new node
4527      */
4528     overwrite : function(el, o, returnElement){
4529         el = Roo.getDom(el);
4530         if (o.ns) {
4531           
4532             while (el.childNodes.length) {
4533                 el.removeChild(el.firstChild);
4534             }
4535             createDom(o, el);
4536         } else {
4537             el.innerHTML = createHtml(o);   
4538         }
4539         
4540         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4541     },
4542
4543     /**
4544      * Creates a new Roo.DomHelper.Template from the Dom object spec
4545      * @param {Object} o The Dom object spec (and children)
4546      * @return {Roo.DomHelper.Template} The new template
4547      */
4548     createTemplate : function(o){
4549         var html = createHtml(o);
4550         return new Roo.Template(html);
4551     }
4552     };
4553 }();
4554 /*
4555  * Based on:
4556  * Ext JS Library 1.1.1
4557  * Copyright(c) 2006-2007, Ext JS, LLC.
4558  *
4559  * Originally Released Under LGPL - original licence link has changed is not relivant.
4560  *
4561  * Fork - LGPL
4562  * <script type="text/javascript">
4563  */
4564  
4565 /**
4566 * @class Roo.Template
4567 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4568 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4569 * Usage:
4570 <pre><code>
4571 var t = new Roo.Template({
4572     html :  '&lt;div name="{id}"&gt;' + 
4573         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4574         '&lt;/div&gt;',
4575     myformat: function (value, allValues) {
4576         return 'XX' + value;
4577     }
4578 });
4579 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4580 </code></pre>
4581 * For more information see this blog post with examples:
4582 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4583      - Create Elements using DOM, HTML fragments and Templates</a>. 
4584 * @constructor
4585 * @param {Object} cfg - Configuration object.
4586 */
4587 Roo.Template = function(cfg){
4588     // BC!
4589     if(cfg instanceof Array){
4590         cfg = cfg.join("");
4591     }else if(arguments.length > 1){
4592         cfg = Array.prototype.join.call(arguments, "");
4593     }
4594     
4595     
4596     if (typeof(cfg) == 'object') {
4597         Roo.apply(this,cfg)
4598     } else {
4599         // bc
4600         this.html = cfg;
4601     }
4602     if (this.url) {
4603         this.load();
4604     }
4605     
4606 };
4607 Roo.Template.prototype = {
4608     
4609     /**
4610      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4611      *                    it should be fixed so that template is observable...
4612      */
4613     url : false,
4614     /**
4615      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4616      */
4617     html : '',
4618     /**
4619      * Returns an HTML fragment of this template with the specified values applied.
4620      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4621      * @return {String} The HTML fragment
4622      */
4623     applyTemplate : function(values){
4624         try {
4625            
4626             if(this.compiled){
4627                 return this.compiled(values);
4628             }
4629             var useF = this.disableFormats !== true;
4630             var fm = Roo.util.Format, tpl = this;
4631             var fn = function(m, name, format, args){
4632                 if(format && useF){
4633                     if(format.substr(0, 5) == "this."){
4634                         return tpl.call(format.substr(5), values[name], values);
4635                     }else{
4636                         if(args){
4637                             // quoted values are required for strings in compiled templates, 
4638                             // but for non compiled we need to strip them
4639                             // quoted reversed for jsmin
4640                             var re = /^\s*['"](.*)["']\s*$/;
4641                             args = args.split(',');
4642                             for(var i = 0, len = args.length; i < len; i++){
4643                                 args[i] = args[i].replace(re, "$1");
4644                             }
4645                             args = [values[name]].concat(args);
4646                         }else{
4647                             args = [values[name]];
4648                         }
4649                         return fm[format].apply(fm, args);
4650                     }
4651                 }else{
4652                     return values[name] !== undefined ? values[name] : "";
4653                 }
4654             };
4655             return this.html.replace(this.re, fn);
4656         } catch (e) {
4657             Roo.log(e);
4658             throw e;
4659         }
4660          
4661     },
4662     
4663     loading : false,
4664       
4665     load : function ()
4666     {
4667          
4668         if (this.loading) {
4669             return;
4670         }
4671         var _t = this;
4672         
4673         this.loading = true;
4674         this.compiled = false;
4675         
4676         var cx = new Roo.data.Connection();
4677         cx.request({
4678             url : this.url,
4679             method : 'GET',
4680             success : function (response) {
4681                 _t.loading = false;
4682                 _t.html = response.responseText;
4683                 _t.url = false;
4684                 _t.compile();
4685              },
4686             failure : function(response) {
4687                 Roo.log("Template failed to load from " + _t.url);
4688                 _t.loading = false;
4689             }
4690         });
4691     },
4692
4693     /**
4694      * Sets the HTML used as the template and optionally compiles it.
4695      * @param {String} html
4696      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4697      * @return {Roo.Template} this
4698      */
4699     set : function(html, compile){
4700         this.html = html;
4701         this.compiled = null;
4702         if(compile){
4703             this.compile();
4704         }
4705         return this;
4706     },
4707     
4708     /**
4709      * True to disable format functions (defaults to false)
4710      * @type Boolean
4711      */
4712     disableFormats : false,
4713     
4714     /**
4715     * The regular expression used to match template variables 
4716     * @type RegExp
4717     * @property 
4718     */
4719     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4720     
4721     /**
4722      * Compiles the template into an internal function, eliminating the RegEx overhead.
4723      * @return {Roo.Template} this
4724      */
4725     compile : function(){
4726         var fm = Roo.util.Format;
4727         var useF = this.disableFormats !== true;
4728         var sep = Roo.isGecko ? "+" : ",";
4729         var fn = function(m, name, format, args){
4730             if(format && useF){
4731                 args = args ? ',' + args : "";
4732                 if(format.substr(0, 5) != "this."){
4733                     format = "fm." + format + '(';
4734                 }else{
4735                     format = 'this.call("'+ format.substr(5) + '", ';
4736                     args = ", values";
4737                 }
4738             }else{
4739                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4740             }
4741             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4742         };
4743         var body;
4744         // branched to use + in gecko and [].join() in others
4745         if(Roo.isGecko){
4746             body = "this.compiled = function(values){ return '" +
4747                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4748                     "';};";
4749         }else{
4750             body = ["this.compiled = function(values){ return ['"];
4751             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4752             body.push("'].join('');};");
4753             body = body.join('');
4754         }
4755         /**
4756          * eval:var:values
4757          * eval:var:fm
4758          */
4759         eval(body);
4760         return this;
4761     },
4762     
4763     // private function used to call members
4764     call : function(fnName, value, allValues){
4765         return this[fnName](value, allValues);
4766     },
4767     
4768     /**
4769      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4770      * @param {String/HTMLElement/Roo.Element} el The context element
4771      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4772      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4773      * @return {HTMLElement/Roo.Element} The new node or Element
4774      */
4775     insertFirst: function(el, values, returnElement){
4776         return this.doInsert('afterBegin', el, values, returnElement);
4777     },
4778
4779     /**
4780      * Applies the supplied values to the template and inserts the new node(s) before el.
4781      * @param {String/HTMLElement/Roo.Element} el The context element
4782      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4783      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4784      * @return {HTMLElement/Roo.Element} The new node or Element
4785      */
4786     insertBefore: function(el, values, returnElement){
4787         return this.doInsert('beforeBegin', el, values, returnElement);
4788     },
4789
4790     /**
4791      * Applies the supplied values to the template and inserts the new node(s) after el.
4792      * @param {String/HTMLElement/Roo.Element} el The context element
4793      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4794      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4795      * @return {HTMLElement/Roo.Element} The new node or Element
4796      */
4797     insertAfter : function(el, values, returnElement){
4798         return this.doInsert('afterEnd', el, values, returnElement);
4799     },
4800     
4801     /**
4802      * Applies the supplied values to the template and appends the new node(s) to el.
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     append : function(el, values, returnElement){
4809         return this.doInsert('beforeEnd', el, values, returnElement);
4810     },
4811
4812     doInsert : function(where, el, values, returnEl){
4813         el = Roo.getDom(el);
4814         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4815         return returnEl ? Roo.get(newNode, true) : newNode;
4816     },
4817
4818     /**
4819      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4820      * @param {String/HTMLElement/Roo.Element} el The context element
4821      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4822      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4823      * @return {HTMLElement/Roo.Element} The new node or Element
4824      */
4825     overwrite : function(el, values, returnElement){
4826         el = Roo.getDom(el);
4827         el.innerHTML = this.applyTemplate(values);
4828         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4829     }
4830 };
4831 /**
4832  * Alias for {@link #applyTemplate}
4833  * @method
4834  */
4835 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4836
4837 // backwards compat
4838 Roo.DomHelper.Template = Roo.Template;
4839
4840 /**
4841  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4842  * @param {String/HTMLElement} el A DOM element or its id
4843  * @returns {Roo.Template} The created template
4844  * @static
4845  */
4846 Roo.Template.from = function(el){
4847     el = Roo.getDom(el);
4848     return new Roo.Template(el.value || el.innerHTML);
4849 };/*
4850  * Based on:
4851  * Ext JS Library 1.1.1
4852  * Copyright(c) 2006-2007, Ext JS, LLC.
4853  *
4854  * Originally Released Under LGPL - original licence link has changed is not relivant.
4855  *
4856  * Fork - LGPL
4857  * <script type="text/javascript">
4858  */
4859  
4860
4861 /*
4862  * This is code is also distributed under MIT license for use
4863  * with jQuery and prototype JavaScript libraries.
4864  */
4865 /**
4866  * @class Roo.DomQuery
4867 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4868 <p>
4869 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4870
4871 <p>
4872 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4873 </p>
4874 <h4>Element Selectors:</h4>
4875 <ul class="list">
4876     <li> <b>*</b> any element</li>
4877     <li> <b>E</b> an element with the tag E</li>
4878     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4879     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4880     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4881     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4882 </ul>
4883 <h4>Attribute Selectors:</h4>
4884 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4885 <ul class="list">
4886     <li> <b>E[foo]</b> has an attribute "foo"</li>
4887     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4888     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4889     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4890     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4891     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4892     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4893 </ul>
4894 <h4>Pseudo Classes:</h4>
4895 <ul class="list">
4896     <li> <b>E:first-child</b> E is the first child of its parent</li>
4897     <li> <b>E:last-child</b> E is the last child of its parent</li>
4898     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4899     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4900     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4901     <li> <b>E:only-child</b> E is the only child of its parent</li>
4902     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4903     <li> <b>E:first</b> the first E in the resultset</li>
4904     <li> <b>E:last</b> the last E in the resultset</li>
4905     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4906     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4907     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4908     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4909     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4910     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4911     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4912     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4913     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4914 </ul>
4915 <h4>CSS Value Selectors:</h4>
4916 <ul class="list">
4917     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4918     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4919     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4920     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4921     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4922     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4923 </ul>
4924  * @singleton
4925  */
4926 Roo.DomQuery = function(){
4927     var cache = {}, simpleCache = {}, valueCache = {};
4928     var nonSpace = /\S/;
4929     var trimRe = /^\s+|\s+$/g;
4930     var tplRe = /\{(\d+)\}/g;
4931     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4932     var tagTokenRe = /^(#)?([\w-\*]+)/;
4933     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4934
4935     function child(p, index){
4936         var i = 0;
4937         var n = p.firstChild;
4938         while(n){
4939             if(n.nodeType == 1){
4940                if(++i == index){
4941                    return n;
4942                }
4943             }
4944             n = n.nextSibling;
4945         }
4946         return null;
4947     };
4948
4949     function next(n){
4950         while((n = n.nextSibling) && n.nodeType != 1);
4951         return n;
4952     };
4953
4954     function prev(n){
4955         while((n = n.previousSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function children(d){
4960         var n = d.firstChild, ni = -1;
4961             while(n){
4962                 var nx = n.nextSibling;
4963                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4964                     d.removeChild(n);
4965                 }else{
4966                     n.nodeIndex = ++ni;
4967                 }
4968                 n = nx;
4969             }
4970             return this;
4971         };
4972
4973     function byClassName(c, a, v){
4974         if(!v){
4975             return c;
4976         }
4977         var r = [], ri = -1, cn;
4978         for(var i = 0, ci; ci = c[i]; i++){
4979             if((' '+ci.className+' ').indexOf(v) != -1){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function attrValue(n, attr){
4987         if(!n.tagName && typeof n.length != "undefined"){
4988             n = n[0];
4989         }
4990         if(!n){
4991             return null;
4992         }
4993         if(attr == "for"){
4994             return n.htmlFor;
4995         }
4996         if(attr == "class" || attr == "className"){
4997             return n.className;
4998         }
4999         return n.getAttribute(attr) || n[attr];
5000
5001     };
5002
5003     function getNodes(ns, mode, tagName){
5004         var result = [], ri = -1, cs;
5005         if(!ns){
5006             return result;
5007         }
5008         tagName = tagName || "*";
5009         if(typeof ns.getElementsByTagName != "undefined"){
5010             ns = [ns];
5011         }
5012         if(!mode){
5013             for(var i = 0, ni; ni = ns[i]; i++){
5014                 cs = ni.getElementsByTagName(tagName);
5015                 for(var j = 0, ci; ci = cs[j]; j++){
5016                     result[++ri] = ci;
5017                 }
5018             }
5019         }else if(mode == "/" || mode == ">"){
5020             var utag = tagName.toUpperCase();
5021             for(var i = 0, ni, cn; ni = ns[i]; i++){
5022                 cn = ni.children || ni.childNodes;
5023                 for(var j = 0, cj; cj = cn[j]; j++){
5024                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5025                         result[++ri] = cj;
5026                     }
5027                 }
5028             }
5029         }else if(mode == "+"){
5030             var utag = tagName.toUpperCase();
5031             for(var i = 0, n; n = ns[i]; i++){
5032                 while((n = n.nextSibling) && n.nodeType != 1);
5033                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5034                     result[++ri] = n;
5035                 }
5036             }
5037         }else if(mode == "~"){
5038             for(var i = 0, n; n = ns[i]; i++){
5039                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5040                 if(n){
5041                     result[++ri] = n;
5042                 }
5043             }
5044         }
5045         return result;
5046     };
5047
5048     function concat(a, b){
5049         if(b.slice){
5050             return a.concat(b);
5051         }
5052         for(var i = 0, l = b.length; i < l; i++){
5053             a[a.length] = b[i];
5054         }
5055         return a;
5056     }
5057
5058     function byTag(cs, tagName){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!tagName){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         tagName = tagName.toLowerCase();
5067         for(var i = 0, ci; ci = cs[i]; i++){
5068             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5069                 r[++ri] = ci;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byId(cs, attr, id){
5076         if(cs.tagName || cs == document){
5077             cs = [cs];
5078         }
5079         if(!id){
5080             return cs;
5081         }
5082         var r = [], ri = -1;
5083         for(var i = 0,ci; ci = cs[i]; i++){
5084             if(ci && ci.id == id){
5085                 r[++ri] = ci;
5086                 return r;
5087             }
5088         }
5089         return r;
5090     };
5091
5092     function byAttribute(cs, attr, value, op, custom){
5093         var r = [], ri = -1, st = custom=="{";
5094         var f = Roo.DomQuery.operators[op];
5095         for(var i = 0, ci; ci = cs[i]; i++){
5096             var a;
5097             if(st){
5098                 a = Roo.DomQuery.getStyle(ci, attr);
5099             }
5100             else if(attr == "class" || attr == "className"){
5101                 a = ci.className;
5102             }else if(attr == "for"){
5103                 a = ci.htmlFor;
5104             }else if(attr == "href"){
5105                 a = ci.getAttribute("href", 2);
5106             }else{
5107                 a = ci.getAttribute(attr);
5108             }
5109             if((f && f(a, value)) || (!f && a)){
5110                 r[++ri] = ci;
5111             }
5112         }
5113         return r;
5114     };
5115
5116     function byPseudo(cs, name, value){
5117         return Roo.DomQuery.pseudos[name](cs, value);
5118     };
5119
5120     // This is for IE MSXML which does not support expandos.
5121     // IE runs the same speed using setAttribute, however FF slows way down
5122     // and Safari completely fails so they need to continue to use expandos.
5123     var isIE = window.ActiveXObject ? true : false;
5124
5125     // this eval is stop the compressor from
5126     // renaming the variable to something shorter
5127     
5128     /** eval:var:batch */
5129     var batch = 30803; 
5130
5131     var key = 30803;
5132
5133     function nodupIEXml(cs){
5134         var d = ++key;
5135         cs[0].setAttribute("_nodup", d);
5136         var r = [cs[0]];
5137         for(var i = 1, len = cs.length; i < len; i++){
5138             var c = cs[i];
5139             if(!c.getAttribute("_nodup") != d){
5140                 c.setAttribute("_nodup", d);
5141                 r[r.length] = c;
5142             }
5143         }
5144         for(var i = 0, len = cs.length; i < len; i++){
5145             cs[i].removeAttribute("_nodup");
5146         }
5147         return r;
5148     }
5149
5150     function nodup(cs){
5151         if(!cs){
5152             return [];
5153         }
5154         var len = cs.length, c, i, r = cs, cj, ri = -1;
5155         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5156             return cs;
5157         }
5158         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5159             return nodupIEXml(cs);
5160         }
5161         var d = ++key;
5162         cs[0]._nodup = d;
5163         for(i = 1; c = cs[i]; i++){
5164             if(c._nodup != d){
5165                 c._nodup = d;
5166             }else{
5167                 r = [];
5168                 for(var j = 0; j < i; j++){
5169                     r[++ri] = cs[j];
5170                 }
5171                 for(j = i+1; cj = cs[j]; j++){
5172                     if(cj._nodup != d){
5173                         cj._nodup = d;
5174                         r[++ri] = cj;
5175                     }
5176                 }
5177                 return r;
5178             }
5179         }
5180         return r;
5181     }
5182
5183     function quickDiffIEXml(c1, c2){
5184         var d = ++key;
5185         for(var i = 0, len = c1.length; i < len; i++){
5186             c1[i].setAttribute("_qdiff", d);
5187         }
5188         var r = [];
5189         for(var i = 0, len = c2.length; i < len; i++){
5190             if(c2[i].getAttribute("_qdiff") != d){
5191                 r[r.length] = c2[i];
5192             }
5193         }
5194         for(var i = 0, len = c1.length; i < len; i++){
5195            c1[i].removeAttribute("_qdiff");
5196         }
5197         return r;
5198     }
5199
5200     function quickDiff(c1, c2){
5201         var len1 = c1.length;
5202         if(!len1){
5203             return c2;
5204         }
5205         if(isIE && c1[0].selectSingleNode){
5206             return quickDiffIEXml(c1, c2);
5207         }
5208         var d = ++key;
5209         for(var i = 0; i < len1; i++){
5210             c1[i]._qdiff = d;
5211         }
5212         var r = [];
5213         for(var i = 0, len = c2.length; i < len; i++){
5214             if(c2[i]._qdiff != d){
5215                 r[r.length] = c2[i];
5216             }
5217         }
5218         return r;
5219     }
5220
5221     function quickId(ns, mode, root, id){
5222         if(ns == root){
5223            var d = root.ownerDocument || root;
5224            return d.getElementById(id);
5225         }
5226         ns = getNodes(ns, mode, "*");
5227         return byId(ns, null, id);
5228     }
5229
5230     return {
5231         getStyle : function(el, name){
5232             return Roo.fly(el).getStyle(name);
5233         },
5234         /**
5235          * Compiles a selector/xpath query into a reusable function. The returned function
5236          * takes one parameter "root" (optional), which is the context node from where the query should start.
5237          * @param {String} selector The selector/xpath query
5238          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5239          * @return {Function}
5240          */
5241         compile : function(path, type){
5242             type = type || "select";
5243             
5244             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5245             var q = path, mode, lq;
5246             var tk = Roo.DomQuery.matchers;
5247             var tklen = tk.length;
5248             var mm;
5249
5250             // accept leading mode switch
5251             var lmode = q.match(modeRe);
5252             if(lmode && lmode[1]){
5253                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5254                 q = q.replace(lmode[1], "");
5255             }
5256             // strip leading slashes
5257             while(path.substr(0, 1)=="/"){
5258                 path = path.substr(1);
5259             }
5260
5261             while(q && lq != q){
5262                 lq = q;
5263                 var tm = q.match(tagTokenRe);
5264                 if(type == "select"){
5265                     if(tm){
5266                         if(tm[1] == "#"){
5267                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5268                         }else{
5269                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5270                         }
5271                         q = q.replace(tm[0], "");
5272                     }else if(q.substr(0, 1) != '@'){
5273                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5274                     }
5275                 }else{
5276                     if(tm){
5277                         if(tm[1] == "#"){
5278                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5279                         }else{
5280                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5281                         }
5282                         q = q.replace(tm[0], "");
5283                     }
5284                 }
5285                 while(!(mm = q.match(modeRe))){
5286                     var matched = false;
5287                     for(var j = 0; j < tklen; j++){
5288                         var t = tk[j];
5289                         var m = q.match(t.re);
5290                         if(m){
5291                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5292                                                     return m[i];
5293                                                 });
5294                             q = q.replace(m[0], "");
5295                             matched = true;
5296                             break;
5297                         }
5298                     }
5299                     // prevent infinite loop on bad selector
5300                     if(!matched){
5301                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5302                     }
5303                 }
5304                 if(mm[1]){
5305                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5306                     q = q.replace(mm[1], "");
5307                 }
5308             }
5309             fn[fn.length] = "return nodup(n);\n}";
5310             
5311              /** 
5312               * list of variables that need from compression as they are used by eval.
5313              *  eval:var:batch 
5314              *  eval:var:nodup
5315              *  eval:var:byTag
5316              *  eval:var:ById
5317              *  eval:var:getNodes
5318              *  eval:var:quickId
5319              *  eval:var:mode
5320              *  eval:var:root
5321              *  eval:var:n
5322              *  eval:var:byClassName
5323              *  eval:var:byPseudo
5324              *  eval:var:byAttribute
5325              *  eval:var:attrValue
5326              * 
5327              **/ 
5328             eval(fn.join(""));
5329             return f;
5330         },
5331
5332         /**
5333          * Selects a group of elements.
5334          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5335          * @param {Node} root (optional) The start of the query (defaults to document).
5336          * @return {Array}
5337          */
5338         select : function(path, root, type){
5339             if(!root || root == document){
5340                 root = document;
5341             }
5342             if(typeof root == "string"){
5343                 root = document.getElementById(root);
5344             }
5345             var paths = path.split(",");
5346             var results = [];
5347             for(var i = 0, len = paths.length; i < len; i++){
5348                 var p = paths[i].replace(trimRe, "");
5349                 if(!cache[p]){
5350                     cache[p] = Roo.DomQuery.compile(p);
5351                     if(!cache[p]){
5352                         throw p + " is not a valid selector";
5353                     }
5354                 }
5355                 var result = cache[p](root);
5356                 if(result && result != document){
5357                     results = results.concat(result);
5358                 }
5359             }
5360             if(paths.length > 1){
5361                 return nodup(results);
5362             }
5363             return results;
5364         },
5365
5366         /**
5367          * Selects a single element.
5368          * @param {String} selector The selector/xpath query
5369          * @param {Node} root (optional) The start of the query (defaults to document).
5370          * @return {Element}
5371          */
5372         selectNode : function(path, root){
5373             return Roo.DomQuery.select(path, root)[0];
5374         },
5375
5376         /**
5377          * Selects the value of a node, optionally replacing null with the defaultValue.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {String} defaultValue
5381          */
5382         selectValue : function(path, root, defaultValue){
5383             path = path.replace(trimRe, "");
5384             if(!valueCache[path]){
5385                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5386             }
5387             var n = valueCache[path](root);
5388             n = n[0] ? n[0] : n;
5389             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5390             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5391         },
5392
5393         /**
5394          * Selects the value of a node, parsing integers and floats.
5395          * @param {String} selector The selector/xpath query
5396          * @param {Node} root (optional) The start of the query (defaults to document).
5397          * @param {Number} defaultValue
5398          * @return {Number}
5399          */
5400         selectNumber : function(path, root, defaultValue){
5401             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5402             return parseFloat(v);
5403         },
5404
5405         /**
5406          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5407          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5408          * @param {String} selector The simple selector to test
5409          * @return {Boolean}
5410          */
5411         is : function(el, ss){
5412             if(typeof el == "string"){
5413                 el = document.getElementById(el);
5414             }
5415             var isArray = (el instanceof Array);
5416             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5417             return isArray ? (result.length == el.length) : (result.length > 0);
5418         },
5419
5420         /**
5421          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5422          * @param {Array} el An array of elements to filter
5423          * @param {String} selector The simple selector to test
5424          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5425          * the selector instead of the ones that match
5426          * @return {Array}
5427          */
5428         filter : function(els, ss, nonMatches){
5429             ss = ss.replace(trimRe, "");
5430             if(!simpleCache[ss]){
5431                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5432             }
5433             var result = simpleCache[ss](els);
5434             return nonMatches ? quickDiff(result, els) : result;
5435         },
5436
5437         /**
5438          * Collection of matching regular expressions and code snippets.
5439          */
5440         matchers : [{
5441                 re: /^\.([\w-]+)/,
5442                 select: 'n = byClassName(n, null, " {1} ");'
5443             }, {
5444                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5445                 select: 'n = byPseudo(n, "{1}", "{2}");'
5446             },{
5447                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5448                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5449             }, {
5450                 re: /^#([\w-]+)/,
5451                 select: 'n = byId(n, null, "{1}");'
5452             },{
5453                 re: /^@([\w-]+)/,
5454                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5455             }
5456         ],
5457
5458         /**
5459          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5460          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5461          */
5462         operators : {
5463             "=" : function(a, v){
5464                 return a == v;
5465             },
5466             "!=" : function(a, v){
5467                 return a != v;
5468             },
5469             "^=" : function(a, v){
5470                 return a && a.substr(0, v.length) == v;
5471             },
5472             "$=" : function(a, v){
5473                 return a && a.substr(a.length-v.length) == v;
5474             },
5475             "*=" : function(a, v){
5476                 return a && a.indexOf(v) !== -1;
5477             },
5478             "%=" : function(a, v){
5479                 return (a % v) == 0;
5480             },
5481             "|=" : function(a, v){
5482                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5483             },
5484             "~=" : function(a, v){
5485                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5486             }
5487         },
5488
5489         /**
5490          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5491          * and the argument (if any) supplied in the selector.
5492          */
5493         pseudos : {
5494             "first-child" : function(c){
5495                 var r = [], ri = -1, n;
5496                 for(var i = 0, ci; ci = n = c[i]; i++){
5497                     while((n = n.previousSibling) && n.nodeType != 1);
5498                     if(!n){
5499                         r[++ri] = ci;
5500                     }
5501                 }
5502                 return r;
5503             },
5504
5505             "last-child" : function(c){
5506                 var r = [], ri = -1, n;
5507                 for(var i = 0, ci; ci = n = c[i]; i++){
5508                     while((n = n.nextSibling) && n.nodeType != 1);
5509                     if(!n){
5510                         r[++ri] = ci;
5511                     }
5512                 }
5513                 return r;
5514             },
5515
5516             "nth-child" : function(c, a) {
5517                 var r = [], ri = -1;
5518                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5519                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5520                 for(var i = 0, n; n = c[i]; i++){
5521                     var pn = n.parentNode;
5522                     if (batch != pn._batch) {
5523                         var j = 0;
5524                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5525                             if(cn.nodeType == 1){
5526                                cn.nodeIndex = ++j;
5527                             }
5528                         }
5529                         pn._batch = batch;
5530                     }
5531                     if (f == 1) {
5532                         if (l == 0 || n.nodeIndex == l){
5533                             r[++ri] = n;
5534                         }
5535                     } else if ((n.nodeIndex + l) % f == 0){
5536                         r[++ri] = n;
5537                     }
5538                 }
5539
5540                 return r;
5541             },
5542
5543             "only-child" : function(c){
5544                 var r = [], ri = -1;;
5545                 for(var i = 0, ci; ci = c[i]; i++){
5546                     if(!prev(ci) && !next(ci)){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "empty" : function(c){
5554                 var r = [], ri = -1;
5555                 for(var i = 0, ci; ci = c[i]; i++){
5556                     var cns = ci.childNodes, j = 0, cn, empty = true;
5557                     while(cn = cns[j]){
5558                         ++j;
5559                         if(cn.nodeType == 1 || cn.nodeType == 3){
5560                             empty = false;
5561                             break;
5562                         }
5563                     }
5564                     if(empty){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "contains" : function(c, v){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "nodeValue" : function(c, v){
5582                 var r = [], ri = -1;
5583                 for(var i = 0, ci; ci = c[i]; i++){
5584                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5585                         r[++ri] = ci;
5586                     }
5587                 }
5588                 return r;
5589             },
5590
5591             "checked" : function(c){
5592                 var r = [], ri = -1;
5593                 for(var i = 0, ci; ci = c[i]; i++){
5594                     if(ci.checked == true){
5595                         r[++ri] = ci;
5596                     }
5597                 }
5598                 return r;
5599             },
5600
5601             "not" : function(c, ss){
5602                 return Roo.DomQuery.filter(c, ss, true);
5603             },
5604
5605             "odd" : function(c){
5606                 return this["nth-child"](c, "odd");
5607             },
5608
5609             "even" : function(c){
5610                 return this["nth-child"](c, "even");
5611             },
5612
5613             "nth" : function(c, a){
5614                 return c[a-1] || [];
5615             },
5616
5617             "first" : function(c){
5618                 return c[0] || [];
5619             },
5620
5621             "last" : function(c){
5622                 return c[c.length-1] || [];
5623             },
5624
5625             "has" : function(c, ss){
5626                 var s = Roo.DomQuery.select;
5627                 var r = [], ri = -1;
5628                 for(var i = 0, ci; ci = c[i]; i++){
5629                     if(s(ss, ci).length > 0){
5630                         r[++ri] = ci;
5631                     }
5632                 }
5633                 return r;
5634             },
5635
5636             "next" : function(c, ss){
5637                 var is = Roo.DomQuery.is;
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     var n = next(ci);
5641                     if(n && is(n, ss)){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "prev" : function(c, ss){
5649                 var is = Roo.DomQuery.is;
5650                 var r = [], ri = -1;
5651                 for(var i = 0, ci; ci = c[i]; i++){
5652                     var n = prev(ci);
5653                     if(n && is(n, ss)){
5654                         r[++ri] = ci;
5655                     }
5656                 }
5657                 return r;
5658             }
5659         }
5660     };
5661 }();
5662
5663 /**
5664  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5665  * @param {String} path The selector/xpath query
5666  * @param {Node} root (optional) The start of the query (defaults to document).
5667  * @return {Array}
5668  * @member Roo
5669  * @method query
5670  */
5671 Roo.query = Roo.DomQuery.select;
5672 /*
5673  * Based on:
5674  * Ext JS Library 1.1.1
5675  * Copyright(c) 2006-2007, Ext JS, LLC.
5676  *
5677  * Originally Released Under LGPL - original licence link has changed is not relivant.
5678  *
5679  * Fork - LGPL
5680  * <script type="text/javascript">
5681  */
5682
5683 /**
5684  * @class Roo.util.Observable
5685  * Base class that provides a common interface for publishing events. Subclasses are expected to
5686  * to have a property "events" with all the events defined.<br>
5687  * For example:
5688  * <pre><code>
5689  Employee = function(name){
5690     this.name = name;
5691     this.addEvents({
5692         "fired" : true,
5693         "quit" : true
5694     });
5695  }
5696  Roo.extend(Employee, Roo.util.Observable);
5697 </code></pre>
5698  * @param {Object} config properties to use (incuding events / listeners)
5699  */
5700
5701 Roo.util.Observable = function(cfg){
5702     
5703     cfg = cfg|| {};
5704     this.addEvents(cfg.events || {});
5705     if (cfg.events) {
5706         delete cfg.events; // make sure
5707     }
5708      
5709     Roo.apply(this, cfg);
5710     
5711     if(this.listeners){
5712         this.on(this.listeners);
5713         delete this.listeners;
5714     }
5715 };
5716 Roo.util.Observable.prototype = {
5717     /** 
5718  * @cfg {Object} listeners  list of events and functions to call for this object, 
5719  * For example :
5720  * <pre><code>
5721     listeners :  { 
5722        'click' : function(e) {
5723            ..... 
5724         } ,
5725         .... 
5726     } 
5727   </code></pre>
5728  */
5729     
5730     
5731     /**
5732      * Fires the specified event with the passed parameters (minus the event name).
5733      * @param {String} eventName
5734      * @param {Object...} args Variable number of parameters are passed to handlers
5735      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5736      */
5737     fireEvent : function(){
5738         var ce = this.events[arguments[0].toLowerCase()];
5739         if(typeof ce == "object"){
5740             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5741         }else{
5742             return true;
5743         }
5744     },
5745
5746     // private
5747     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5748
5749     /**
5750      * Appends an event handler to this component
5751      * @param {String}   eventName The type of event to listen for
5752      * @param {Function} handler The method the event invokes
5753      * @param {Object}   scope (optional) The scope in which to execute the handler
5754      * function. The handler function's "this" context.
5755      * @param {Object}   options (optional) An object containing handler configuration
5756      * properties. This may contain any of the following properties:<ul>
5757      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5758      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5759      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5760      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5761      * by the specified number of milliseconds. If the event fires again within that time, the original
5762      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5763      * </ul><br>
5764      * <p>
5765      * <b>Combining Options</b><br>
5766      * Using the options argument, it is possible to combine different types of listeners:<br>
5767      * <br>
5768      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5769                 <pre><code>
5770                 el.on('click', this.onClick, this, {
5771                         single: true,
5772                 delay: 100,
5773                 forumId: 4
5774                 });
5775                 </code></pre>
5776      * <p>
5777      * <b>Attaching multiple handlers in 1 call</b><br>
5778      * The method also allows for a single argument to be passed which is a config object containing properties
5779      * which specify multiple handlers.
5780      * <pre><code>
5781                 el.on({
5782                         'click': {
5783                         fn: this.onClick,
5784                         scope: this,
5785                         delay: 100
5786                 }, 
5787                 'mouseover': {
5788                         fn: this.onMouseOver,
5789                         scope: this
5790                 },
5791                 'mouseout': {
5792                         fn: this.onMouseOut,
5793                         scope: this
5794                 }
5795                 });
5796                 </code></pre>
5797      * <p>
5798      * Or a shorthand syntax which passes the same scope object to all handlers:
5799         <pre><code>
5800                 el.on({
5801                         'click': this.onClick,
5802                 'mouseover': this.onMouseOver,
5803                 'mouseout': this.onMouseOut,
5804                 scope: this
5805                 });
5806                 </code></pre>
5807      */
5808     addListener : function(eventName, fn, scope, o){
5809         if(typeof eventName == "object"){
5810             o = eventName;
5811             for(var e in o){
5812                 if(this.filterOptRe.test(e)){
5813                     continue;
5814                 }
5815                 if(typeof o[e] == "function"){
5816                     // shared options
5817                     this.addListener(e, o[e], o.scope,  o);
5818                 }else{
5819                     // individual options
5820                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5821                 }
5822             }
5823             return;
5824         }
5825         o = (!o || typeof o == "boolean") ? {} : o;
5826         eventName = eventName.toLowerCase();
5827         var ce = this.events[eventName] || true;
5828         if(typeof ce == "boolean"){
5829             ce = new Roo.util.Event(this, eventName);
5830             this.events[eventName] = ce;
5831         }
5832         ce.addListener(fn, scope, o);
5833     },
5834
5835     /**
5836      * Removes a listener
5837      * @param {String}   eventName     The type of event to listen for
5838      * @param {Function} handler        The handler to remove
5839      * @param {Object}   scope  (optional) The scope (this object) for the handler
5840      */
5841     removeListener : function(eventName, fn, scope){
5842         var ce = this.events[eventName.toLowerCase()];
5843         if(typeof ce == "object"){
5844             ce.removeListener(fn, scope);
5845         }
5846     },
5847
5848     /**
5849      * Removes all listeners for this object
5850      */
5851     purgeListeners : function(){
5852         for(var evt in this.events){
5853             if(typeof this.events[evt] == "object"){
5854                  this.events[evt].clearListeners();
5855             }
5856         }
5857     },
5858
5859     relayEvents : function(o, events){
5860         var createHandler = function(ename){
5861             return function(){
5862                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5863             };
5864         };
5865         for(var i = 0, len = events.length; i < len; i++){
5866             var ename = events[i];
5867             if(!this.events[ename]){ this.events[ename] = true; };
5868             o.on(ename, createHandler(ename), this);
5869         }
5870     },
5871
5872     /**
5873      * Used to define events on this Observable
5874      * @param {Object} object The object with the events defined
5875      */
5876     addEvents : function(o){
5877         if(!this.events){
5878             this.events = {};
5879         }
5880         Roo.applyIf(this.events, o);
5881     },
5882
5883     /**
5884      * Checks to see if this object has any listeners for a specified event
5885      * @param {String} eventName The name of the event to check for
5886      * @return {Boolean} True if the event is being listened for, else false
5887      */
5888     hasListener : function(eventName){
5889         var e = this.events[eventName];
5890         return typeof e == "object" && e.listeners.length > 0;
5891     }
5892 };
5893 /**
5894  * Appends an event handler to this element (shorthand for addListener)
5895  * @param {String}   eventName     The type of event to listen for
5896  * @param {Function} handler        The method the event invokes
5897  * @param {Object}   scope (optional) The scope in which to execute the handler
5898  * function. The handler function's "this" context.
5899  * @param {Object}   options  (optional)
5900  * @method
5901  */
5902 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5903 /**
5904  * Removes a listener (shorthand for removeListener)
5905  * @param {String}   eventName     The type of event to listen for
5906  * @param {Function} handler        The handler to remove
5907  * @param {Object}   scope  (optional) The scope (this object) for the handler
5908  * @method
5909  */
5910 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5911
5912 /**
5913  * Starts capture on the specified Observable. All events will be passed
5914  * to the supplied function with the event name + standard signature of the event
5915  * <b>before</b> the event is fired. If the supplied function returns false,
5916  * the event will not fire.
5917  * @param {Observable} o The Observable to capture
5918  * @param {Function} fn The function to call
5919  * @param {Object} scope (optional) The scope (this object) for the fn
5920  * @static
5921  */
5922 Roo.util.Observable.capture = function(o, fn, scope){
5923     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5924 };
5925
5926 /**
5927  * Removes <b>all</b> added captures from the Observable.
5928  * @param {Observable} o The Observable to release
5929  * @static
5930  */
5931 Roo.util.Observable.releaseCapture = function(o){
5932     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5933 };
5934
5935 (function(){
5936
5937     var createBuffered = function(h, o, scope){
5938         var task = new Roo.util.DelayedTask();
5939         return function(){
5940             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5941         };
5942     };
5943
5944     var createSingle = function(h, e, fn, scope){
5945         return function(){
5946             e.removeListener(fn, scope);
5947             return h.apply(scope, arguments);
5948         };
5949     };
5950
5951     var createDelayed = function(h, o, scope){
5952         return function(){
5953             var args = Array.prototype.slice.call(arguments, 0);
5954             setTimeout(function(){
5955                 h.apply(scope, args);
5956             }, o.delay || 10);
5957         };
5958     };
5959
5960     Roo.util.Event = function(obj, name){
5961         this.name = name;
5962         this.obj = obj;
5963         this.listeners = [];
5964     };
5965
5966     Roo.util.Event.prototype = {
5967         addListener : function(fn, scope, options){
5968             var o = options || {};
5969             scope = scope || this.obj;
5970             if(!this.isListening(fn, scope)){
5971                 var l = {fn: fn, scope: scope, options: o};
5972                 var h = fn;
5973                 if(o.delay){
5974                     h = createDelayed(h, o, scope);
5975                 }
5976                 if(o.single){
5977                     h = createSingle(h, this, fn, scope);
5978                 }
5979                 if(o.buffer){
5980                     h = createBuffered(h, o, scope);
5981                 }
5982                 l.fireFn = h;
5983                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5984                     this.listeners.push(l);
5985                 }else{
5986                     this.listeners = this.listeners.slice(0);
5987                     this.listeners.push(l);
5988                 }
5989             }
5990         },
5991
5992         findListener : function(fn, scope){
5993             scope = scope || this.obj;
5994             var ls = this.listeners;
5995             for(var i = 0, len = ls.length; i < len; i++){
5996                 var l = ls[i];
5997                 if(l.fn == fn && l.scope == scope){
5998                     return i;
5999                 }
6000             }
6001             return -1;
6002         },
6003
6004         isListening : function(fn, scope){
6005             return this.findListener(fn, scope) != -1;
6006         },
6007
6008         removeListener : function(fn, scope){
6009             var index;
6010             if((index = this.findListener(fn, scope)) != -1){
6011                 if(!this.firing){
6012                     this.listeners.splice(index, 1);
6013                 }else{
6014                     this.listeners = this.listeners.slice(0);
6015                     this.listeners.splice(index, 1);
6016                 }
6017                 return true;
6018             }
6019             return false;
6020         },
6021
6022         clearListeners : function(){
6023             this.listeners = [];
6024         },
6025
6026         fire : function(){
6027             var ls = this.listeners, scope, len = ls.length;
6028             if(len > 0){
6029                 this.firing = true;
6030                 var args = Array.prototype.slice.call(arguments, 0);
6031                 for(var i = 0; i < len; i++){
6032                     var l = ls[i];
6033                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6034                         this.firing = false;
6035                         return false;
6036                     }
6037                 }
6038                 this.firing = false;
6039             }
6040             return true;
6041         }
6042     };
6043 })();/*
6044  * Based on:
6045  * Ext JS Library 1.1.1
6046  * Copyright(c) 2006-2007, Ext JS, LLC.
6047  *
6048  * Originally Released Under LGPL - original licence link has changed is not relivant.
6049  *
6050  * Fork - LGPL
6051  * <script type="text/javascript">
6052  */
6053
6054 /**
6055  * @class Roo.EventManager
6056  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6057  * several useful events directly.
6058  * See {@link Roo.EventObject} for more details on normalized event objects.
6059  * @singleton
6060  */
6061 Roo.EventManager = function(){
6062     var docReadyEvent, docReadyProcId, docReadyState = false;
6063     var resizeEvent, resizeTask, textEvent, textSize;
6064     var E = Roo.lib.Event;
6065     var D = Roo.lib.Dom;
6066
6067     
6068     
6069
6070     var fireDocReady = function(){
6071         if(!docReadyState){
6072             docReadyState = true;
6073             Roo.isReady = true;
6074             if(docReadyProcId){
6075                 clearInterval(docReadyProcId);
6076             }
6077             if(Roo.isGecko || Roo.isOpera) {
6078                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6079             }
6080             if(Roo.isIE){
6081                 var defer = document.getElementById("ie-deferred-loader");
6082                 if(defer){
6083                     defer.onreadystatechange = null;
6084                     defer.parentNode.removeChild(defer);
6085                 }
6086             }
6087             if(docReadyEvent){
6088                 docReadyEvent.fire();
6089                 docReadyEvent.clearListeners();
6090             }
6091         }
6092     };
6093     
6094     var initDocReady = function(){
6095         docReadyEvent = new Roo.util.Event();
6096         if(Roo.isGecko || Roo.isOpera) {
6097             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6098         }else if(Roo.isIE){
6099             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6100             var defer = document.getElementById("ie-deferred-loader");
6101             defer.onreadystatechange = function(){
6102                 if(this.readyState == "complete"){
6103                     fireDocReady();
6104                 }
6105             };
6106         }else if(Roo.isSafari){ 
6107             docReadyProcId = setInterval(function(){
6108                 var rs = document.readyState;
6109                 if(rs == "complete") {
6110                     fireDocReady();     
6111                  }
6112             }, 10);
6113         }
6114         // no matter what, make sure it fires on load
6115         E.on(window, "load", fireDocReady);
6116     };
6117
6118     var createBuffered = function(h, o){
6119         var task = new Roo.util.DelayedTask(h);
6120         return function(e){
6121             // create new event object impl so new events don't wipe out properties
6122             e = new Roo.EventObjectImpl(e);
6123             task.delay(o.buffer, h, null, [e]);
6124         };
6125     };
6126
6127     var createSingle = function(h, el, ename, fn){
6128         return function(e){
6129             Roo.EventManager.removeListener(el, ename, fn);
6130             h(e);
6131         };
6132     };
6133
6134     var createDelayed = function(h, o){
6135         return function(e){
6136             // create new event object impl so new events don't wipe out properties
6137             e = new Roo.EventObjectImpl(e);
6138             setTimeout(function(){
6139                 h(e);
6140             }, o.delay || 10);
6141         };
6142     };
6143     var transitionEndVal = false;
6144     
6145     var transitionEnd = function()
6146     {
6147         if (transitionEndVal) {
6148             return transitionEndVal;
6149         }
6150         var el = document.createElement('div');
6151
6152         var transEndEventNames = {
6153             WebkitTransition : 'webkitTransitionEnd',
6154             MozTransition    : 'transitionend',
6155             OTransition      : 'oTransitionEnd otransitionend',
6156             transition       : 'transitionend'
6157         };
6158     
6159         for (var name in transEndEventNames) {
6160             if (el.style[name] !== undefined) {
6161                 transitionEndVal = transEndEventNames[name];
6162                 return  transitionEndVal ;
6163             }
6164         }
6165     }
6166     
6167
6168     var listen = function(element, ename, opt, fn, scope){
6169         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6170         fn = fn || o.fn; scope = scope || o.scope;
6171         var el = Roo.getDom(element);
6172         
6173         
6174         if(!el){
6175             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6176         }
6177         
6178         if (ename == 'transitionend') {
6179             ename = transitionEnd();
6180         }
6181         var h = function(e){
6182             e = Roo.EventObject.setEvent(e);
6183             var t;
6184             if(o.delegate){
6185                 t = e.getTarget(o.delegate, el);
6186                 if(!t){
6187                     return;
6188                 }
6189             }else{
6190                 t = e.target;
6191             }
6192             if(o.stopEvent === true){
6193                 e.stopEvent();
6194             }
6195             if(o.preventDefault === true){
6196                e.preventDefault();
6197             }
6198             if(o.stopPropagation === true){
6199                 e.stopPropagation();
6200             }
6201
6202             if(o.normalized === false){
6203                 e = e.browserEvent;
6204             }
6205
6206             fn.call(scope || el, e, t, o);
6207         };
6208         if(o.delay){
6209             h = createDelayed(h, o);
6210         }
6211         if(o.single){
6212             h = createSingle(h, el, ename, fn);
6213         }
6214         if(o.buffer){
6215             h = createBuffered(h, o);
6216         }
6217         fn._handlers = fn._handlers || [];
6218         
6219         
6220         fn._handlers.push([Roo.id(el), ename, h]);
6221         
6222         
6223          
6224         E.on(el, ename, h);
6225         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6226             el.addEventListener("DOMMouseScroll", h, false);
6227             E.on(window, 'unload', function(){
6228                 el.removeEventListener("DOMMouseScroll", h, false);
6229             });
6230         }
6231         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6232             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6233         }
6234         return h;
6235     };
6236
6237     var stopListening = function(el, ename, fn){
6238         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6239         if(hds){
6240             for(var i = 0, len = hds.length; i < len; i++){
6241                 var h = hds[i];
6242                 if(h[0] == id && h[1] == ename){
6243                     hd = h[2];
6244                     hds.splice(i, 1);
6245                     break;
6246                 }
6247             }
6248         }
6249         E.un(el, ename, hd);
6250         el = Roo.getDom(el);
6251         if(ename == "mousewheel" && el.addEventListener){
6252             el.removeEventListener("DOMMouseScroll", hd, false);
6253         }
6254         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6255             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6256         }
6257     };
6258
6259     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6260     
6261     var pub = {
6262         
6263         
6264         /** 
6265          * Fix for doc tools
6266          * @scope Roo.EventManager
6267          */
6268         
6269         
6270         /** 
6271          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6272          * object with a Roo.EventObject
6273          * @param {Function} fn        The method the event invokes
6274          * @param {Object}   scope    An object that becomes the scope of the handler
6275          * @param {boolean}  override If true, the obj passed in becomes
6276          *                             the execution scope of the listener
6277          * @return {Function} The wrapped function
6278          * @deprecated
6279          */
6280         wrap : function(fn, scope, override){
6281             return function(e){
6282                 Roo.EventObject.setEvent(e);
6283                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6284             };
6285         },
6286         
6287         /**
6288      * Appends an event handler to an element (shorthand for addListener)
6289      * @param {String/HTMLElement}   element        The html element or id to assign the
6290      * @param {String}   eventName The type of event to listen for
6291      * @param {Function} handler The method the event invokes
6292      * @param {Object}   scope (optional) The scope in which to execute the handler
6293      * function. The handler function's "this" context.
6294      * @param {Object}   options (optional) An object containing handler configuration
6295      * properties. This may contain any of the following properties:<ul>
6296      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6297      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6298      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6299      * <li>preventDefault {Boolean} True to prevent the default action</li>
6300      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6301      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6302      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6303      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6304      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6305      * by the specified number of milliseconds. If the event fires again within that time, the original
6306      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6307      * </ul><br>
6308      * <p>
6309      * <b>Combining Options</b><br>
6310      * Using the options argument, it is possible to combine different types of listeners:<br>
6311      * <br>
6312      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6313      * Code:<pre><code>
6314 el.on('click', this.onClick, this, {
6315     single: true,
6316     delay: 100,
6317     stopEvent : true,
6318     forumId: 4
6319 });</code></pre>
6320      * <p>
6321      * <b>Attaching multiple handlers in 1 call</b><br>
6322       * The method also allows for a single argument to be passed which is a config object containing properties
6323      * which specify multiple handlers.
6324      * <p>
6325      * Code:<pre><code>
6326 el.on({
6327     'click' : {
6328         fn: this.onClick
6329         scope: this,
6330         delay: 100
6331     },
6332     'mouseover' : {
6333         fn: this.onMouseOver
6334         scope: this
6335     },
6336     'mouseout' : {
6337         fn: this.onMouseOut
6338         scope: this
6339     }
6340 });</code></pre>
6341      * <p>
6342      * Or a shorthand syntax:<br>
6343      * Code:<pre><code>
6344 el.on({
6345     'click' : this.onClick,
6346     'mouseover' : this.onMouseOver,
6347     'mouseout' : this.onMouseOut
6348     scope: this
6349 });</code></pre>
6350      */
6351         addListener : function(element, eventName, fn, scope, options){
6352             if(typeof eventName == "object"){
6353                 var o = eventName;
6354                 for(var e in o){
6355                     if(propRe.test(e)){
6356                         continue;
6357                     }
6358                     if(typeof o[e] == "function"){
6359                         // shared options
6360                         listen(element, e, o, o[e], o.scope);
6361                     }else{
6362                         // individual options
6363                         listen(element, e, o[e]);
6364                     }
6365                 }
6366                 return;
6367             }
6368             return listen(element, eventName, options, fn, scope);
6369         },
6370         
6371         /**
6372          * Removes an event handler
6373          *
6374          * @param {String/HTMLElement}   element        The id or html element to remove the 
6375          *                             event from
6376          * @param {String}   eventName     The type of event
6377          * @param {Function} fn
6378          * @return {Boolean} True if a listener was actually removed
6379          */
6380         removeListener : function(element, eventName, fn){
6381             return stopListening(element, eventName, fn);
6382         },
6383         
6384         /**
6385          * Fires when the document is ready (before onload and before images are loaded). Can be 
6386          * accessed shorthanded Roo.onReady().
6387          * @param {Function} fn        The method the event invokes
6388          * @param {Object}   scope    An  object that becomes the scope of the handler
6389          * @param {boolean}  options
6390          */
6391         onDocumentReady : function(fn, scope, options){
6392             if(docReadyState){ // if it already fired
6393                 docReadyEvent.addListener(fn, scope, options);
6394                 docReadyEvent.fire();
6395                 docReadyEvent.clearListeners();
6396                 return;
6397             }
6398             if(!docReadyEvent){
6399                 initDocReady();
6400             }
6401             docReadyEvent.addListener(fn, scope, options);
6402         },
6403         
6404         /**
6405          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6406          * @param {Function} fn        The method the event invokes
6407          * @param {Object}   scope    An object that becomes the scope of the handler
6408          * @param {boolean}  options
6409          */
6410         onWindowResize : function(fn, scope, options){
6411             if(!resizeEvent){
6412                 resizeEvent = new Roo.util.Event();
6413                 resizeTask = new Roo.util.DelayedTask(function(){
6414                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6415                 });
6416                 E.on(window, "resize", function(){
6417                     if(Roo.isIE){
6418                         resizeTask.delay(50);
6419                     }else{
6420                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6421                     }
6422                 });
6423             }
6424             resizeEvent.addListener(fn, scope, options);
6425         },
6426
6427         /**
6428          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6429          * @param {Function} fn        The method the event invokes
6430          * @param {Object}   scope    An object that becomes the scope of the handler
6431          * @param {boolean}  options
6432          */
6433         onTextResize : function(fn, scope, options){
6434             if(!textEvent){
6435                 textEvent = new Roo.util.Event();
6436                 var textEl = new Roo.Element(document.createElement('div'));
6437                 textEl.dom.className = 'x-text-resize';
6438                 textEl.dom.innerHTML = 'X';
6439                 textEl.appendTo(document.body);
6440                 textSize = textEl.dom.offsetHeight;
6441                 setInterval(function(){
6442                     if(textEl.dom.offsetHeight != textSize){
6443                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6444                     }
6445                 }, this.textResizeInterval);
6446             }
6447             textEvent.addListener(fn, scope, options);
6448         },
6449
6450         /**
6451          * Removes the passed window resize listener.
6452          * @param {Function} fn        The method the event invokes
6453          * @param {Object}   scope    The scope of handler
6454          */
6455         removeResizeListener : function(fn, scope){
6456             if(resizeEvent){
6457                 resizeEvent.removeListener(fn, scope);
6458             }
6459         },
6460
6461         // private
6462         fireResize : function(){
6463             if(resizeEvent){
6464                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6465             }   
6466         },
6467         /**
6468          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6469          */
6470         ieDeferSrc : false,
6471         /**
6472          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6473          */
6474         textResizeInterval : 50
6475     };
6476     
6477     /**
6478      * Fix for doc tools
6479      * @scopeAlias pub=Roo.EventManager
6480      */
6481     
6482      /**
6483      * Appends an event handler to an element (shorthand for addListener)
6484      * @param {String/HTMLElement}   element        The html element or id to assign the
6485      * @param {String}   eventName The type of event to listen for
6486      * @param {Function} handler The method the event invokes
6487      * @param {Object}   scope (optional) The scope in which to execute the handler
6488      * function. The handler function's "this" context.
6489      * @param {Object}   options (optional) An object containing handler configuration
6490      * properties. This may contain any of the following properties:<ul>
6491      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6492      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6493      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6494      * <li>preventDefault {Boolean} True to prevent the default action</li>
6495      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6496      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6497      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6498      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6499      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6500      * by the specified number of milliseconds. If the event fires again within that time, the original
6501      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6502      * </ul><br>
6503      * <p>
6504      * <b>Combining Options</b><br>
6505      * Using the options argument, it is possible to combine different types of listeners:<br>
6506      * <br>
6507      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6508      * Code:<pre><code>
6509 el.on('click', this.onClick, this, {
6510     single: true,
6511     delay: 100,
6512     stopEvent : true,
6513     forumId: 4
6514 });</code></pre>
6515      * <p>
6516      * <b>Attaching multiple handlers in 1 call</b><br>
6517       * The method also allows for a single argument to be passed which is a config object containing properties
6518      * which specify multiple handlers.
6519      * <p>
6520      * Code:<pre><code>
6521 el.on({
6522     'click' : {
6523         fn: this.onClick
6524         scope: this,
6525         delay: 100
6526     },
6527     'mouseover' : {
6528         fn: this.onMouseOver
6529         scope: this
6530     },
6531     'mouseout' : {
6532         fn: this.onMouseOut
6533         scope: this
6534     }
6535 });</code></pre>
6536      * <p>
6537      * Or a shorthand syntax:<br>
6538      * Code:<pre><code>
6539 el.on({
6540     'click' : this.onClick,
6541     'mouseover' : this.onMouseOver,
6542     'mouseout' : this.onMouseOut
6543     scope: this
6544 });</code></pre>
6545      */
6546     pub.on = pub.addListener;
6547     pub.un = pub.removeListener;
6548
6549     pub.stoppedMouseDownEvent = new Roo.util.Event();
6550     return pub;
6551 }();
6552 /**
6553   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6554   * @param {Function} fn        The method the event invokes
6555   * @param {Object}   scope    An  object that becomes the scope of the handler
6556   * @param {boolean}  override If true, the obj passed in becomes
6557   *                             the execution scope of the listener
6558   * @member Roo
6559   * @method onReady
6560  */
6561 Roo.onReady = Roo.EventManager.onDocumentReady;
6562
6563 Roo.onReady(function(){
6564     var bd = Roo.get(document.body);
6565     if(!bd){ return; }
6566
6567     var cls = [
6568             Roo.isIE ? "roo-ie"
6569             : Roo.isGecko ? "roo-gecko"
6570             : Roo.isOpera ? "roo-opera"
6571             : Roo.isSafari ? "roo-safari" : ""];
6572
6573     if(Roo.isMac){
6574         cls.push("roo-mac");
6575     }
6576     if(Roo.isLinux){
6577         cls.push("roo-linux");
6578     }
6579     if(Roo.isBorderBox){
6580         cls.push('roo-border-box');
6581     }
6582     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6583         var p = bd.dom.parentNode;
6584         if(p){
6585             p.className += ' roo-strict';
6586         }
6587     }
6588     bd.addClass(cls.join(' '));
6589 });
6590
6591 /**
6592  * @class Roo.EventObject
6593  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6594  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6595  * Example:
6596  * <pre><code>
6597  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6598     e.preventDefault();
6599     var target = e.getTarget();
6600     ...
6601  }
6602  var myDiv = Roo.get("myDiv");
6603  myDiv.on("click", handleClick);
6604  //or
6605  Roo.EventManager.on("myDiv", 'click', handleClick);
6606  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6607  </code></pre>
6608  * @singleton
6609  */
6610 Roo.EventObject = function(){
6611     
6612     var E = Roo.lib.Event;
6613     
6614     // safari keypress events for special keys return bad keycodes
6615     var safariKeys = {
6616         63234 : 37, // left
6617         63235 : 39, // right
6618         63232 : 38, // up
6619         63233 : 40, // down
6620         63276 : 33, // page up
6621         63277 : 34, // page down
6622         63272 : 46, // delete
6623         63273 : 36, // home
6624         63275 : 35  // end
6625     };
6626
6627     // normalize button clicks
6628     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6629                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6630
6631     Roo.EventObjectImpl = function(e){
6632         if(e){
6633             this.setEvent(e.browserEvent || e);
6634         }
6635     };
6636     Roo.EventObjectImpl.prototype = {
6637         /**
6638          * Used to fix doc tools.
6639          * @scope Roo.EventObject.prototype
6640          */
6641             
6642
6643         
6644         
6645         /** The normal browser event */
6646         browserEvent : null,
6647         /** The button pressed in a mouse event */
6648         button : -1,
6649         /** True if the shift key was down during the event */
6650         shiftKey : false,
6651         /** True if the control key was down during the event */
6652         ctrlKey : false,
6653         /** True if the alt key was down during the event */
6654         altKey : false,
6655
6656         /** Key constant 
6657         * @type Number */
6658         BACKSPACE : 8,
6659         /** Key constant 
6660         * @type Number */
6661         TAB : 9,
6662         /** Key constant 
6663         * @type Number */
6664         RETURN : 13,
6665         /** Key constant 
6666         * @type Number */
6667         ENTER : 13,
6668         /** Key constant 
6669         * @type Number */
6670         SHIFT : 16,
6671         /** Key constant 
6672         * @type Number */
6673         CONTROL : 17,
6674         /** Key constant 
6675         * @type Number */
6676         ESC : 27,
6677         /** Key constant 
6678         * @type Number */
6679         SPACE : 32,
6680         /** Key constant 
6681         * @type Number */
6682         PAGEUP : 33,
6683         /** Key constant 
6684         * @type Number */
6685         PAGEDOWN : 34,
6686         /** Key constant 
6687         * @type Number */
6688         END : 35,
6689         /** Key constant 
6690         * @type Number */
6691         HOME : 36,
6692         /** Key constant 
6693         * @type Number */
6694         LEFT : 37,
6695         /** Key constant 
6696         * @type Number */
6697         UP : 38,
6698         /** Key constant 
6699         * @type Number */
6700         RIGHT : 39,
6701         /** Key constant 
6702         * @type Number */
6703         DOWN : 40,
6704         /** Key constant 
6705         * @type Number */
6706         DELETE : 46,
6707         /** Key constant 
6708         * @type Number */
6709         F5 : 116,
6710
6711            /** @private */
6712         setEvent : function(e){
6713             if(e == this || (e && e.browserEvent)){ // already wrapped
6714                 return e;
6715             }
6716             this.browserEvent = e;
6717             if(e){
6718                 // normalize buttons
6719                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6720                 if(e.type == 'click' && this.button == -1){
6721                     this.button = 0;
6722                 }
6723                 this.type = e.type;
6724                 this.shiftKey = e.shiftKey;
6725                 // mac metaKey behaves like ctrlKey
6726                 this.ctrlKey = e.ctrlKey || e.metaKey;
6727                 this.altKey = e.altKey;
6728                 // in getKey these will be normalized for the mac
6729                 this.keyCode = e.keyCode;
6730                 // keyup warnings on firefox.
6731                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6732                 // cache the target for the delayed and or buffered events
6733                 this.target = E.getTarget(e);
6734                 // same for XY
6735                 this.xy = E.getXY(e);
6736             }else{
6737                 this.button = -1;
6738                 this.shiftKey = false;
6739                 this.ctrlKey = false;
6740                 this.altKey = false;
6741                 this.keyCode = 0;
6742                 this.charCode =0;
6743                 this.target = null;
6744                 this.xy = [0, 0];
6745             }
6746             return this;
6747         },
6748
6749         /**
6750          * Stop the event (preventDefault and stopPropagation)
6751          */
6752         stopEvent : function(){
6753             if(this.browserEvent){
6754                 if(this.browserEvent.type == 'mousedown'){
6755                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6756                 }
6757                 E.stopEvent(this.browserEvent);
6758             }
6759         },
6760
6761         /**
6762          * Prevents the browsers default handling of the event.
6763          */
6764         preventDefault : function(){
6765             if(this.browserEvent){
6766                 E.preventDefault(this.browserEvent);
6767             }
6768         },
6769
6770         /** @private */
6771         isNavKeyPress : function(){
6772             var k = this.keyCode;
6773             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6774             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6775         },
6776
6777         isSpecialKey : function(){
6778             var k = this.keyCode;
6779             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6780             (k == 16) || (k == 17) ||
6781             (k >= 18 && k <= 20) ||
6782             (k >= 33 && k <= 35) ||
6783             (k >= 36 && k <= 39) ||
6784             (k >= 44 && k <= 45);
6785         },
6786         /**
6787          * Cancels bubbling of the event.
6788          */
6789         stopPropagation : function(){
6790             if(this.browserEvent){
6791                 if(this.type == 'mousedown'){
6792                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6793                 }
6794                 E.stopPropagation(this.browserEvent);
6795             }
6796         },
6797
6798         /**
6799          * Gets the key code for the event.
6800          * @return {Number}
6801          */
6802         getCharCode : function(){
6803             return this.charCode || this.keyCode;
6804         },
6805
6806         /**
6807          * Returns a normalized keyCode for the event.
6808          * @return {Number} The key code
6809          */
6810         getKey : function(){
6811             var k = this.keyCode || this.charCode;
6812             return Roo.isSafari ? (safariKeys[k] || k) : k;
6813         },
6814
6815         /**
6816          * Gets the x coordinate of the event.
6817          * @return {Number}
6818          */
6819         getPageX : function(){
6820             return this.xy[0];
6821         },
6822
6823         /**
6824          * Gets the y coordinate of the event.
6825          * @return {Number}
6826          */
6827         getPageY : function(){
6828             return this.xy[1];
6829         },
6830
6831         /**
6832          * Gets the time of the event.
6833          * @return {Number}
6834          */
6835         getTime : function(){
6836             if(this.browserEvent){
6837                 return E.getTime(this.browserEvent);
6838             }
6839             return null;
6840         },
6841
6842         /**
6843          * Gets the page coordinates of the event.
6844          * @return {Array} The xy values like [x, y]
6845          */
6846         getXY : function(){
6847             return this.xy;
6848         },
6849
6850         /**
6851          * Gets the target for the event.
6852          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6853          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6854                 search as a number or element (defaults to 10 || document.body)
6855          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6856          * @return {HTMLelement}
6857          */
6858         getTarget : function(selector, maxDepth, returnEl){
6859             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6860         },
6861         /**
6862          * Gets the related target.
6863          * @return {HTMLElement}
6864          */
6865         getRelatedTarget : function(){
6866             if(this.browserEvent){
6867                 return E.getRelatedTarget(this.browserEvent);
6868             }
6869             return null;
6870         },
6871
6872         /**
6873          * Normalizes mouse wheel delta across browsers
6874          * @return {Number} The delta
6875          */
6876         getWheelDelta : function(){
6877             var e = this.browserEvent;
6878             var delta = 0;
6879             if(e.wheelDelta){ /* IE/Opera. */
6880                 delta = e.wheelDelta/120;
6881             }else if(e.detail){ /* Mozilla case. */
6882                 delta = -e.detail/3;
6883             }
6884             return delta;
6885         },
6886
6887         /**
6888          * Returns true if the control, meta, shift or alt key was pressed during this event.
6889          * @return {Boolean}
6890          */
6891         hasModifier : function(){
6892             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6893         },
6894
6895         /**
6896          * Returns true if the target of this event equals el or is a child of el
6897          * @param {String/HTMLElement/Element} el
6898          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6899          * @return {Boolean}
6900          */
6901         within : function(el, related){
6902             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6903             return t && Roo.fly(el).contains(t);
6904         },
6905
6906         getPoint : function(){
6907             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6908         }
6909     };
6910
6911     return new Roo.EventObjectImpl();
6912 }();
6913             
6914     /*
6915  * Based on:
6916  * Ext JS Library 1.1.1
6917  * Copyright(c) 2006-2007, Ext JS, LLC.
6918  *
6919  * Originally Released Under LGPL - original licence link has changed is not relivant.
6920  *
6921  * Fork - LGPL
6922  * <script type="text/javascript">
6923  */
6924
6925  
6926 // was in Composite Element!??!?!
6927  
6928 (function(){
6929     var D = Roo.lib.Dom;
6930     var E = Roo.lib.Event;
6931     var A = Roo.lib.Anim;
6932
6933     // local style camelizing for speed
6934     var propCache = {};
6935     var camelRe = /(-[a-z])/gi;
6936     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6937     var view = document.defaultView;
6938
6939 /**
6940  * @class Roo.Element
6941  * Represents an Element in the DOM.<br><br>
6942  * Usage:<br>
6943 <pre><code>
6944 var el = Roo.get("my-div");
6945
6946 // or with getEl
6947 var el = getEl("my-div");
6948
6949 // or with a DOM element
6950 var el = Roo.get(myDivElement);
6951 </code></pre>
6952  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6953  * each call instead of constructing a new one.<br><br>
6954  * <b>Animations</b><br />
6955  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6956  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6957 <pre>
6958 Option    Default   Description
6959 --------- --------  ---------------------------------------------
6960 duration  .35       The duration of the animation in seconds
6961 easing    easeOut   The YUI easing method
6962 callback  none      A function to execute when the anim completes
6963 scope     this      The scope (this) of the callback function
6964 </pre>
6965 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6966 * manipulate the animation. Here's an example:
6967 <pre><code>
6968 var el = Roo.get("my-div");
6969
6970 // no animation
6971 el.setWidth(100);
6972
6973 // default animation
6974 el.setWidth(100, true);
6975
6976 // animation with some options set
6977 el.setWidth(100, {
6978     duration: 1,
6979     callback: this.foo,
6980     scope: this
6981 });
6982
6983 // using the "anim" property to get the Anim object
6984 var opt = {
6985     duration: 1,
6986     callback: this.foo,
6987     scope: this
6988 };
6989 el.setWidth(100, opt);
6990 ...
6991 if(opt.anim.isAnimated()){
6992     opt.anim.stop();
6993 }
6994 </code></pre>
6995 * <b> Composite (Collections of) Elements</b><br />
6996  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6997  * @constructor Create a new Element directly.
6998  * @param {String/HTMLElement} element
6999  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7000  */
7001     Roo.Element = function(element, forceNew){
7002         var dom = typeof element == "string" ?
7003                 document.getElementById(element) : element;
7004         if(!dom){ // invalid id/element
7005             return null;
7006         }
7007         var id = dom.id;
7008         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7009             return Roo.Element.cache[id];
7010         }
7011
7012         /**
7013          * The DOM element
7014          * @type HTMLElement
7015          */
7016         this.dom = dom;
7017
7018         /**
7019          * The DOM element ID
7020          * @type String
7021          */
7022         this.id = id || Roo.id(dom);
7023     };
7024
7025     var El = Roo.Element;
7026
7027     El.prototype = {
7028         /**
7029          * The element's default display mode  (defaults to "")
7030          * @type String
7031          */
7032         originalDisplay : "",
7033
7034         visibilityMode : 1,
7035         /**
7036          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7037          * @type String
7038          */
7039         defaultUnit : "px",
7040         /**
7041          * Sets the element's visibility mode. When setVisible() is called it
7042          * will use this to determine whether to set the visibility or the display property.
7043          * @param visMode Element.VISIBILITY or Element.DISPLAY
7044          * @return {Roo.Element} this
7045          */
7046         setVisibilityMode : function(visMode){
7047             this.visibilityMode = visMode;
7048             return this;
7049         },
7050         /**
7051          * Convenience method for setVisibilityMode(Element.DISPLAY)
7052          * @param {String} display (optional) What to set display to when visible
7053          * @return {Roo.Element} this
7054          */
7055         enableDisplayMode : function(display){
7056             this.setVisibilityMode(El.DISPLAY);
7057             if(typeof display != "undefined") this.originalDisplay = display;
7058             return this;
7059         },
7060
7061         /**
7062          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7063          * @param {String} selector The simple selector to test
7064          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7065                 search as a number or element (defaults to 10 || document.body)
7066          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7067          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7068          */
7069         findParent : function(simpleSelector, maxDepth, returnEl){
7070             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7071             maxDepth = maxDepth || 50;
7072             if(typeof maxDepth != "number"){
7073                 stopEl = Roo.getDom(maxDepth);
7074                 maxDepth = 10;
7075             }
7076             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7077                 if(dq.is(p, simpleSelector)){
7078                     return returnEl ? Roo.get(p) : p;
7079                 }
7080                 depth++;
7081                 p = p.parentNode;
7082             }
7083             return null;
7084         },
7085
7086
7087         /**
7088          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7089          * @param {String} selector The simple selector to test
7090          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7091                 search as a number or element (defaults to 10 || document.body)
7092          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7093          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7094          */
7095         findParentNode : function(simpleSelector, maxDepth, returnEl){
7096             var p = Roo.fly(this.dom.parentNode, '_internal');
7097             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7098         },
7099
7100         /**
7101          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7102          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7103          * @param {String} selector The simple selector to test
7104          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7105                 search as a number or element (defaults to 10 || document.body)
7106          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7107          */
7108         up : function(simpleSelector, maxDepth){
7109             return this.findParentNode(simpleSelector, maxDepth, true);
7110         },
7111
7112
7113
7114         /**
7115          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7116          * @param {String} selector The simple selector to test
7117          * @return {Boolean} True if this element matches the selector, else false
7118          */
7119         is : function(simpleSelector){
7120             return Roo.DomQuery.is(this.dom, simpleSelector);
7121         },
7122
7123         /**
7124          * Perform animation on this element.
7125          * @param {Object} args The YUI animation control args
7126          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7127          * @param {Function} onComplete (optional) Function to call when animation completes
7128          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7129          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7130          * @return {Roo.Element} this
7131          */
7132         animate : function(args, duration, onComplete, easing, animType){
7133             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7134             return this;
7135         },
7136
7137         /*
7138          * @private Internal animation call
7139          */
7140         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7141             animType = animType || 'run';
7142             opt = opt || {};
7143             var anim = Roo.lib.Anim[animType](
7144                 this.dom, args,
7145                 (opt.duration || defaultDur) || .35,
7146                 (opt.easing || defaultEase) || 'easeOut',
7147                 function(){
7148                     Roo.callback(cb, this);
7149                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7150                 },
7151                 this
7152             );
7153             opt.anim = anim;
7154             return anim;
7155         },
7156
7157         // private legacy anim prep
7158         preanim : function(a, i){
7159             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7160         },
7161
7162         /**
7163          * Removes worthless text nodes
7164          * @param {Boolean} forceReclean (optional) By default the element
7165          * keeps track if it has been cleaned already so
7166          * you can call this over and over. However, if you update the element and
7167          * need to force a reclean, you can pass true.
7168          */
7169         clean : function(forceReclean){
7170             if(this.isCleaned && forceReclean !== true){
7171                 return this;
7172             }
7173             var ns = /\S/;
7174             var d = this.dom, n = d.firstChild, ni = -1;
7175             while(n){
7176                 var nx = n.nextSibling;
7177                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7178                     d.removeChild(n);
7179                 }else{
7180                     n.nodeIndex = ++ni;
7181                 }
7182                 n = nx;
7183             }
7184             this.isCleaned = true;
7185             return this;
7186         },
7187
7188         // private
7189         calcOffsetsTo : function(el){
7190             el = Roo.get(el);
7191             var d = el.dom;
7192             var restorePos = false;
7193             if(el.getStyle('position') == 'static'){
7194                 el.position('relative');
7195                 restorePos = true;
7196             }
7197             var x = 0, y =0;
7198             var op = this.dom;
7199             while(op && op != d && op.tagName != 'HTML'){
7200                 x+= op.offsetLeft;
7201                 y+= op.offsetTop;
7202                 op = op.offsetParent;
7203             }
7204             if(restorePos){
7205                 el.position('static');
7206             }
7207             return [x, y];
7208         },
7209
7210         /**
7211          * Scrolls this element into view within the passed container.
7212          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7213          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7214          * @return {Roo.Element} this
7215          */
7216         scrollIntoView : function(container, hscroll){
7217             var c = Roo.getDom(container) || document.body;
7218             var el = this.dom;
7219
7220             var o = this.calcOffsetsTo(c),
7221                 l = o[0],
7222                 t = o[1],
7223                 b = t+el.offsetHeight,
7224                 r = l+el.offsetWidth;
7225
7226             var ch = c.clientHeight;
7227             var ct = parseInt(c.scrollTop, 10);
7228             var cl = parseInt(c.scrollLeft, 10);
7229             var cb = ct + ch;
7230             var cr = cl + c.clientWidth;
7231
7232             if(t < ct){
7233                 c.scrollTop = t;
7234             }else if(b > cb){
7235                 c.scrollTop = b-ch;
7236             }
7237
7238             if(hscroll !== false){
7239                 if(l < cl){
7240                     c.scrollLeft = l;
7241                 }else if(r > cr){
7242                     c.scrollLeft = r-c.clientWidth;
7243                 }
7244             }
7245             return this;
7246         },
7247
7248         // private
7249         scrollChildIntoView : function(child, hscroll){
7250             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7251         },
7252
7253         /**
7254          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7255          * the new height may not be available immediately.
7256          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7257          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7258          * @param {Function} onComplete (optional) Function to call when animation completes
7259          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7260          * @return {Roo.Element} this
7261          */
7262         autoHeight : function(animate, duration, onComplete, easing){
7263             var oldHeight = this.getHeight();
7264             this.clip();
7265             this.setHeight(1); // force clipping
7266             setTimeout(function(){
7267                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7268                 if(!animate){
7269                     this.setHeight(height);
7270                     this.unclip();
7271                     if(typeof onComplete == "function"){
7272                         onComplete();
7273                     }
7274                 }else{
7275                     this.setHeight(oldHeight); // restore original height
7276                     this.setHeight(height, animate, duration, function(){
7277                         this.unclip();
7278                         if(typeof onComplete == "function") onComplete();
7279                     }.createDelegate(this), easing);
7280                 }
7281             }.createDelegate(this), 0);
7282             return this;
7283         },
7284
7285         /**
7286          * Returns true if this element is an ancestor of the passed element
7287          * @param {HTMLElement/String} el The element to check
7288          * @return {Boolean} True if this element is an ancestor of el, else false
7289          */
7290         contains : function(el){
7291             if(!el){return false;}
7292             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7293         },
7294
7295         /**
7296          * Checks whether the element is currently visible using both visibility and display properties.
7297          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7298          * @return {Boolean} True if the element is currently visible, else false
7299          */
7300         isVisible : function(deep) {
7301             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7302             if(deep !== true || !vis){
7303                 return vis;
7304             }
7305             var p = this.dom.parentNode;
7306             while(p && p.tagName.toLowerCase() != "body"){
7307                 if(!Roo.fly(p, '_isVisible').isVisible()){
7308                     return false;
7309                 }
7310                 p = p.parentNode;
7311             }
7312             return true;
7313         },
7314
7315         /**
7316          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7317          * @param {String} selector The CSS selector
7318          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7319          * @return {CompositeElement/CompositeElementLite} The composite element
7320          */
7321         select : function(selector, unique){
7322             return El.select(selector, unique, this.dom);
7323         },
7324
7325         /**
7326          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7327          * @param {String} selector The CSS selector
7328          * @return {Array} An array of the matched nodes
7329          */
7330         query : function(selector, unique){
7331             return Roo.DomQuery.select(selector, this.dom);
7332         },
7333
7334         /**
7335          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7336          * @param {String} selector The CSS selector
7337          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7338          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7339          */
7340         child : function(selector, returnDom){
7341             var n = Roo.DomQuery.selectNode(selector, this.dom);
7342             return returnDom ? n : Roo.get(n);
7343         },
7344
7345         /**
7346          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7347          * @param {String} selector The CSS selector
7348          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7349          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7350          */
7351         down : function(selector, returnDom){
7352             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7353             return returnDom ? n : Roo.get(n);
7354         },
7355
7356         /**
7357          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7358          * @param {String} group The group the DD object is member of
7359          * @param {Object} config The DD config object
7360          * @param {Object} overrides An object containing methods to override/implement on the DD object
7361          * @return {Roo.dd.DD} The DD object
7362          */
7363         initDD : function(group, config, overrides){
7364             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7365             return Roo.apply(dd, overrides);
7366         },
7367
7368         /**
7369          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7370          * @param {String} group The group the DDProxy object is member of
7371          * @param {Object} config The DDProxy config object
7372          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7373          * @return {Roo.dd.DDProxy} The DDProxy object
7374          */
7375         initDDProxy : function(group, config, overrides){
7376             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7377             return Roo.apply(dd, overrides);
7378         },
7379
7380         /**
7381          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7382          * @param {String} group The group the DDTarget object is member of
7383          * @param {Object} config The DDTarget config object
7384          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7385          * @return {Roo.dd.DDTarget} The DDTarget object
7386          */
7387         initDDTarget : function(group, config, overrides){
7388             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7389             return Roo.apply(dd, overrides);
7390         },
7391
7392         /**
7393          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7394          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7395          * @param {Boolean} visible Whether the element is visible
7396          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7397          * @return {Roo.Element} this
7398          */
7399          setVisible : function(visible, animate){
7400             if(!animate || !A){
7401                 if(this.visibilityMode == El.DISPLAY){
7402                     this.setDisplayed(visible);
7403                 }else{
7404                     this.fixDisplay();
7405                     this.dom.style.visibility = visible ? "visible" : "hidden";
7406                 }
7407             }else{
7408                 // closure for composites
7409                 var dom = this.dom;
7410                 var visMode = this.visibilityMode;
7411                 if(visible){
7412                     this.setOpacity(.01);
7413                     this.setVisible(true);
7414                 }
7415                 this.anim({opacity: { to: (visible?1:0) }},
7416                       this.preanim(arguments, 1),
7417                       null, .35, 'easeIn', function(){
7418                          if(!visible){
7419                              if(visMode == El.DISPLAY){
7420                                  dom.style.display = "none";
7421                              }else{
7422                                  dom.style.visibility = "hidden";
7423                              }
7424                              Roo.get(dom).setOpacity(1);
7425                          }
7426                      });
7427             }
7428             return this;
7429         },
7430
7431         /**
7432          * Returns true if display is not "none"
7433          * @return {Boolean}
7434          */
7435         isDisplayed : function() {
7436             return this.getStyle("display") != "none";
7437         },
7438
7439         /**
7440          * Toggles the element's visibility or display, depending on visibility mode.
7441          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7442          * @return {Roo.Element} this
7443          */
7444         toggle : function(animate){
7445             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7446             return this;
7447         },
7448
7449         /**
7450          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7451          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7452          * @return {Roo.Element} this
7453          */
7454         setDisplayed : function(value) {
7455             if(typeof value == "boolean"){
7456                value = value ? this.originalDisplay : "none";
7457             }
7458             this.setStyle("display", value);
7459             return this;
7460         },
7461
7462         /**
7463          * Tries to focus the element. Any exceptions are caught and ignored.
7464          * @return {Roo.Element} this
7465          */
7466         focus : function() {
7467             try{
7468                 this.dom.focus();
7469             }catch(e){}
7470             return this;
7471         },
7472
7473         /**
7474          * Tries to blur the element. Any exceptions are caught and ignored.
7475          * @return {Roo.Element} this
7476          */
7477         blur : function() {
7478             try{
7479                 this.dom.blur();
7480             }catch(e){}
7481             return this;
7482         },
7483
7484         /**
7485          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7486          * @param {String/Array} className The CSS class to add, or an array of classes
7487          * @return {Roo.Element} this
7488          */
7489         addClass : function(className){
7490             if(className instanceof Array){
7491                 for(var i = 0, len = className.length; i < len; i++) {
7492                     this.addClass(className[i]);
7493                 }
7494             }else{
7495                 if(className && !this.hasClass(className)){
7496                     this.dom.className = this.dom.className + " " + className;
7497                 }
7498             }
7499             return this;
7500         },
7501
7502         /**
7503          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7504          * @param {String/Array} className The CSS class to add, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         radioClass : function(className){
7508             var siblings = this.dom.parentNode.childNodes;
7509             for(var i = 0; i < siblings.length; i++) {
7510                 var s = siblings[i];
7511                 if(s.nodeType == 1){
7512                     Roo.get(s).removeClass(className);
7513                 }
7514             }
7515             this.addClass(className);
7516             return this;
7517         },
7518
7519         /**
7520          * Removes one or more CSS classes from the element.
7521          * @param {String/Array} className The CSS class to remove, or an array of classes
7522          * @return {Roo.Element} this
7523          */
7524         removeClass : function(className){
7525             if(!className || !this.dom.className){
7526                 return this;
7527             }
7528             if(className instanceof Array){
7529                 for(var i = 0, len = className.length; i < len; i++) {
7530                     this.removeClass(className[i]);
7531                 }
7532             }else{
7533                 if(this.hasClass(className)){
7534                     var re = this.classReCache[className];
7535                     if (!re) {
7536                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7537                        this.classReCache[className] = re;
7538                     }
7539                     this.dom.className =
7540                         this.dom.className.replace(re, " ");
7541                 }
7542             }
7543             return this;
7544         },
7545
7546         // private
7547         classReCache: {},
7548
7549         /**
7550          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7551          * @param {String} className The CSS class to toggle
7552          * @return {Roo.Element} this
7553          */
7554         toggleClass : function(className){
7555             if(this.hasClass(className)){
7556                 this.removeClass(className);
7557             }else{
7558                 this.addClass(className);
7559             }
7560             return this;
7561         },
7562
7563         /**
7564          * Checks if the specified CSS class exists on this element's DOM node.
7565          * @param {String} className The CSS class to check for
7566          * @return {Boolean} True if the class exists, else false
7567          */
7568         hasClass : function(className){
7569             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7570         },
7571
7572         /**
7573          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7574          * @param {String} oldClassName The CSS class to replace
7575          * @param {String} newClassName The replacement CSS class
7576          * @return {Roo.Element} this
7577          */
7578         replaceClass : function(oldClassName, newClassName){
7579             this.removeClass(oldClassName);
7580             this.addClass(newClassName);
7581             return this;
7582         },
7583
7584         /**
7585          * Returns an object with properties matching the styles requested.
7586          * For example, el.getStyles('color', 'font-size', 'width') might return
7587          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7588          * @param {String} style1 A style name
7589          * @param {String} style2 A style name
7590          * @param {String} etc.
7591          * @return {Object} The style object
7592          */
7593         getStyles : function(){
7594             var a = arguments, len = a.length, r = {};
7595             for(var i = 0; i < len; i++){
7596                 r[a[i]] = this.getStyle(a[i]);
7597             }
7598             return r;
7599         },
7600
7601         /**
7602          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7603          * @param {String} property The style property whose value is returned.
7604          * @return {String} The current value of the style property for this element.
7605          */
7606         getStyle : function(){
7607             return view && view.getComputedStyle ?
7608                 function(prop){
7609                     var el = this.dom, v, cs, camel;
7610                     if(prop == 'float'){
7611                         prop = "cssFloat";
7612                     }
7613                     if(el.style && (v = el.style[prop])){
7614                         return v;
7615                     }
7616                     if(cs = view.getComputedStyle(el, "")){
7617                         if(!(camel = propCache[prop])){
7618                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7619                         }
7620                         return cs[camel];
7621                     }
7622                     return null;
7623                 } :
7624                 function(prop){
7625                     var el = this.dom, v, cs, camel;
7626                     if(prop == 'opacity'){
7627                         if(typeof el.style.filter == 'string'){
7628                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7629                             if(m){
7630                                 var fv = parseFloat(m[1]);
7631                                 if(!isNaN(fv)){
7632                                     return fv ? fv / 100 : 0;
7633                                 }
7634                             }
7635                         }
7636                         return 1;
7637                     }else if(prop == 'float'){
7638                         prop = "styleFloat";
7639                     }
7640                     if(!(camel = propCache[prop])){
7641                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7642                     }
7643                     if(v = el.style[camel]){
7644                         return v;
7645                     }
7646                     if(cs = el.currentStyle){
7647                         return cs[camel];
7648                     }
7649                     return null;
7650                 };
7651         }(),
7652
7653         /**
7654          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7655          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7656          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7657          * @return {Roo.Element} this
7658          */
7659         setStyle : function(prop, value){
7660             if(typeof prop == "string"){
7661                 
7662                 if (prop == 'float') {
7663                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7664                     return this;
7665                 }
7666                 
7667                 var camel;
7668                 if(!(camel = propCache[prop])){
7669                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7670                 }
7671                 
7672                 if(camel == 'opacity') {
7673                     this.setOpacity(value);
7674                 }else{
7675                     this.dom.style[camel] = value;
7676                 }
7677             }else{
7678                 for(var style in prop){
7679                     if(typeof prop[style] != "function"){
7680                        this.setStyle(style, prop[style]);
7681                     }
7682                 }
7683             }
7684             return this;
7685         },
7686
7687         /**
7688          * More flexible version of {@link #setStyle} for setting style properties.
7689          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7690          * a function which returns such a specification.
7691          * @return {Roo.Element} this
7692          */
7693         applyStyles : function(style){
7694             Roo.DomHelper.applyStyles(this.dom, style);
7695             return this;
7696         },
7697
7698         /**
7699           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7700           * @return {Number} The X position of the element
7701           */
7702         getX : function(){
7703             return D.getX(this.dom);
7704         },
7705
7706         /**
7707           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7708           * @return {Number} The Y position of the element
7709           */
7710         getY : function(){
7711             return D.getY(this.dom);
7712         },
7713
7714         /**
7715           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7716           * @return {Array} The XY position of the element
7717           */
7718         getXY : function(){
7719             return D.getXY(this.dom);
7720         },
7721
7722         /**
7723          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7724          * @param {Number} The X position of the element
7725          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7726          * @return {Roo.Element} this
7727          */
7728         setX : function(x, animate){
7729             if(!animate || !A){
7730                 D.setX(this.dom, x);
7731             }else{
7732                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7733             }
7734             return this;
7735         },
7736
7737         /**
7738          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Number} The Y position of the element
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setY : function(y, animate){
7744             if(!animate || !A){
7745                 D.setY(this.dom, y);
7746             }else{
7747                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7754          * @param {String} left The left CSS property value
7755          * @return {Roo.Element} this
7756          */
7757         setLeft : function(left){
7758             this.setStyle("left", this.addUnits(left));
7759             return this;
7760         },
7761
7762         /**
7763          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7764          * @param {String} top The top CSS property value
7765          * @return {Roo.Element} this
7766          */
7767         setTop : function(top){
7768             this.setStyle("top", this.addUnits(top));
7769             return this;
7770         },
7771
7772         /**
7773          * Sets the element's CSS right style.
7774          * @param {String} right The right CSS property value
7775          * @return {Roo.Element} this
7776          */
7777         setRight : function(right){
7778             this.setStyle("right", this.addUnits(right));
7779             return this;
7780         },
7781
7782         /**
7783          * Sets the element's CSS bottom style.
7784          * @param {String} bottom The bottom CSS property value
7785          * @return {Roo.Element} this
7786          */
7787         setBottom : function(bottom){
7788             this.setStyle("bottom", this.addUnits(bottom));
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7794          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7795          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setXY : function(pos, animate){
7800             if(!animate || !A){
7801                 D.setXY(this.dom, pos);
7802             }else{
7803                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7804             }
7805             return this;
7806         },
7807
7808         /**
7809          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7810          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7811          * @param {Number} x X value for new position (coordinates are page-based)
7812          * @param {Number} y Y value for new position (coordinates are page-based)
7813          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7814          * @return {Roo.Element} this
7815          */
7816         setLocation : function(x, y, animate){
7817             this.setXY([x, y], this.preanim(arguments, 2));
7818             return this;
7819         },
7820
7821         /**
7822          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7823          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7824          * @param {Number} x X value for new position (coordinates are page-based)
7825          * @param {Number} y Y value for new position (coordinates are page-based)
7826          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7827          * @return {Roo.Element} this
7828          */
7829         moveTo : function(x, y, animate){
7830             this.setXY([x, y], this.preanim(arguments, 2));
7831             return this;
7832         },
7833
7834         /**
7835          * Returns the region of the given element.
7836          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7837          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7838          */
7839         getRegion : function(){
7840             return D.getRegion(this.dom);
7841         },
7842
7843         /**
7844          * Returns the offset height of the element
7845          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7846          * @return {Number} The element's height
7847          */
7848         getHeight : function(contentHeight){
7849             var h = this.dom.offsetHeight || 0;
7850             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7851         },
7852
7853         /**
7854          * Returns the offset width of the element
7855          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7856          * @return {Number} The element's width
7857          */
7858         getWidth : function(contentWidth){
7859             var w = this.dom.offsetWidth || 0;
7860             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7861         },
7862
7863         /**
7864          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7865          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7866          * if a height has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedHeight : function(){
7870             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7871             if(!h){
7872                 h = parseInt(this.getStyle('height'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     h += this.getFrameWidth('tb');
7875                 }
7876             }
7877             return h;
7878         },
7879
7880         /**
7881          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7882          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7883          * if a width has not been set using CSS.
7884          * @return {Number}
7885          */
7886         getComputedWidth : function(){
7887             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7888             if(!w){
7889                 w = parseInt(this.getStyle('width'), 10) || 0;
7890                 if(!this.isBorderBox()){
7891                     w += this.getFrameWidth('lr');
7892                 }
7893             }
7894             return w;
7895         },
7896
7897         /**
7898          * Returns the size of the element.
7899          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7900          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7901          */
7902         getSize : function(contentSize){
7903             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7904         },
7905
7906         /**
7907          * Returns the width and height of the viewport.
7908          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7909          */
7910         getViewSize : function(){
7911             var d = this.dom, doc = document, aw = 0, ah = 0;
7912             if(d == doc || d == doc.body){
7913                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7914             }else{
7915                 return {
7916                     width : d.clientWidth,
7917                     height: d.clientHeight
7918                 };
7919             }
7920         },
7921
7922         /**
7923          * Returns the value of the "value" attribute
7924          * @param {Boolean} asNumber true to parse the value as a number
7925          * @return {String/Number}
7926          */
7927         getValue : function(asNumber){
7928             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7929         },
7930
7931         // private
7932         adjustWidth : function(width){
7933             if(typeof width == "number"){
7934                 if(this.autoBoxAdjust && !this.isBorderBox()){
7935                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7936                 }
7937                 if(width < 0){
7938                     width = 0;
7939                 }
7940             }
7941             return width;
7942         },
7943
7944         // private
7945         adjustHeight : function(height){
7946             if(typeof height == "number"){
7947                if(this.autoBoxAdjust && !this.isBorderBox()){
7948                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7949                }
7950                if(height < 0){
7951                    height = 0;
7952                }
7953             }
7954             return height;
7955         },
7956
7957         /**
7958          * Set the width of the element
7959          * @param {Number} width The new width
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setWidth : function(width, animate){
7964             width = this.adjustWidth(width);
7965             if(!animate || !A){
7966                 this.dom.style.width = this.addUnits(width);
7967             }else{
7968                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Set the height of the element
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setHeight : function(height, animate){
7980             height = this.adjustHeight(height);
7981             if(!animate || !A){
7982                 this.dom.style.height = this.addUnits(height);
7983             }else{
7984                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7985             }
7986             return this;
7987         },
7988
7989         /**
7990          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7991          * @param {Number} width The new width
7992          * @param {Number} height The new height
7993          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7994          * @return {Roo.Element} this
7995          */
7996          setSize : function(width, height, animate){
7997             if(typeof width == "object"){ // in case of object from getSize()
7998                 height = width.height; width = width.width;
7999             }
8000             width = this.adjustWidth(width); height = this.adjustHeight(height);
8001             if(!animate || !A){
8002                 this.dom.style.width = this.addUnits(width);
8003                 this.dom.style.height = this.addUnits(height);
8004             }else{
8005                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8006             }
8007             return this;
8008         },
8009
8010         /**
8011          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8012          * @param {Number} x X value for new position (coordinates are page-based)
8013          * @param {Number} y Y value for new position (coordinates are page-based)
8014          * @param {Number} width The new width
8015          * @param {Number} height The new height
8016          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8017          * @return {Roo.Element} this
8018          */
8019         setBounds : function(x, y, width, height, animate){
8020             if(!animate || !A){
8021                 this.setSize(width, height);
8022                 this.setLocation(x, y);
8023             }else{
8024                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8025                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8026                               this.preanim(arguments, 4), 'motion');
8027             }
8028             return this;
8029         },
8030
8031         /**
8032          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8033          * @param {Roo.lib.Region} region The region to fill
8034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8035          * @return {Roo.Element} this
8036          */
8037         setRegion : function(region, animate){
8038             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8039             return this;
8040         },
8041
8042         /**
8043          * Appends an event handler
8044          *
8045          * @param {String}   eventName     The type of event to append
8046          * @param {Function} fn        The method the event invokes
8047          * @param {Object} scope       (optional) The scope (this object) of the fn
8048          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8049          */
8050         addListener : function(eventName, fn, scope, options){
8051             if (this.dom) {
8052                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8053             }
8054         },
8055
8056         /**
8057          * Removes an event handler from this element
8058          * @param {String} eventName the type of event to remove
8059          * @param {Function} fn the method the event invokes
8060          * @return {Roo.Element} this
8061          */
8062         removeListener : function(eventName, fn){
8063             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8064             return this;
8065         },
8066
8067         /**
8068          * Removes all previous added listeners from this element
8069          * @return {Roo.Element} this
8070          */
8071         removeAllListeners : function(){
8072             E.purgeElement(this.dom);
8073             return this;
8074         },
8075
8076         relayEvent : function(eventName, observable){
8077             this.on(eventName, function(e){
8078                 observable.fireEvent(eventName, e);
8079             });
8080         },
8081
8082         /**
8083          * Set the opacity of the element
8084          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8085          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8086          * @return {Roo.Element} this
8087          */
8088          setOpacity : function(opacity, animate){
8089             if(!animate || !A){
8090                 var s = this.dom.style;
8091                 if(Roo.isIE){
8092                     s.zoom = 1;
8093                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8094                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8095                 }else{
8096                     s.opacity = opacity;
8097                 }
8098             }else{
8099                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8100             }
8101             return this;
8102         },
8103
8104         /**
8105          * Gets the left X coordinate
8106          * @param {Boolean} local True to get the local css position instead of page coordinate
8107          * @return {Number}
8108          */
8109         getLeft : function(local){
8110             if(!local){
8111                 return this.getX();
8112             }else{
8113                 return parseInt(this.getStyle("left"), 10) || 0;
8114             }
8115         },
8116
8117         /**
8118          * Gets the right X coordinate of the element (element X position + element width)
8119          * @param {Boolean} local True to get the local css position instead of page coordinate
8120          * @return {Number}
8121          */
8122         getRight : function(local){
8123             if(!local){
8124                 return this.getX() + this.getWidth();
8125             }else{
8126                 return (this.getLeft(true) + this.getWidth()) || 0;
8127             }
8128         },
8129
8130         /**
8131          * Gets the top Y coordinate
8132          * @param {Boolean} local True to get the local css position instead of page coordinate
8133          * @return {Number}
8134          */
8135         getTop : function(local) {
8136             if(!local){
8137                 return this.getY();
8138             }else{
8139                 return parseInt(this.getStyle("top"), 10) || 0;
8140             }
8141         },
8142
8143         /**
8144          * Gets the bottom Y coordinate of the element (element Y position + element height)
8145          * @param {Boolean} local True to get the local css position instead of page coordinate
8146          * @return {Number}
8147          */
8148         getBottom : function(local){
8149             if(!local){
8150                 return this.getY() + this.getHeight();
8151             }else{
8152                 return (this.getTop(true) + this.getHeight()) || 0;
8153             }
8154         },
8155
8156         /**
8157         * Initializes positioning on this element. If a desired position is not passed, it will make the
8158         * the element positioned relative IF it is not already positioned.
8159         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8160         * @param {Number} zIndex (optional) The zIndex to apply
8161         * @param {Number} x (optional) Set the page X position
8162         * @param {Number} y (optional) Set the page Y position
8163         */
8164         position : function(pos, zIndex, x, y){
8165             if(!pos){
8166                if(this.getStyle('position') == 'static'){
8167                    this.setStyle('position', 'relative');
8168                }
8169             }else{
8170                 this.setStyle("position", pos);
8171             }
8172             if(zIndex){
8173                 this.setStyle("z-index", zIndex);
8174             }
8175             if(x !== undefined && y !== undefined){
8176                 this.setXY([x, y]);
8177             }else if(x !== undefined){
8178                 this.setX(x);
8179             }else if(y !== undefined){
8180                 this.setY(y);
8181             }
8182         },
8183
8184         /**
8185         * Clear positioning back to the default when the document was loaded
8186         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8187         * @return {Roo.Element} this
8188          */
8189         clearPositioning : function(value){
8190             value = value ||'';
8191             this.setStyle({
8192                 "left": value,
8193                 "right": value,
8194                 "top": value,
8195                 "bottom": value,
8196                 "z-index": "",
8197                 "position" : "static"
8198             });
8199             return this;
8200         },
8201
8202         /**
8203         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8204         * snapshot before performing an update and then restoring the element.
8205         * @return {Object}
8206         */
8207         getPositioning : function(){
8208             var l = this.getStyle("left");
8209             var t = this.getStyle("top");
8210             return {
8211                 "position" : this.getStyle("position"),
8212                 "left" : l,
8213                 "right" : l ? "" : this.getStyle("right"),
8214                 "top" : t,
8215                 "bottom" : t ? "" : this.getStyle("bottom"),
8216                 "z-index" : this.getStyle("z-index")
8217             };
8218         },
8219
8220         /**
8221          * Gets the width of the border(s) for the specified side(s)
8222          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8223          * passing lr would get the border (l)eft width + the border (r)ight width.
8224          * @return {Number} The width of the sides passed added together
8225          */
8226         getBorderWidth : function(side){
8227             return this.addStyles(side, El.borders);
8228         },
8229
8230         /**
8231          * Gets the width of the padding(s) for the specified side(s)
8232          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8233          * passing lr would get the padding (l)eft + the padding (r)ight.
8234          * @return {Number} The padding of the sides passed added together
8235          */
8236         getPadding : function(side){
8237             return this.addStyles(side, El.paddings);
8238         },
8239
8240         /**
8241         * Set positioning with an object returned by getPositioning().
8242         * @param {Object} posCfg
8243         * @return {Roo.Element} this
8244          */
8245         setPositioning : function(pc){
8246             this.applyStyles(pc);
8247             if(pc.right == "auto"){
8248                 this.dom.style.right = "";
8249             }
8250             if(pc.bottom == "auto"){
8251                 this.dom.style.bottom = "";
8252             }
8253             return this;
8254         },
8255
8256         // private
8257         fixDisplay : function(){
8258             if(this.getStyle("display") == "none"){
8259                 this.setStyle("visibility", "hidden");
8260                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8261                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8262                     this.setStyle("display", "block");
8263                 }
8264             }
8265         },
8266
8267         /**
8268          * Quick set left and top adding default units
8269          * @param {String} left The left CSS property value
8270          * @param {String} top The top CSS property value
8271          * @return {Roo.Element} this
8272          */
8273          setLeftTop : function(left, top){
8274             this.dom.style.left = this.addUnits(left);
8275             this.dom.style.top = this.addUnits(top);
8276             return this;
8277         },
8278
8279         /**
8280          * Move this element relative to its current position.
8281          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8282          * @param {Number} distance How far to move the element in pixels
8283          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8284          * @return {Roo.Element} this
8285          */
8286          move : function(direction, distance, animate){
8287             var xy = this.getXY();
8288             direction = direction.toLowerCase();
8289             switch(direction){
8290                 case "l":
8291                 case "left":
8292                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8293                     break;
8294                case "r":
8295                case "right":
8296                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8297                     break;
8298                case "t":
8299                case "top":
8300                case "up":
8301                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8302                     break;
8303                case "b":
8304                case "bottom":
8305                case "down":
8306                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8307                     break;
8308             }
8309             return this;
8310         },
8311
8312         /**
8313          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8314          * @return {Roo.Element} this
8315          */
8316         clip : function(){
8317             if(!this.isClipped){
8318                this.isClipped = true;
8319                this.originalClip = {
8320                    "o": this.getStyle("overflow"),
8321                    "x": this.getStyle("overflow-x"),
8322                    "y": this.getStyle("overflow-y")
8323                };
8324                this.setStyle("overflow", "hidden");
8325                this.setStyle("overflow-x", "hidden");
8326                this.setStyle("overflow-y", "hidden");
8327             }
8328             return this;
8329         },
8330
8331         /**
8332          *  Return clipping (overflow) to original clipping before clip() was called
8333          * @return {Roo.Element} this
8334          */
8335         unclip : function(){
8336             if(this.isClipped){
8337                 this.isClipped = false;
8338                 var o = this.originalClip;
8339                 if(o.o){this.setStyle("overflow", o.o);}
8340                 if(o.x){this.setStyle("overflow-x", o.x);}
8341                 if(o.y){this.setStyle("overflow-y", o.y);}
8342             }
8343             return this;
8344         },
8345
8346
8347         /**
8348          * Gets the x,y coordinates specified by the anchor position on the element.
8349          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8350          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8351          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8352          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8353          * @return {Array} [x, y] An array containing the element's x and y coordinates
8354          */
8355         getAnchorXY : function(anchor, local, s){
8356             //Passing a different size is useful for pre-calculating anchors,
8357             //especially for anchored animations that change the el size.
8358
8359             var w, h, vp = false;
8360             if(!s){
8361                 var d = this.dom;
8362                 if(d == document.body || d == document){
8363                     vp = true;
8364                     w = D.getViewWidth(); h = D.getViewHeight();
8365                 }else{
8366                     w = this.getWidth(); h = this.getHeight();
8367                 }
8368             }else{
8369                 w = s.width;  h = s.height;
8370             }
8371             var x = 0, y = 0, r = Math.round;
8372             switch((anchor || "tl").toLowerCase()){
8373                 case "c":
8374                     x = r(w*.5);
8375                     y = r(h*.5);
8376                 break;
8377                 case "t":
8378                     x = r(w*.5);
8379                     y = 0;
8380                 break;
8381                 case "l":
8382                     x = 0;
8383                     y = r(h*.5);
8384                 break;
8385                 case "r":
8386                     x = w;
8387                     y = r(h*.5);
8388                 break;
8389                 case "b":
8390                     x = r(w*.5);
8391                     y = h;
8392                 break;
8393                 case "tl":
8394                     x = 0;
8395                     y = 0;
8396                 break;
8397                 case "bl":
8398                     x = 0;
8399                     y = h;
8400                 break;
8401                 case "br":
8402                     x = w;
8403                     y = h;
8404                 break;
8405                 case "tr":
8406                     x = w;
8407                     y = 0;
8408                 break;
8409             }
8410             if(local === true){
8411                 return [x, y];
8412             }
8413             if(vp){
8414                 var sc = this.getScroll();
8415                 return [x + sc.left, y + sc.top];
8416             }
8417             //Add the element's offset xy
8418             var o = this.getXY();
8419             return [x+o[0], y+o[1]];
8420         },
8421
8422         /**
8423          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8424          * supported position values.
8425          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8426          * @param {String} position The position to align to.
8427          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8428          * @return {Array} [x, y]
8429          */
8430         getAlignToXY : function(el, p, o){
8431             el = Roo.get(el);
8432             var d = this.dom;
8433             if(!el.dom){
8434                 throw "Element.alignTo with an element that doesn't exist";
8435             }
8436             var c = false; //constrain to viewport
8437             var p1 = "", p2 = "";
8438             o = o || [0,0];
8439
8440             if(!p){
8441                 p = "tl-bl";
8442             }else if(p == "?"){
8443                 p = "tl-bl?";
8444             }else if(p.indexOf("-") == -1){
8445                 p = "tl-" + p;
8446             }
8447             p = p.toLowerCase();
8448             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8449             if(!m){
8450                throw "Element.alignTo with an invalid alignment " + p;
8451             }
8452             p1 = m[1]; p2 = m[2]; c = !!m[3];
8453
8454             //Subtract the aligned el's internal xy from the target's offset xy
8455             //plus custom offset to get the aligned el's new offset xy
8456             var a1 = this.getAnchorXY(p1, true);
8457             var a2 = el.getAnchorXY(p2, false);
8458             var x = a2[0] - a1[0] + o[0];
8459             var y = a2[1] - a1[1] + o[1];
8460             if(c){
8461                 //constrain the aligned el to viewport if necessary
8462                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8463                 // 5px of margin for ie
8464                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8465
8466                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8467                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8468                 //otherwise swap the aligned el to the opposite border of the target.
8469                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8470                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8471                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8472                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8473
8474                var doc = document;
8475                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8476                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8477
8478                if((x+w) > dw + scrollX){
8479                     x = swapX ? r.left-w : dw+scrollX-w;
8480                 }
8481                if(x < scrollX){
8482                    x = swapX ? r.right : scrollX;
8483                }
8484                if((y+h) > dh + scrollY){
8485                     y = swapY ? r.top-h : dh+scrollY-h;
8486                 }
8487                if (y < scrollY){
8488                    y = swapY ? r.bottom : scrollY;
8489                }
8490             }
8491             return [x,y];
8492         },
8493
8494         // private
8495         getConstrainToXY : function(){
8496             var os = {top:0, left:0, bottom:0, right: 0};
8497
8498             return function(el, local, offsets, proposedXY){
8499                 el = Roo.get(el);
8500                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8501
8502                 var vw, vh, vx = 0, vy = 0;
8503                 if(el.dom == document.body || el.dom == document){
8504                     vw = Roo.lib.Dom.getViewWidth();
8505                     vh = Roo.lib.Dom.getViewHeight();
8506                 }else{
8507                     vw = el.dom.clientWidth;
8508                     vh = el.dom.clientHeight;
8509                     if(!local){
8510                         var vxy = el.getXY();
8511                         vx = vxy[0];
8512                         vy = vxy[1];
8513                     }
8514                 }
8515
8516                 var s = el.getScroll();
8517
8518                 vx += offsets.left + s.left;
8519                 vy += offsets.top + s.top;
8520
8521                 vw -= offsets.right;
8522                 vh -= offsets.bottom;
8523
8524                 var vr = vx+vw;
8525                 var vb = vy+vh;
8526
8527                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8528                 var x = xy[0], y = xy[1];
8529                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8530
8531                 // only move it if it needs it
8532                 var moved = false;
8533
8534                 // first validate right/bottom
8535                 if((x + w) > vr){
8536                     x = vr - w;
8537                     moved = true;
8538                 }
8539                 if((y + h) > vb){
8540                     y = vb - h;
8541                     moved = true;
8542                 }
8543                 // then make sure top/left isn't negative
8544                 if(x < vx){
8545                     x = vx;
8546                     moved = true;
8547                 }
8548                 if(y < vy){
8549                     y = vy;
8550                     moved = true;
8551                 }
8552                 return moved ? [x, y] : false;
8553             };
8554         }(),
8555
8556         // private
8557         adjustForConstraints : function(xy, parent, offsets){
8558             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8559         },
8560
8561         /**
8562          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8563          * document it aligns it to the viewport.
8564          * The position parameter is optional, and can be specified in any one of the following formats:
8565          * <ul>
8566          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8567          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8568          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8569          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8570          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8571          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8572          * </ul>
8573          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8574          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8575          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8576          * that specified in order to enforce the viewport constraints.
8577          * Following are all of the supported anchor positions:
8578     <pre>
8579     Value  Description
8580     -----  -----------------------------
8581     tl     The top left corner (default)
8582     t      The center of the top edge
8583     tr     The top right corner
8584     l      The center of the left edge
8585     c      In the center of the element
8586     r      The center of the right edge
8587     bl     The bottom left corner
8588     b      The center of the bottom edge
8589     br     The bottom right corner
8590     </pre>
8591     Example Usage:
8592     <pre><code>
8593     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8594     el.alignTo("other-el");
8595
8596     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8597     el.alignTo("other-el", "tr?");
8598
8599     // align the bottom right corner of el with the center left edge of other-el
8600     el.alignTo("other-el", "br-l?");
8601
8602     // align the center of el with the bottom left corner of other-el and
8603     // adjust the x position by -6 pixels (and the y position by 0)
8604     el.alignTo("other-el", "c-bl", [-6, 0]);
8605     </code></pre>
8606          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8607          * @param {String} position The position to align to.
8608          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8610          * @return {Roo.Element} this
8611          */
8612         alignTo : function(element, position, offsets, animate){
8613             var xy = this.getAlignToXY(element, position, offsets);
8614             this.setXY(xy, this.preanim(arguments, 3));
8615             return this;
8616         },
8617
8618         /**
8619          * Anchors an element to another element and realigns it when the window is resized.
8620          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8621          * @param {String} position The position to align to.
8622          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8623          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8624          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8625          * is a number, it is used as the buffer delay (defaults to 50ms).
8626          * @param {Function} callback The function to call after the animation finishes
8627          * @return {Roo.Element} this
8628          */
8629         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8630             var action = function(){
8631                 this.alignTo(el, alignment, offsets, animate);
8632                 Roo.callback(callback, this);
8633             };
8634             Roo.EventManager.onWindowResize(action, this);
8635             var tm = typeof monitorScroll;
8636             if(tm != 'undefined'){
8637                 Roo.EventManager.on(window, 'scroll', action, this,
8638                     {buffer: tm == 'number' ? monitorScroll : 50});
8639             }
8640             action.call(this); // align immediately
8641             return this;
8642         },
8643         /**
8644          * Clears any opacity settings from this element. Required in some cases for IE.
8645          * @return {Roo.Element} this
8646          */
8647         clearOpacity : function(){
8648             if (window.ActiveXObject) {
8649                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8650                     this.dom.style.filter = "";
8651                 }
8652             } else {
8653                 this.dom.style.opacity = "";
8654                 this.dom.style["-moz-opacity"] = "";
8655                 this.dom.style["-khtml-opacity"] = "";
8656             }
8657             return this;
8658         },
8659
8660         /**
8661          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8662          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8663          * @return {Roo.Element} this
8664          */
8665         hide : function(animate){
8666             this.setVisible(false, this.preanim(arguments, 0));
8667             return this;
8668         },
8669
8670         /**
8671         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8672         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8673          * @return {Roo.Element} this
8674          */
8675         show : function(animate){
8676             this.setVisible(true, this.preanim(arguments, 0));
8677             return this;
8678         },
8679
8680         /**
8681          * @private Test if size has a unit, otherwise appends the default
8682          */
8683         addUnits : function(size){
8684             return Roo.Element.addUnits(size, this.defaultUnit);
8685         },
8686
8687         /**
8688          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8689          * @return {Roo.Element} this
8690          */
8691         beginMeasure : function(){
8692             var el = this.dom;
8693             if(el.offsetWidth || el.offsetHeight){
8694                 return this; // offsets work already
8695             }
8696             var changed = [];
8697             var p = this.dom, b = document.body; // start with this element
8698             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8699                 var pe = Roo.get(p);
8700                 if(pe.getStyle('display') == 'none'){
8701                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8702                     p.style.visibility = "hidden";
8703                     p.style.display = "block";
8704                 }
8705                 p = p.parentNode;
8706             }
8707             this._measureChanged = changed;
8708             return this;
8709
8710         },
8711
8712         /**
8713          * Restores displays to before beginMeasure was called
8714          * @return {Roo.Element} this
8715          */
8716         endMeasure : function(){
8717             var changed = this._measureChanged;
8718             if(changed){
8719                 for(var i = 0, len = changed.length; i < len; i++) {
8720                     var r = changed[i];
8721                     r.el.style.visibility = r.visibility;
8722                     r.el.style.display = "none";
8723                 }
8724                 this._measureChanged = null;
8725             }
8726             return this;
8727         },
8728
8729         /**
8730         * Update the innerHTML of this element, optionally searching for and processing scripts
8731         * @param {String} html The new HTML
8732         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8733         * @param {Function} callback For async script loading you can be noticed when the update completes
8734         * @return {Roo.Element} this
8735          */
8736         update : function(html, loadScripts, callback){
8737             if(typeof html == "undefined"){
8738                 html = "";
8739             }
8740             if(loadScripts !== true){
8741                 this.dom.innerHTML = html;
8742                 if(typeof callback == "function"){
8743                     callback();
8744                 }
8745                 return this;
8746             }
8747             var id = Roo.id();
8748             var dom = this.dom;
8749
8750             html += '<span id="' + id + '"></span>';
8751
8752             E.onAvailable(id, function(){
8753                 var hd = document.getElementsByTagName("head")[0];
8754                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8755                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8756                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8757
8758                 var match;
8759                 while(match = re.exec(html)){
8760                     var attrs = match[1];
8761                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8762                     if(srcMatch && srcMatch[2]){
8763                        var s = document.createElement("script");
8764                        s.src = srcMatch[2];
8765                        var typeMatch = attrs.match(typeRe);
8766                        if(typeMatch && typeMatch[2]){
8767                            s.type = typeMatch[2];
8768                        }
8769                        hd.appendChild(s);
8770                     }else if(match[2] && match[2].length > 0){
8771                         if(window.execScript) {
8772                            window.execScript(match[2]);
8773                         } else {
8774                             /**
8775                              * eval:var:id
8776                              * eval:var:dom
8777                              * eval:var:html
8778                              * 
8779                              */
8780                            window.eval(match[2]);
8781                         }
8782                     }
8783                 }
8784                 var el = document.getElementById(id);
8785                 if(el){el.parentNode.removeChild(el);}
8786                 if(typeof callback == "function"){
8787                     callback();
8788                 }
8789             });
8790             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8791             return this;
8792         },
8793
8794         /**
8795          * Direct access to the UpdateManager update() method (takes the same parameters).
8796          * @param {String/Function} url The url for this request or a function to call to get the url
8797          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8798          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8799          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8800          * @return {Roo.Element} this
8801          */
8802         load : function(){
8803             var um = this.getUpdateManager();
8804             um.update.apply(um, arguments);
8805             return this;
8806         },
8807
8808         /**
8809         * Gets this element's UpdateManager
8810         * @return {Roo.UpdateManager} The UpdateManager
8811         */
8812         getUpdateManager : function(){
8813             if(!this.updateManager){
8814                 this.updateManager = new Roo.UpdateManager(this);
8815             }
8816             return this.updateManager;
8817         },
8818
8819         /**
8820          * Disables text selection for this element (normalized across browsers)
8821          * @return {Roo.Element} this
8822          */
8823         unselectable : function(){
8824             this.dom.unselectable = "on";
8825             this.swallowEvent("selectstart", true);
8826             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8827             this.addClass("x-unselectable");
8828             return this;
8829         },
8830
8831         /**
8832         * Calculates the x, y to center this element on the screen
8833         * @return {Array} The x, y values [x, y]
8834         */
8835         getCenterXY : function(){
8836             return this.getAlignToXY(document, 'c-c');
8837         },
8838
8839         /**
8840         * Centers the Element in either the viewport, or another Element.
8841         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8842         */
8843         center : function(centerIn){
8844             this.alignTo(centerIn || document, 'c-c');
8845             return this;
8846         },
8847
8848         /**
8849          * Tests various css rules/browsers to determine if this element uses a border box
8850          * @return {Boolean}
8851          */
8852         isBorderBox : function(){
8853             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8854         },
8855
8856         /**
8857          * Return a box {x, y, width, height} that can be used to set another elements
8858          * size/location to match this element.
8859          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8860          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8861          * @return {Object} box An object in the format {x, y, width, height}
8862          */
8863         getBox : function(contentBox, local){
8864             var xy;
8865             if(!local){
8866                 xy = this.getXY();
8867             }else{
8868                 var left = parseInt(this.getStyle("left"), 10) || 0;
8869                 var top = parseInt(this.getStyle("top"), 10) || 0;
8870                 xy = [left, top];
8871             }
8872             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8873             if(!contentBox){
8874                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8875             }else{
8876                 var l = this.getBorderWidth("l")+this.getPadding("l");
8877                 var r = this.getBorderWidth("r")+this.getPadding("r");
8878                 var t = this.getBorderWidth("t")+this.getPadding("t");
8879                 var b = this.getBorderWidth("b")+this.getPadding("b");
8880                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8881             }
8882             bx.right = bx.x + bx.width;
8883             bx.bottom = bx.y + bx.height;
8884             return bx;
8885         },
8886
8887         /**
8888          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8889          for more information about the sides.
8890          * @param {String} sides
8891          * @return {Number}
8892          */
8893         getFrameWidth : function(sides, onlyContentBox){
8894             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8895         },
8896
8897         /**
8898          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8899          * @param {Object} box The box to fill {x, y, width, height}
8900          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8901          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8902          * @return {Roo.Element} this
8903          */
8904         setBox : function(box, adjust, animate){
8905             var w = box.width, h = box.height;
8906             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8907                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8908                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8909             }
8910             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8911             return this;
8912         },
8913
8914         /**
8915          * Forces the browser to repaint this element
8916          * @return {Roo.Element} this
8917          */
8918          repaint : function(){
8919             var dom = this.dom;
8920             this.addClass("x-repaint");
8921             setTimeout(function(){
8922                 Roo.get(dom).removeClass("x-repaint");
8923             }, 1);
8924             return this;
8925         },
8926
8927         /**
8928          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8929          * then it returns the calculated width of the sides (see getPadding)
8930          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8931          * @return {Object/Number}
8932          */
8933         getMargins : function(side){
8934             if(!side){
8935                 return {
8936                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8937                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8938                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8939                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8940                 };
8941             }else{
8942                 return this.addStyles(side, El.margins);
8943              }
8944         },
8945
8946         // private
8947         addStyles : function(sides, styles){
8948             var val = 0, v, w;
8949             for(var i = 0, len = sides.length; i < len; i++){
8950                 v = this.getStyle(styles[sides.charAt(i)]);
8951                 if(v){
8952                      w = parseInt(v, 10);
8953                      if(w){ val += w; }
8954                 }
8955             }
8956             return val;
8957         },
8958
8959         /**
8960          * Creates a proxy element of this element
8961          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8962          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8963          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8964          * @return {Roo.Element} The new proxy element
8965          */
8966         createProxy : function(config, renderTo, matchBox){
8967             if(renderTo){
8968                 renderTo = Roo.getDom(renderTo);
8969             }else{
8970                 renderTo = document.body;
8971             }
8972             config = typeof config == "object" ?
8973                 config : {tag : "div", cls: config};
8974             var proxy = Roo.DomHelper.append(renderTo, config, true);
8975             if(matchBox){
8976                proxy.setBox(this.getBox());
8977             }
8978             return proxy;
8979         },
8980
8981         /**
8982          * Puts a mask over this element to disable user interaction. Requires core.css.
8983          * This method can only be applied to elements which accept child nodes.
8984          * @param {String} msg (optional) A message to display in the mask
8985          * @param {String} msgCls (optional) A css class to apply to the msg element
8986          * @return {Element} The mask  element
8987          */
8988         mask : function(msg, msgCls)
8989         {
8990             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8991                 this.setStyle("position", "relative");
8992             }
8993             if(!this._mask){
8994                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8995             }
8996             this.addClass("x-masked");
8997             this._mask.setDisplayed(true);
8998             
8999             // we wander
9000             var z = 0;
9001             var dom = this.dom
9002             while (dom && dom.style) {
9003                 if (!isNaN(parseInt(dom.style.zIndex))) {
9004                     z = Math.max(z, parseInt(dom.style.zIndex));
9005                 }
9006                 dom = dom.parentNode;
9007             }
9008             // if we are masking the body - then it hides everything..
9009             if (this.dom == document.body) {
9010                 z = 1000000;
9011                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9012                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9013             }
9014            
9015             if(typeof msg == 'string'){
9016                 if(!this._maskMsg){
9017                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9018                 }
9019                 var mm = this._maskMsg;
9020                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9021                 if (mm.dom.firstChild) { // weird IE issue?
9022                     mm.dom.firstChild.innerHTML = msg;
9023                 }
9024                 mm.setDisplayed(true);
9025                 mm.center(this);
9026                 mm.setStyle('z-index', z + 102);
9027             }
9028             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9029                 this._mask.setHeight(this.getHeight());
9030             }
9031             this._mask.setStyle('z-index', z + 100);
9032             
9033             return this._mask;
9034         },
9035
9036         /**
9037          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9038          * it is cached for reuse.
9039          */
9040         unmask : function(removeEl){
9041             if(this._mask){
9042                 if(removeEl === true){
9043                     this._mask.remove();
9044                     delete this._mask;
9045                     if(this._maskMsg){
9046                         this._maskMsg.remove();
9047                         delete this._maskMsg;
9048                     }
9049                 }else{
9050                     this._mask.setDisplayed(false);
9051                     if(this._maskMsg){
9052                         this._maskMsg.setDisplayed(false);
9053                     }
9054                 }
9055             }
9056             this.removeClass("x-masked");
9057         },
9058
9059         /**
9060          * Returns true if this element is masked
9061          * @return {Boolean}
9062          */
9063         isMasked : function(){
9064             return this._mask && this._mask.isVisible();
9065         },
9066
9067         /**
9068          * Creates an iframe shim for this element to keep selects and other windowed objects from
9069          * showing through.
9070          * @return {Roo.Element} The new shim element
9071          */
9072         createShim : function(){
9073             var el = document.createElement('iframe');
9074             el.frameBorder = 'no';
9075             el.className = 'roo-shim';
9076             if(Roo.isIE && Roo.isSecure){
9077                 el.src = Roo.SSL_SECURE_URL;
9078             }
9079             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9080             shim.autoBoxAdjust = false;
9081             return shim;
9082         },
9083
9084         /**
9085          * Removes this element from the DOM and deletes it from the cache
9086          */
9087         remove : function(){
9088             if(this.dom.parentNode){
9089                 this.dom.parentNode.removeChild(this.dom);
9090             }
9091             delete El.cache[this.dom.id];
9092         },
9093
9094         /**
9095          * Sets up event handlers to add and remove a css class when the mouse is over this element
9096          * @param {String} className
9097          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9098          * mouseout events for children elements
9099          * @return {Roo.Element} this
9100          */
9101         addClassOnOver : function(className, preventFlicker){
9102             this.on("mouseover", function(){
9103                 Roo.fly(this, '_internal').addClass(className);
9104             }, this.dom);
9105             var removeFn = function(e){
9106                 if(preventFlicker !== true || !e.within(this, true)){
9107                     Roo.fly(this, '_internal').removeClass(className);
9108                 }
9109             };
9110             this.on("mouseout", removeFn, this.dom);
9111             return this;
9112         },
9113
9114         /**
9115          * Sets up event handlers to add and remove a css class when this element has the focus
9116          * @param {String} className
9117          * @return {Roo.Element} this
9118          */
9119         addClassOnFocus : function(className){
9120             this.on("focus", function(){
9121                 Roo.fly(this, '_internal').addClass(className);
9122             }, this.dom);
9123             this.on("blur", function(){
9124                 Roo.fly(this, '_internal').removeClass(className);
9125             }, this.dom);
9126             return this;
9127         },
9128         /**
9129          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9130          * @param {String} className
9131          * @return {Roo.Element} this
9132          */
9133         addClassOnClick : function(className){
9134             var dom = this.dom;
9135             this.on("mousedown", function(){
9136                 Roo.fly(dom, '_internal').addClass(className);
9137                 var d = Roo.get(document);
9138                 var fn = function(){
9139                     Roo.fly(dom, '_internal').removeClass(className);
9140                     d.removeListener("mouseup", fn);
9141                 };
9142                 d.on("mouseup", fn);
9143             });
9144             return this;
9145         },
9146
9147         /**
9148          * Stops the specified event from bubbling and optionally prevents the default action
9149          * @param {String} eventName
9150          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9151          * @return {Roo.Element} this
9152          */
9153         swallowEvent : function(eventName, preventDefault){
9154             var fn = function(e){
9155                 e.stopPropagation();
9156                 if(preventDefault){
9157                     e.preventDefault();
9158                 }
9159             };
9160             if(eventName instanceof Array){
9161                 for(var i = 0, len = eventName.length; i < len; i++){
9162                      this.on(eventName[i], fn);
9163                 }
9164                 return this;
9165             }
9166             this.on(eventName, fn);
9167             return this;
9168         },
9169
9170         /**
9171          * @private
9172          */
9173       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9174
9175         /**
9176          * Sizes this element to its parent element's dimensions performing
9177          * neccessary box adjustments.
9178          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9179          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9180          * @return {Roo.Element} this
9181          */
9182         fitToParent : function(monitorResize, targetParent) {
9183           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9184           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9185           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9186             return;
9187           }
9188           var p = Roo.get(targetParent || this.dom.parentNode);
9189           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9190           if (monitorResize === true) {
9191             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9192             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9193           }
9194           return this;
9195         },
9196
9197         /**
9198          * Gets the next sibling, skipping text nodes
9199          * @return {HTMLElement} The next sibling or null
9200          */
9201         getNextSibling : function(){
9202             var n = this.dom.nextSibling;
9203             while(n && n.nodeType != 1){
9204                 n = n.nextSibling;
9205             }
9206             return n;
9207         },
9208
9209         /**
9210          * Gets the previous sibling, skipping text nodes
9211          * @return {HTMLElement} The previous sibling or null
9212          */
9213         getPrevSibling : function(){
9214             var n = this.dom.previousSibling;
9215             while(n && n.nodeType != 1){
9216                 n = n.previousSibling;
9217             }
9218             return n;
9219         },
9220
9221
9222         /**
9223          * Appends the passed element(s) to this element
9224          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9225          * @return {Roo.Element} this
9226          */
9227         appendChild: function(el){
9228             el = Roo.get(el);
9229             el.appendTo(this);
9230             return this;
9231         },
9232
9233         /**
9234          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9235          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9236          * automatically generated with the specified attributes.
9237          * @param {HTMLElement} insertBefore (optional) a child element of this element
9238          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9239          * @return {Roo.Element} The new child element
9240          */
9241         createChild: function(config, insertBefore, returnDom){
9242             config = config || {tag:'div'};
9243             if(insertBefore){
9244                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9245             }
9246             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9247         },
9248
9249         /**
9250          * Appends this element to the passed element
9251          * @param {String/HTMLElement/Element} el The new parent element
9252          * @return {Roo.Element} this
9253          */
9254         appendTo: function(el){
9255             el = Roo.getDom(el);
9256             el.appendChild(this.dom);
9257             return this;
9258         },
9259
9260         /**
9261          * Inserts this element before the passed element in the DOM
9262          * @param {String/HTMLElement/Element} el The element to insert before
9263          * @return {Roo.Element} this
9264          */
9265         insertBefore: function(el){
9266             el = Roo.getDom(el);
9267             el.parentNode.insertBefore(this.dom, el);
9268             return this;
9269         },
9270
9271         /**
9272          * Inserts this element after the passed element in the DOM
9273          * @param {String/HTMLElement/Element} el The element to insert after
9274          * @return {Roo.Element} this
9275          */
9276         insertAfter: function(el){
9277             el = Roo.getDom(el);
9278             el.parentNode.insertBefore(this.dom, el.nextSibling);
9279             return this;
9280         },
9281
9282         /**
9283          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9284          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9285          * @return {Roo.Element} The new child
9286          */
9287         insertFirst: function(el, returnDom){
9288             el = el || {};
9289             if(typeof el == 'object' && !el.nodeType){ // dh config
9290                 return this.createChild(el, this.dom.firstChild, returnDom);
9291             }else{
9292                 el = Roo.getDom(el);
9293                 this.dom.insertBefore(el, this.dom.firstChild);
9294                 return !returnDom ? Roo.get(el) : el;
9295             }
9296         },
9297
9298         /**
9299          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9300          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9301          * @param {String} where (optional) 'before' or 'after' defaults to before
9302          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9303          * @return {Roo.Element} the inserted Element
9304          */
9305         insertSibling: function(el, where, returnDom){
9306             where = where ? where.toLowerCase() : 'before';
9307             el = el || {};
9308             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9309
9310             if(typeof el == 'object' && !el.nodeType){ // dh config
9311                 if(where == 'after' && !this.dom.nextSibling){
9312                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9313                 }else{
9314                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9315                 }
9316
9317             }else{
9318                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9319                             where == 'before' ? this.dom : this.dom.nextSibling);
9320                 if(!returnDom){
9321                     rt = Roo.get(rt);
9322                 }
9323             }
9324             return rt;
9325         },
9326
9327         /**
9328          * Creates and wraps this element with another element
9329          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9330          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9331          * @return {HTMLElement/Element} The newly created wrapper element
9332          */
9333         wrap: function(config, returnDom){
9334             if(!config){
9335                 config = {tag: "div"};
9336             }
9337             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9338             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9339             return newEl;
9340         },
9341
9342         /**
9343          * Replaces the passed element with this element
9344          * @param {String/HTMLElement/Element} el The element to replace
9345          * @return {Roo.Element} this
9346          */
9347         replace: function(el){
9348             el = Roo.get(el);
9349             this.insertBefore(el);
9350             el.remove();
9351             return this;
9352         },
9353
9354         /**
9355          * Inserts an html fragment into this element
9356          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9357          * @param {String} html The HTML fragment
9358          * @param {Boolean} returnEl True to return an Roo.Element
9359          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9360          */
9361         insertHtml : function(where, html, returnEl){
9362             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9363             return returnEl ? Roo.get(el) : el;
9364         },
9365
9366         /**
9367          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9368          * @param {Object} o The object with the attributes
9369          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9370          * @return {Roo.Element} this
9371          */
9372         set : function(o, useSet){
9373             var el = this.dom;
9374             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9375             for(var attr in o){
9376                 if(attr == "style" || typeof o[attr] == "function") continue;
9377                 if(attr=="cls"){
9378                     el.className = o["cls"];
9379                 }else{
9380                     if(useSet) el.setAttribute(attr, o[attr]);
9381                     else el[attr] = o[attr];
9382                 }
9383             }
9384             if(o.style){
9385                 Roo.DomHelper.applyStyles(el, o.style);
9386             }
9387             return this;
9388         },
9389
9390         /**
9391          * Convenience method for constructing a KeyMap
9392          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9393          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9394          * @param {Function} fn The function to call
9395          * @param {Object} scope (optional) The scope of the function
9396          * @return {Roo.KeyMap} The KeyMap created
9397          */
9398         addKeyListener : function(key, fn, scope){
9399             var config;
9400             if(typeof key != "object" || key instanceof Array){
9401                 config = {
9402                     key: key,
9403                     fn: fn,
9404                     scope: scope
9405                 };
9406             }else{
9407                 config = {
9408                     key : key.key,
9409                     shift : key.shift,
9410                     ctrl : key.ctrl,
9411                     alt : key.alt,
9412                     fn: fn,
9413                     scope: scope
9414                 };
9415             }
9416             return new Roo.KeyMap(this, config);
9417         },
9418
9419         /**
9420          * Creates a KeyMap for this element
9421          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9422          * @return {Roo.KeyMap} The KeyMap created
9423          */
9424         addKeyMap : function(config){
9425             return new Roo.KeyMap(this, config);
9426         },
9427
9428         /**
9429          * Returns true if this element is scrollable.
9430          * @return {Boolean}
9431          */
9432          isScrollable : function(){
9433             var dom = this.dom;
9434             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9435         },
9436
9437         /**
9438          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9439          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9440          * @param {Number} value The new scroll value
9441          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9442          * @return {Element} this
9443          */
9444
9445         scrollTo : function(side, value, animate){
9446             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9447             if(!animate || !A){
9448                 this.dom[prop] = value;
9449             }else{
9450                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9451                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9452             }
9453             return this;
9454         },
9455
9456         /**
9457          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9458          * within this element's scrollable range.
9459          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9460          * @param {Number} distance How far to scroll the element in pixels
9461          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9462          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9463          * was scrolled as far as it could go.
9464          */
9465          scroll : function(direction, distance, animate){
9466              if(!this.isScrollable()){
9467                  return;
9468              }
9469              var el = this.dom;
9470              var l = el.scrollLeft, t = el.scrollTop;
9471              var w = el.scrollWidth, h = el.scrollHeight;
9472              var cw = el.clientWidth, ch = el.clientHeight;
9473              direction = direction.toLowerCase();
9474              var scrolled = false;
9475              var a = this.preanim(arguments, 2);
9476              switch(direction){
9477                  case "l":
9478                  case "left":
9479                      if(w - l > cw){
9480                          var v = Math.min(l + distance, w-cw);
9481                          this.scrollTo("left", v, a);
9482                          scrolled = true;
9483                      }
9484                      break;
9485                 case "r":
9486                 case "right":
9487                      if(l > 0){
9488                          var v = Math.max(l - distance, 0);
9489                          this.scrollTo("left", v, a);
9490                          scrolled = true;
9491                      }
9492                      break;
9493                 case "t":
9494                 case "top":
9495                 case "up":
9496                      if(t > 0){
9497                          var v = Math.max(t - distance, 0);
9498                          this.scrollTo("top", v, a);
9499                          scrolled = true;
9500                      }
9501                      break;
9502                 case "b":
9503                 case "bottom":
9504                 case "down":
9505                      if(h - t > ch){
9506                          var v = Math.min(t + distance, h-ch);
9507                          this.scrollTo("top", v, a);
9508                          scrolled = true;
9509                      }
9510                      break;
9511              }
9512              return scrolled;
9513         },
9514
9515         /**
9516          * Translates the passed page coordinates into left/top css values for this element
9517          * @param {Number/Array} x The page x or an array containing [x, y]
9518          * @param {Number} y The page y
9519          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9520          */
9521         translatePoints : function(x, y){
9522             if(typeof x == 'object' || x instanceof Array){
9523                 y = x[1]; x = x[0];
9524             }
9525             var p = this.getStyle('position');
9526             var o = this.getXY();
9527
9528             var l = parseInt(this.getStyle('left'), 10);
9529             var t = parseInt(this.getStyle('top'), 10);
9530
9531             if(isNaN(l)){
9532                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9533             }
9534             if(isNaN(t)){
9535                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9536             }
9537
9538             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9539         },
9540
9541         /**
9542          * Returns the current scroll position of the element.
9543          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9544          */
9545         getScroll : function(){
9546             var d = this.dom, doc = document;
9547             if(d == doc || d == doc.body){
9548                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9549                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9550                 return {left: l, top: t};
9551             }else{
9552                 return {left: d.scrollLeft, top: d.scrollTop};
9553             }
9554         },
9555
9556         /**
9557          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9558          * are convert to standard 6 digit hex color.
9559          * @param {String} attr The css attribute
9560          * @param {String} defaultValue The default value to use when a valid color isn't found
9561          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9562          * YUI color anims.
9563          */
9564         getColor : function(attr, defaultValue, prefix){
9565             var v = this.getStyle(attr);
9566             if(!v || v == "transparent" || v == "inherit") {
9567                 return defaultValue;
9568             }
9569             var color = typeof prefix == "undefined" ? "#" : prefix;
9570             if(v.substr(0, 4) == "rgb("){
9571                 var rvs = v.slice(4, v.length -1).split(",");
9572                 for(var i = 0; i < 3; i++){
9573                     var h = parseInt(rvs[i]).toString(16);
9574                     if(h < 16){
9575                         h = "0" + h;
9576                     }
9577                     color += h;
9578                 }
9579             } else {
9580                 if(v.substr(0, 1) == "#"){
9581                     if(v.length == 4) {
9582                         for(var i = 1; i < 4; i++){
9583                             var c = v.charAt(i);
9584                             color +=  c + c;
9585                         }
9586                     }else if(v.length == 7){
9587                         color += v.substr(1);
9588                     }
9589                 }
9590             }
9591             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9592         },
9593
9594         /**
9595          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9596          * gradient background, rounded corners and a 4-way shadow.
9597          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9598          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9599          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9600          * @return {Roo.Element} this
9601          */
9602         boxWrap : function(cls){
9603             cls = cls || 'x-box';
9604             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9605             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9606             return el;
9607         },
9608
9609         /**
9610          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9611          * @param {String} namespace The namespace in which to look for the attribute
9612          * @param {String} name The attribute name
9613          * @return {String} The attribute value
9614          */
9615         getAttributeNS : Roo.isIE ? function(ns, name){
9616             var d = this.dom;
9617             var type = typeof d[ns+":"+name];
9618             if(type != 'undefined' && type != 'unknown'){
9619                 return d[ns+":"+name];
9620             }
9621             return d[name];
9622         } : function(ns, name){
9623             var d = this.dom;
9624             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9625         },
9626         
9627         
9628         /**
9629          * Sets or Returns the value the dom attribute value
9630          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9631          * @param {String} value (optional) The value to set the attribute to
9632          * @return {String} The attribute value
9633          */
9634         attr : function(name){
9635             if (arguments.length > 1) {
9636                 this.dom.setAttribute(name, arguments[1]);
9637                 return arguments[1];
9638             }
9639             if (typeof(name) == 'object') {
9640                 for(var i in name) {
9641                     this.attr(i, name[i]);
9642                 }
9643                 return name;
9644             }
9645             
9646             
9647             if (!this.dom.hasAttribute(name)) {
9648                 return undefined;
9649             }
9650             return this.dom.getAttribute(name);
9651         }
9652         
9653         
9654         
9655     };
9656
9657     var ep = El.prototype;
9658
9659     /**
9660      * Appends an event handler (Shorthand for addListener)
9661      * @param {String}   eventName     The type of event to append
9662      * @param {Function} fn        The method the event invokes
9663      * @param {Object} scope       (optional) The scope (this object) of the fn
9664      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9665      * @method
9666      */
9667     ep.on = ep.addListener;
9668         // backwards compat
9669     ep.mon = ep.addListener;
9670
9671     /**
9672      * Removes an event handler from this element (shorthand for removeListener)
9673      * @param {String} eventName the type of event to remove
9674      * @param {Function} fn the method the event invokes
9675      * @return {Roo.Element} this
9676      * @method
9677      */
9678     ep.un = ep.removeListener;
9679
9680     /**
9681      * true to automatically adjust width and height settings for box-model issues (default to true)
9682      */
9683     ep.autoBoxAdjust = true;
9684
9685     // private
9686     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9687
9688     // private
9689     El.addUnits = function(v, defaultUnit){
9690         if(v === "" || v == "auto"){
9691             return v;
9692         }
9693         if(v === undefined){
9694             return '';
9695         }
9696         if(typeof v == "number" || !El.unitPattern.test(v)){
9697             return v + (defaultUnit || 'px');
9698         }
9699         return v;
9700     };
9701
9702     // special markup used throughout Roo when box wrapping elements
9703     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9704     /**
9705      * Visibility mode constant - Use visibility to hide element
9706      * @static
9707      * @type Number
9708      */
9709     El.VISIBILITY = 1;
9710     /**
9711      * Visibility mode constant - Use display to hide element
9712      * @static
9713      * @type Number
9714      */
9715     El.DISPLAY = 2;
9716
9717     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9718     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9719     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9720
9721
9722
9723     /**
9724      * @private
9725      */
9726     El.cache = {};
9727
9728     var docEl;
9729
9730     /**
9731      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9732      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9733      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9734      * @return {Element} The Element object
9735      * @static
9736      */
9737     El.get = function(el){
9738         var ex, elm, id;
9739         if(!el){ return null; }
9740         if(typeof el == "string"){ // element id
9741             if(!(elm = document.getElementById(el))){
9742                 return null;
9743             }
9744             if(ex = El.cache[el]){
9745                 ex.dom = elm;
9746             }else{
9747                 ex = El.cache[el] = new El(elm);
9748             }
9749             return ex;
9750         }else if(el.tagName){ // dom element
9751             if(!(id = el.id)){
9752                 id = Roo.id(el);
9753             }
9754             if(ex = El.cache[id]){
9755                 ex.dom = el;
9756             }else{
9757                 ex = El.cache[id] = new El(el);
9758             }
9759             return ex;
9760         }else if(el instanceof El){
9761             if(el != docEl){
9762                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9763                                                               // catch case where it hasn't been appended
9764                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9765             }
9766             return el;
9767         }else if(el.isComposite){
9768             return el;
9769         }else if(el instanceof Array){
9770             return El.select(el);
9771         }else if(el == document){
9772             // create a bogus element object representing the document object
9773             if(!docEl){
9774                 var f = function(){};
9775                 f.prototype = El.prototype;
9776                 docEl = new f();
9777                 docEl.dom = document;
9778             }
9779             return docEl;
9780         }
9781         return null;
9782     };
9783
9784     // private
9785     El.uncache = function(el){
9786         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9787             if(a[i]){
9788                 delete El.cache[a[i].id || a[i]];
9789             }
9790         }
9791     };
9792
9793     // private
9794     // Garbage collection - uncache elements/purge listeners on orphaned elements
9795     // so we don't hold a reference and cause the browser to retain them
9796     El.garbageCollect = function(){
9797         if(!Roo.enableGarbageCollector){
9798             clearInterval(El.collectorThread);
9799             return;
9800         }
9801         for(var eid in El.cache){
9802             var el = El.cache[eid], d = el.dom;
9803             // -------------------------------------------------------
9804             // Determining what is garbage:
9805             // -------------------------------------------------------
9806             // !d
9807             // dom node is null, definitely garbage
9808             // -------------------------------------------------------
9809             // !d.parentNode
9810             // no parentNode == direct orphan, definitely garbage
9811             // -------------------------------------------------------
9812             // !d.offsetParent && !document.getElementById(eid)
9813             // display none elements have no offsetParent so we will
9814             // also try to look it up by it's id. However, check
9815             // offsetParent first so we don't do unneeded lookups.
9816             // This enables collection of elements that are not orphans
9817             // directly, but somewhere up the line they have an orphan
9818             // parent.
9819             // -------------------------------------------------------
9820             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9821                 delete El.cache[eid];
9822                 if(d && Roo.enableListenerCollection){
9823                     E.purgeElement(d);
9824                 }
9825             }
9826         }
9827     }
9828     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9829
9830
9831     // dom is optional
9832     El.Flyweight = function(dom){
9833         this.dom = dom;
9834     };
9835     El.Flyweight.prototype = El.prototype;
9836
9837     El._flyweights = {};
9838     /**
9839      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9840      * the dom node can be overwritten by other code.
9841      * @param {String/HTMLElement} el The dom node or id
9842      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9843      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9844      * @static
9845      * @return {Element} The shared Element object
9846      */
9847     El.fly = function(el, named){
9848         named = named || '_global';
9849         el = Roo.getDom(el);
9850         if(!el){
9851             return null;
9852         }
9853         if(!El._flyweights[named]){
9854             El._flyweights[named] = new El.Flyweight();
9855         }
9856         El._flyweights[named].dom = el;
9857         return El._flyweights[named];
9858     };
9859
9860     /**
9861      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9862      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9863      * Shorthand of {@link Roo.Element#get}
9864      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9865      * @return {Element} The Element object
9866      * @member Roo
9867      * @method get
9868      */
9869     Roo.get = El.get;
9870     /**
9871      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9872      * the dom node can be overwritten by other code.
9873      * Shorthand of {@link Roo.Element#fly}
9874      * @param {String/HTMLElement} el The dom node or id
9875      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9876      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9877      * @static
9878      * @return {Element} The shared Element object
9879      * @member Roo
9880      * @method fly
9881      */
9882     Roo.fly = El.fly;
9883
9884     // speedy lookup for elements never to box adjust
9885     var noBoxAdjust = Roo.isStrict ? {
9886         select:1
9887     } : {
9888         input:1, select:1, textarea:1
9889     };
9890     if(Roo.isIE || Roo.isGecko){
9891         noBoxAdjust['button'] = 1;
9892     }
9893
9894
9895     Roo.EventManager.on(window, 'unload', function(){
9896         delete El.cache;
9897         delete El._flyweights;
9898     });
9899 })();
9900
9901
9902
9903
9904 if(Roo.DomQuery){
9905     Roo.Element.selectorFunction = Roo.DomQuery.select;
9906 }
9907
9908 Roo.Element.select = function(selector, unique, root){
9909     var els;
9910     if(typeof selector == "string"){
9911         els = Roo.Element.selectorFunction(selector, root);
9912     }else if(selector.length !== undefined){
9913         els = selector;
9914     }else{
9915         throw "Invalid selector";
9916     }
9917     if(unique === true){
9918         return new Roo.CompositeElement(els);
9919     }else{
9920         return new Roo.CompositeElementLite(els);
9921     }
9922 };
9923 /**
9924  * Selects elements based on the passed CSS selector to enable working on them as 1.
9925  * @param {String/Array} selector The CSS selector or an array of elements
9926  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9927  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9928  * @return {CompositeElementLite/CompositeElement}
9929  * @member Roo
9930  * @method select
9931  */
9932 Roo.select = Roo.Element.select;
9933
9934
9935
9936
9937
9938
9939
9940
9941
9942
9943
9944
9945
9946
9947 /*
9948  * Based on:
9949  * Ext JS Library 1.1.1
9950  * Copyright(c) 2006-2007, Ext JS, LLC.
9951  *
9952  * Originally Released Under LGPL - original licence link has changed is not relivant.
9953  *
9954  * Fork - LGPL
9955  * <script type="text/javascript">
9956  */
9957
9958
9959
9960 //Notifies Element that fx methods are available
9961 Roo.enableFx = true;
9962
9963 /**
9964  * @class Roo.Fx
9965  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9966  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9967  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9968  * Element effects to work.</p><br/>
9969  *
9970  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9971  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9972  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9973  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9974  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9975  * expected results and should be done with care.</p><br/>
9976  *
9977  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9978  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9979 <pre>
9980 Value  Description
9981 -----  -----------------------------
9982 tl     The top left corner
9983 t      The center of the top edge
9984 tr     The top right corner
9985 l      The center of the left edge
9986 r      The center of the right edge
9987 bl     The bottom left corner
9988 b      The center of the bottom edge
9989 br     The bottom right corner
9990 </pre>
9991  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9992  * below are common options that can be passed to any Fx method.</b>
9993  * @cfg {Function} callback A function called when the effect is finished
9994  * @cfg {Object} scope The scope of the effect function
9995  * @cfg {String} easing A valid Easing value for the effect
9996  * @cfg {String} afterCls A css class to apply after the effect
9997  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9998  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9999  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10000  * effects that end with the element being visually hidden, ignored otherwise)
10001  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10002  * a function which returns such a specification that will be applied to the Element after the effect finishes
10003  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10004  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10005  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10006  */
10007 Roo.Fx = {
10008         /**
10009          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10010          * origin for the slide effect.  This function automatically handles wrapping the element with
10011          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10012          * Usage:
10013          *<pre><code>
10014 // default: slide the element in from the top
10015 el.slideIn();
10016
10017 // custom: slide the element in from the right with a 2-second duration
10018 el.slideIn('r', { duration: 2 });
10019
10020 // common config options shown with default values
10021 el.slideIn('t', {
10022     easing: 'easeOut',
10023     duration: .5
10024 });
10025 </code></pre>
10026          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10027          * @param {Object} options (optional) Object literal with any of the Fx config options
10028          * @return {Roo.Element} The Element
10029          */
10030     slideIn : function(anchor, o){
10031         var el = this.getFxEl();
10032         o = o || {};
10033
10034         el.queueFx(o, function(){
10035
10036             anchor = anchor || "t";
10037
10038             // fix display to visibility
10039             this.fixDisplay();
10040
10041             // restore values after effect
10042             var r = this.getFxRestore();
10043             var b = this.getBox();
10044             // fixed size for slide
10045             this.setSize(b);
10046
10047             // wrap if needed
10048             var wrap = this.fxWrap(r.pos, o, "hidden");
10049
10050             var st = this.dom.style;
10051             st.visibility = "visible";
10052             st.position = "absolute";
10053
10054             // clear out temp styles after slide and unwrap
10055             var after = function(){
10056                 el.fxUnwrap(wrap, r.pos, o);
10057                 st.width = r.width;
10058                 st.height = r.height;
10059                 el.afterFx(o);
10060             };
10061             // time to calc the positions
10062             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10063
10064             switch(anchor.toLowerCase()){
10065                 case "t":
10066                     wrap.setSize(b.width, 0);
10067                     st.left = st.bottom = "0";
10068                     a = {height: bh};
10069                 break;
10070                 case "l":
10071                     wrap.setSize(0, b.height);
10072                     st.right = st.top = "0";
10073                     a = {width: bw};
10074                 break;
10075                 case "r":
10076                     wrap.setSize(0, b.height);
10077                     wrap.setX(b.right);
10078                     st.left = st.top = "0";
10079                     a = {width: bw, points: pt};
10080                 break;
10081                 case "b":
10082                     wrap.setSize(b.width, 0);
10083                     wrap.setY(b.bottom);
10084                     st.left = st.top = "0";
10085                     a = {height: bh, points: pt};
10086                 break;
10087                 case "tl":
10088                     wrap.setSize(0, 0);
10089                     st.right = st.bottom = "0";
10090                     a = {width: bw, height: bh};
10091                 break;
10092                 case "bl":
10093                     wrap.setSize(0, 0);
10094                     wrap.setY(b.y+b.height);
10095                     st.right = st.top = "0";
10096                     a = {width: bw, height: bh, points: pt};
10097                 break;
10098                 case "br":
10099                     wrap.setSize(0, 0);
10100                     wrap.setXY([b.right, b.bottom]);
10101                     st.left = st.top = "0";
10102                     a = {width: bw, height: bh, points: pt};
10103                 break;
10104                 case "tr":
10105                     wrap.setSize(0, 0);
10106                     wrap.setX(b.x+b.width);
10107                     st.left = st.bottom = "0";
10108                     a = {width: bw, height: bh, points: pt};
10109                 break;
10110             }
10111             this.dom.style.visibility = "visible";
10112             wrap.show();
10113
10114             arguments.callee.anim = wrap.fxanim(a,
10115                 o,
10116                 'motion',
10117                 .5,
10118                 'easeOut', after);
10119         });
10120         return this;
10121     },
10122     
10123         /**
10124          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10125          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10126          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10127          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10128          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10129          * Usage:
10130          *<pre><code>
10131 // default: slide the element out to the top
10132 el.slideOut();
10133
10134 // custom: slide the element out to the right with a 2-second duration
10135 el.slideOut('r', { duration: 2 });
10136
10137 // common config options shown with default values
10138 el.slideOut('t', {
10139     easing: 'easeOut',
10140     duration: .5,
10141     remove: false,
10142     useDisplay: false
10143 });
10144 </code></pre>
10145          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10146          * @param {Object} options (optional) Object literal with any of the Fx config options
10147          * @return {Roo.Element} The Element
10148          */
10149     slideOut : function(anchor, o){
10150         var el = this.getFxEl();
10151         o = o || {};
10152
10153         el.queueFx(o, function(){
10154
10155             anchor = anchor || "t";
10156
10157             // restore values after effect
10158             var r = this.getFxRestore();
10159             
10160             var b = this.getBox();
10161             // fixed size for slide
10162             this.setSize(b);
10163
10164             // wrap if needed
10165             var wrap = this.fxWrap(r.pos, o, "visible");
10166
10167             var st = this.dom.style;
10168             st.visibility = "visible";
10169             st.position = "absolute";
10170
10171             wrap.setSize(b);
10172
10173             var after = function(){
10174                 if(o.useDisplay){
10175                     el.setDisplayed(false);
10176                 }else{
10177                     el.hide();
10178                 }
10179
10180                 el.fxUnwrap(wrap, r.pos, o);
10181
10182                 st.width = r.width;
10183                 st.height = r.height;
10184
10185                 el.afterFx(o);
10186             };
10187
10188             var a, zero = {to: 0};
10189             switch(anchor.toLowerCase()){
10190                 case "t":
10191                     st.left = st.bottom = "0";
10192                     a = {height: zero};
10193                 break;
10194                 case "l":
10195                     st.right = st.top = "0";
10196                     a = {width: zero};
10197                 break;
10198                 case "r":
10199                     st.left = st.top = "0";
10200                     a = {width: zero, points: {to:[b.right, b.y]}};
10201                 break;
10202                 case "b":
10203                     st.left = st.top = "0";
10204                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10205                 break;
10206                 case "tl":
10207                     st.right = st.bottom = "0";
10208                     a = {width: zero, height: zero};
10209                 break;
10210                 case "bl":
10211                     st.right = st.top = "0";
10212                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10213                 break;
10214                 case "br":
10215                     st.left = st.top = "0";
10216                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10217                 break;
10218                 case "tr":
10219                     st.left = st.bottom = "0";
10220                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10221                 break;
10222             }
10223
10224             arguments.callee.anim = wrap.fxanim(a,
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10235          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10236          * The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.puff();
10241
10242 // common config options shown with default values
10243 el.puff({
10244     easing: 'easeOut',
10245     duration: .5,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     puff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.show();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273
10274                 el.setPositioning(r.pos);
10275                 st.width = r.width;
10276                 st.height = r.height;
10277                 st.fontSize = '';
10278                 el.afterFx(o);
10279             };
10280
10281             var width = this.getWidth();
10282             var height = this.getHeight();
10283
10284             arguments.callee.anim = this.fxanim({
10285                     width : {to: this.adjustWidth(width * 2)},
10286                     height : {to: this.adjustHeight(height * 2)},
10287                     points : {by: [-(width * .5), -(height * .5)]},
10288                     opacity : {to: 0},
10289                     fontSize: {to:200, unit: "%"}
10290                 },
10291                 o,
10292                 'motion',
10293                 .5,
10294                 "easeOut", after);
10295         });
10296         return this;
10297     },
10298
10299         /**
10300          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10301          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10302          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10303          * Usage:
10304          *<pre><code>
10305 // default
10306 el.switchOff();
10307
10308 // all config options shown with default values
10309 el.switchOff({
10310     easing: 'easeIn',
10311     duration: .3,
10312     remove: false,
10313     useDisplay: false
10314 });
10315 </code></pre>
10316          * @param {Object} options (optional) Object literal with any of the Fx config options
10317          * @return {Roo.Element} The Element
10318          */
10319     switchOff : function(o){
10320         var el = this.getFxEl();
10321         o = o || {};
10322
10323         el.queueFx(o, function(){
10324             this.clearOpacity();
10325             this.clip();
10326
10327             // restore values after effect
10328             var r = this.getFxRestore();
10329             var st = this.dom.style;
10330
10331             var after = function(){
10332                 if(o.useDisplay){
10333                     el.setDisplayed(false);
10334                 }else{
10335                     el.hide();
10336                 }
10337
10338                 el.clearOpacity();
10339                 el.setPositioning(r.pos);
10340                 st.width = r.width;
10341                 st.height = r.height;
10342
10343                 el.afterFx(o);
10344             };
10345
10346             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10347                 this.clearOpacity();
10348                 (function(){
10349                     this.fxanim({
10350                         height:{to:1},
10351                         points:{by:[0, this.getHeight() * .5]}
10352                     }, o, 'motion', 0.3, 'easeIn', after);
10353                 }).defer(100, this);
10354             });
10355         });
10356         return this;
10357     },
10358
10359     /**
10360      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10361      * changed using the "attr" config option) and then fading back to the original color. If no original
10362      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10363      * Usage:
10364 <pre><code>
10365 // default: highlight background to yellow
10366 el.highlight();
10367
10368 // custom: highlight foreground text to blue for 2 seconds
10369 el.highlight("0000ff", { attr: 'color', duration: 2 });
10370
10371 // common config options shown with default values
10372 el.highlight("ffff9c", {
10373     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10374     endColor: (current color) or "ffffff",
10375     easing: 'easeIn',
10376     duration: 1
10377 });
10378 </code></pre>
10379      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10380      * @param {Object} options (optional) Object literal with any of the Fx config options
10381      * @return {Roo.Element} The Element
10382      */ 
10383     highlight : function(color, o){
10384         var el = this.getFxEl();
10385         o = o || {};
10386
10387         el.queueFx(o, function(){
10388             color = color || "ffff9c";
10389             attr = o.attr || "backgroundColor";
10390
10391             this.clearOpacity();
10392             this.show();
10393
10394             var origColor = this.getColor(attr);
10395             var restoreColor = this.dom.style[attr];
10396             endColor = (o.endColor || origColor) || "ffffff";
10397
10398             var after = function(){
10399                 el.dom.style[attr] = restoreColor;
10400                 el.afterFx(o);
10401             };
10402
10403             var a = {};
10404             a[attr] = {from: color, to: endColor};
10405             arguments.callee.anim = this.fxanim(a,
10406                 o,
10407                 'color',
10408                 1,
10409                 'easeIn', after);
10410         });
10411         return this;
10412     },
10413
10414    /**
10415     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10416     * Usage:
10417 <pre><code>
10418 // default: a single light blue ripple
10419 el.frame();
10420
10421 // custom: 3 red ripples lasting 3 seconds total
10422 el.frame("ff0000", 3, { duration: 3 });
10423
10424 // common config options shown with default values
10425 el.frame("C3DAF9", 1, {
10426     duration: 1 //duration of entire animation (not each individual ripple)
10427     // Note: Easing is not configurable and will be ignored if included
10428 });
10429 </code></pre>
10430     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10431     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10432     * @param {Object} options (optional) Object literal with any of the Fx config options
10433     * @return {Roo.Element} The Element
10434     */
10435     frame : function(color, count, o){
10436         var el = this.getFxEl();
10437         o = o || {};
10438
10439         el.queueFx(o, function(){
10440             color = color || "#C3DAF9";
10441             if(color.length == 6){
10442                 color = "#" + color;
10443             }
10444             count = count || 1;
10445             duration = o.duration || 1;
10446             this.show();
10447
10448             var b = this.getBox();
10449             var animFn = function(){
10450                 var proxy = this.createProxy({
10451
10452                      style:{
10453                         visbility:"hidden",
10454                         position:"absolute",
10455                         "z-index":"35000", // yee haw
10456                         border:"0px solid " + color
10457                      }
10458                   });
10459                 var scale = Roo.isBorderBox ? 2 : 1;
10460                 proxy.animate({
10461                     top:{from:b.y, to:b.y - 20},
10462                     left:{from:b.x, to:b.x - 20},
10463                     borderWidth:{from:0, to:10},
10464                     opacity:{from:1, to:0},
10465                     height:{from:b.height, to:(b.height + (20*scale))},
10466                     width:{from:b.width, to:(b.width + (20*scale))}
10467                 }, duration, function(){
10468                     proxy.remove();
10469                 });
10470                 if(--count > 0){
10471                      animFn.defer((duration/2)*1000, this);
10472                 }else{
10473                     el.afterFx(o);
10474                 }
10475             };
10476             animFn.call(this);
10477         });
10478         return this;
10479     },
10480
10481    /**
10482     * Creates a pause before any subsequent queued effects begin.  If there are
10483     * no effects queued after the pause it will have no effect.
10484     * Usage:
10485 <pre><code>
10486 el.pause(1);
10487 </code></pre>
10488     * @param {Number} seconds The length of time to pause (in seconds)
10489     * @return {Roo.Element} The Element
10490     */
10491     pause : function(seconds){
10492         var el = this.getFxEl();
10493         var o = {};
10494
10495         el.queueFx(o, function(){
10496             setTimeout(function(){
10497                 el.afterFx(o);
10498             }, seconds * 1000);
10499         });
10500         return this;
10501     },
10502
10503    /**
10504     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10505     * using the "endOpacity" config option.
10506     * Usage:
10507 <pre><code>
10508 // default: fade in from opacity 0 to 100%
10509 el.fadeIn();
10510
10511 // custom: fade in from opacity 0 to 75% over 2 seconds
10512 el.fadeIn({ endOpacity: .75, duration: 2});
10513
10514 // common config options shown with default values
10515 el.fadeIn({
10516     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10517     easing: 'easeOut',
10518     duration: .5
10519 });
10520 </code></pre>
10521     * @param {Object} options (optional) Object literal with any of the Fx config options
10522     * @return {Roo.Element} The Element
10523     */
10524     fadeIn : function(o){
10525         var el = this.getFxEl();
10526         o = o || {};
10527         el.queueFx(o, function(){
10528             this.setOpacity(0);
10529             this.fixDisplay();
10530             this.dom.style.visibility = 'visible';
10531             var to = o.endOpacity || 1;
10532             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10533                 o, null, .5, "easeOut", function(){
10534                 if(to == 1){
10535                     this.clearOpacity();
10536                 }
10537                 el.afterFx(o);
10538             });
10539         });
10540         return this;
10541     },
10542
10543    /**
10544     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10545     * using the "endOpacity" config option.
10546     * Usage:
10547 <pre><code>
10548 // default: fade out from the element's current opacity to 0
10549 el.fadeOut();
10550
10551 // custom: fade out from the element's current opacity to 25% over 2 seconds
10552 el.fadeOut({ endOpacity: .25, duration: 2});
10553
10554 // common config options shown with default values
10555 el.fadeOut({
10556     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10557     easing: 'easeOut',
10558     duration: .5
10559     remove: false,
10560     useDisplay: false
10561 });
10562 </code></pre>
10563     * @param {Object} options (optional) Object literal with any of the Fx config options
10564     * @return {Roo.Element} The Element
10565     */
10566     fadeOut : function(o){
10567         var el = this.getFxEl();
10568         o = o || {};
10569         el.queueFx(o, function(){
10570             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10571                 o, null, .5, "easeOut", function(){
10572                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10573                      this.dom.style.display = "none";
10574                 }else{
10575                      this.dom.style.visibility = "hidden";
10576                 }
10577                 this.clearOpacity();
10578                 el.afterFx(o);
10579             });
10580         });
10581         return this;
10582     },
10583
10584    /**
10585     * Animates the transition of an element's dimensions from a starting height/width
10586     * to an ending height/width.
10587     * Usage:
10588 <pre><code>
10589 // change height and width to 100x100 pixels
10590 el.scale(100, 100);
10591
10592 // common config options shown with default values.  The height and width will default to
10593 // the element's existing values if passed as null.
10594 el.scale(
10595     [element's width],
10596     [element's height], {
10597     easing: 'easeOut',
10598     duration: .35
10599 });
10600 </code></pre>
10601     * @param {Number} width  The new width (pass undefined to keep the original width)
10602     * @param {Number} height  The new height (pass undefined to keep the original height)
10603     * @param {Object} options (optional) Object literal with any of the Fx config options
10604     * @return {Roo.Element} The Element
10605     */
10606     scale : function(w, h, o){
10607         this.shift(Roo.apply({}, o, {
10608             width: w,
10609             height: h
10610         }));
10611         return this;
10612     },
10613
10614    /**
10615     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10616     * Any of these properties not specified in the config object will not be changed.  This effect 
10617     * requires that at least one new dimension, position or opacity setting must be passed in on
10618     * the config object in order for the function to have any effect.
10619     * Usage:
10620 <pre><code>
10621 // slide the element horizontally to x position 200 while changing the height and opacity
10622 el.shift({ x: 200, height: 50, opacity: .8 });
10623
10624 // common config options shown with default values.
10625 el.shift({
10626     width: [element's width],
10627     height: [element's height],
10628     x: [element's x position],
10629     y: [element's y position],
10630     opacity: [element's opacity],
10631     easing: 'easeOut',
10632     duration: .35
10633 });
10634 </code></pre>
10635     * @param {Object} options  Object literal with any of the Fx config options
10636     * @return {Roo.Element} The Element
10637     */
10638     shift : function(o){
10639         var el = this.getFxEl();
10640         o = o || {};
10641         el.queueFx(o, function(){
10642             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10643             if(w !== undefined){
10644                 a.width = {to: this.adjustWidth(w)};
10645             }
10646             if(h !== undefined){
10647                 a.height = {to: this.adjustHeight(h)};
10648             }
10649             if(x !== undefined || y !== undefined){
10650                 a.points = {to: [
10651                     x !== undefined ? x : this.getX(),
10652                     y !== undefined ? y : this.getY()
10653                 ]};
10654             }
10655             if(op !== undefined){
10656                 a.opacity = {to: op};
10657             }
10658             if(o.xy !== undefined){
10659                 a.points = {to: o.xy};
10660             }
10661             arguments.callee.anim = this.fxanim(a,
10662                 o, 'motion', .35, "easeOut", function(){
10663                 el.afterFx(o);
10664             });
10665         });
10666         return this;
10667     },
10668
10669         /**
10670          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10671          * ending point of the effect.
10672          * Usage:
10673          *<pre><code>
10674 // default: slide the element downward while fading out
10675 el.ghost();
10676
10677 // custom: slide the element out to the right with a 2-second duration
10678 el.ghost('r', { duration: 2 });
10679
10680 // common config options shown with default values
10681 el.ghost('b', {
10682     easing: 'easeOut',
10683     duration: .5
10684     remove: false,
10685     useDisplay: false
10686 });
10687 </code></pre>
10688          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10689          * @param {Object} options (optional) Object literal with any of the Fx config options
10690          * @return {Roo.Element} The Element
10691          */
10692     ghost : function(anchor, o){
10693         var el = this.getFxEl();
10694         o = o || {};
10695
10696         el.queueFx(o, function(){
10697             anchor = anchor || "b";
10698
10699             // restore values after effect
10700             var r = this.getFxRestore();
10701             var w = this.getWidth(),
10702                 h = this.getHeight();
10703
10704             var st = this.dom.style;
10705
10706             var after = function(){
10707                 if(o.useDisplay){
10708                     el.setDisplayed(false);
10709                 }else{
10710                     el.hide();
10711                 }
10712
10713                 el.clearOpacity();
10714                 el.setPositioning(r.pos);
10715                 st.width = r.width;
10716                 st.height = r.height;
10717
10718                 el.afterFx(o);
10719             };
10720
10721             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10722             switch(anchor.toLowerCase()){
10723                 case "t":
10724                     pt.by = [0, -h];
10725                 break;
10726                 case "l":
10727                     pt.by = [-w, 0];
10728                 break;
10729                 case "r":
10730                     pt.by = [w, 0];
10731                 break;
10732                 case "b":
10733                     pt.by = [0, h];
10734                 break;
10735                 case "tl":
10736                     pt.by = [-w, -h];
10737                 break;
10738                 case "bl":
10739                     pt.by = [-w, h];
10740                 break;
10741                 case "br":
10742                     pt.by = [w, h];
10743                 break;
10744                 case "tr":
10745                     pt.by = [w, -h];
10746                 break;
10747             }
10748
10749             arguments.callee.anim = this.fxanim(a,
10750                 o,
10751                 'motion',
10752                 .5,
10753                 "easeOut", after);
10754         });
10755         return this;
10756     },
10757
10758         /**
10759          * Ensures that all effects queued after syncFx is called on the element are
10760          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10761          * @return {Roo.Element} The Element
10762          */
10763     syncFx : function(){
10764         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10765             block : false,
10766             concurrent : true,
10767             stopFx : false
10768         });
10769         return this;
10770     },
10771
10772         /**
10773          * Ensures that all effects queued after sequenceFx is called on the element are
10774          * run in sequence.  This is the opposite of {@link #syncFx}.
10775          * @return {Roo.Element} The Element
10776          */
10777     sequenceFx : function(){
10778         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10779             block : false,
10780             concurrent : false,
10781             stopFx : false
10782         });
10783         return this;
10784     },
10785
10786         /* @private */
10787     nextFx : function(){
10788         var ef = this.fxQueue[0];
10789         if(ef){
10790             ef.call(this);
10791         }
10792     },
10793
10794         /**
10795          * Returns true if the element has any effects actively running or queued, else returns false.
10796          * @return {Boolean} True if element has active effects, else false
10797          */
10798     hasActiveFx : function(){
10799         return this.fxQueue && this.fxQueue[0];
10800     },
10801
10802         /**
10803          * Stops any running effects and clears the element's internal effects queue if it contains
10804          * any additional effects that haven't started yet.
10805          * @return {Roo.Element} The Element
10806          */
10807     stopFx : function(){
10808         if(this.hasActiveFx()){
10809             var cur = this.fxQueue[0];
10810             if(cur && cur.anim && cur.anim.isAnimated()){
10811                 this.fxQueue = [cur]; // clear out others
10812                 cur.anim.stop(true);
10813             }
10814         }
10815         return this;
10816     },
10817
10818         /* @private */
10819     beforeFx : function(o){
10820         if(this.hasActiveFx() && !o.concurrent){
10821            if(o.stopFx){
10822                this.stopFx();
10823                return true;
10824            }
10825            return false;
10826         }
10827         return true;
10828     },
10829
10830         /**
10831          * Returns true if the element is currently blocking so that no other effect can be queued
10832          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10833          * used to ensure that an effect initiated by a user action runs to completion prior to the
10834          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10835          * @return {Boolean} True if blocking, else false
10836          */
10837     hasFxBlock : function(){
10838         var q = this.fxQueue;
10839         return q && q[0] && q[0].block;
10840     },
10841
10842         /* @private */
10843     queueFx : function(o, fn){
10844         if(!this.fxQueue){
10845             this.fxQueue = [];
10846         }
10847         if(!this.hasFxBlock()){
10848             Roo.applyIf(o, this.fxDefaults);
10849             if(!o.concurrent){
10850                 var run = this.beforeFx(o);
10851                 fn.block = o.block;
10852                 this.fxQueue.push(fn);
10853                 if(run){
10854                     this.nextFx();
10855                 }
10856             }else{
10857                 fn.call(this);
10858             }
10859         }
10860         return this;
10861     },
10862
10863         /* @private */
10864     fxWrap : function(pos, o, vis){
10865         var wrap;
10866         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10867             var wrapXY;
10868             if(o.fixPosition){
10869                 wrapXY = this.getXY();
10870             }
10871             var div = document.createElement("div");
10872             div.style.visibility = vis;
10873             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10874             wrap.setPositioning(pos);
10875             if(wrap.getStyle("position") == "static"){
10876                 wrap.position("relative");
10877             }
10878             this.clearPositioning('auto');
10879             wrap.clip();
10880             wrap.dom.appendChild(this.dom);
10881             if(wrapXY){
10882                 wrap.setXY(wrapXY);
10883             }
10884         }
10885         return wrap;
10886     },
10887
10888         /* @private */
10889     fxUnwrap : function(wrap, pos, o){
10890         this.clearPositioning();
10891         this.setPositioning(pos);
10892         if(!o.wrap){
10893             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10894             wrap.remove();
10895         }
10896     },
10897
10898         /* @private */
10899     getFxRestore : function(){
10900         var st = this.dom.style;
10901         return {pos: this.getPositioning(), width: st.width, height : st.height};
10902     },
10903
10904         /* @private */
10905     afterFx : function(o){
10906         if(o.afterStyle){
10907             this.applyStyles(o.afterStyle);
10908         }
10909         if(o.afterCls){
10910             this.addClass(o.afterCls);
10911         }
10912         if(o.remove === true){
10913             this.remove();
10914         }
10915         Roo.callback(o.callback, o.scope, [this]);
10916         if(!o.concurrent){
10917             this.fxQueue.shift();
10918             this.nextFx();
10919         }
10920     },
10921
10922         /* @private */
10923     getFxEl : function(){ // support for composite element fx
10924         return Roo.get(this.dom);
10925     },
10926
10927         /* @private */
10928     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10929         animType = animType || 'run';
10930         opt = opt || {};
10931         var anim = Roo.lib.Anim[animType](
10932             this.dom, args,
10933             (opt.duration || defaultDur) || .35,
10934             (opt.easing || defaultEase) || 'easeOut',
10935             function(){
10936                 Roo.callback(cb, this);
10937             },
10938             this
10939         );
10940         opt.anim = anim;
10941         return anim;
10942     }
10943 };
10944
10945 // backwords compat
10946 Roo.Fx.resize = Roo.Fx.scale;
10947
10948 //When included, Roo.Fx is automatically applied to Element so that all basic
10949 //effects are available directly via the Element API
10950 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10951  * Based on:
10952  * Ext JS Library 1.1.1
10953  * Copyright(c) 2006-2007, Ext JS, LLC.
10954  *
10955  * Originally Released Under LGPL - original licence link has changed is not relivant.
10956  *
10957  * Fork - LGPL
10958  * <script type="text/javascript">
10959  */
10960
10961
10962 /**
10963  * @class Roo.CompositeElement
10964  * Standard composite class. Creates a Roo.Element for every element in the collection.
10965  * <br><br>
10966  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10967  * actions will be performed on all the elements in this collection.</b>
10968  * <br><br>
10969  * All methods return <i>this</i> and can be chained.
10970  <pre><code>
10971  var els = Roo.select("#some-el div.some-class", true);
10972  // or select directly from an existing element
10973  var el = Roo.get('some-el');
10974  el.select('div.some-class', true);
10975
10976  els.setWidth(100); // all elements become 100 width
10977  els.hide(true); // all elements fade out and hide
10978  // or
10979  els.setWidth(100).hide(true);
10980  </code></pre>
10981  */
10982 Roo.CompositeElement = function(els){
10983     this.elements = [];
10984     this.addElements(els);
10985 };
10986 Roo.CompositeElement.prototype = {
10987     isComposite: true,
10988     addElements : function(els){
10989         if(!els) return this;
10990         if(typeof els == "string"){
10991             els = Roo.Element.selectorFunction(els);
10992         }
10993         var yels = this.elements;
10994         var index = yels.length-1;
10995         for(var i = 0, len = els.length; i < len; i++) {
10996                 yels[++index] = Roo.get(els[i]);
10997         }
10998         return this;
10999     },
11000
11001     /**
11002     * Clears this composite and adds the elements returned by the passed selector.
11003     * @param {String/Array} els A string CSS selector, an array of elements or an element
11004     * @return {CompositeElement} this
11005     */
11006     fill : function(els){
11007         this.elements = [];
11008         this.add(els);
11009         return this;
11010     },
11011
11012     /**
11013     * Filters this composite to only elements that match the passed selector.
11014     * @param {String} selector A string CSS selector
11015     * @param {Boolean} inverse return inverse filter (not matches)
11016     * @return {CompositeElement} this
11017     */
11018     filter : function(selector, inverse){
11019         var els = [];
11020         inverse = inverse || false;
11021         this.each(function(el){
11022             var match = inverse ? !el.is(selector) : el.is(selector);
11023             if(match){
11024                 els[els.length] = el.dom;
11025             }
11026         });
11027         this.fill(els);
11028         return this;
11029     },
11030
11031     invoke : function(fn, args){
11032         var els = this.elements;
11033         for(var i = 0, len = els.length; i < len; i++) {
11034                 Roo.Element.prototype[fn].apply(els[i], args);
11035         }
11036         return this;
11037     },
11038     /**
11039     * Adds elements to this composite.
11040     * @param {String/Array} els A string CSS selector, an array of elements or an element
11041     * @return {CompositeElement} this
11042     */
11043     add : function(els){
11044         if(typeof els == "string"){
11045             this.addElements(Roo.Element.selectorFunction(els));
11046         }else if(els.length !== undefined){
11047             this.addElements(els);
11048         }else{
11049             this.addElements([els]);
11050         }
11051         return this;
11052     },
11053     /**
11054     * Calls the passed function passing (el, this, index) for each element in this composite.
11055     * @param {Function} fn The function to call
11056     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11057     * @return {CompositeElement} this
11058     */
11059     each : function(fn, scope){
11060         var els = this.elements;
11061         for(var i = 0, len = els.length; i < len; i++){
11062             if(fn.call(scope || els[i], els[i], this, i) === false) {
11063                 break;
11064             }
11065         }
11066         return this;
11067     },
11068
11069     /**
11070      * Returns the Element object at the specified index
11071      * @param {Number} index
11072      * @return {Roo.Element}
11073      */
11074     item : function(index){
11075         return this.elements[index] || null;
11076     },
11077
11078     /**
11079      * Returns the first Element
11080      * @return {Roo.Element}
11081      */
11082     first : function(){
11083         return this.item(0);
11084     },
11085
11086     /**
11087      * Returns the last Element
11088      * @return {Roo.Element}
11089      */
11090     last : function(){
11091         return this.item(this.elements.length-1);
11092     },
11093
11094     /**
11095      * Returns the number of elements in this composite
11096      * @return Number
11097      */
11098     getCount : function(){
11099         return this.elements.length;
11100     },
11101
11102     /**
11103      * Returns true if this composite contains the passed element
11104      * @return Boolean
11105      */
11106     contains : function(el){
11107         return this.indexOf(el) !== -1;
11108     },
11109
11110     /**
11111      * Returns true if this composite contains the passed element
11112      * @return Boolean
11113      */
11114     indexOf : function(el){
11115         return this.elements.indexOf(Roo.get(el));
11116     },
11117
11118
11119     /**
11120     * Removes the specified element(s).
11121     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11122     * or an array of any of those.
11123     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11124     * @return {CompositeElement} this
11125     */
11126     removeElement : function(el, removeDom){
11127         if(el instanceof Array){
11128             for(var i = 0, len = el.length; i < len; i++){
11129                 this.removeElement(el[i]);
11130             }
11131             return this;
11132         }
11133         var index = typeof el == 'number' ? el : this.indexOf(el);
11134         if(index !== -1){
11135             if(removeDom){
11136                 var d = this.elements[index];
11137                 if(d.dom){
11138                     d.remove();
11139                 }else{
11140                     d.parentNode.removeChild(d);
11141                 }
11142             }
11143             this.elements.splice(index, 1);
11144         }
11145         return this;
11146     },
11147
11148     /**
11149     * Replaces the specified element with the passed element.
11150     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11151     * to replace.
11152     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11153     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11154     * @return {CompositeElement} this
11155     */
11156     replaceElement : function(el, replacement, domReplace){
11157         var index = typeof el == 'number' ? el : this.indexOf(el);
11158         if(index !== -1){
11159             if(domReplace){
11160                 this.elements[index].replaceWith(replacement);
11161             }else{
11162                 this.elements.splice(index, 1, Roo.get(replacement))
11163             }
11164         }
11165         return this;
11166     },
11167
11168     /**
11169      * Removes all elements.
11170      */
11171     clear : function(){
11172         this.elements = [];
11173     }
11174 };
11175 (function(){
11176     Roo.CompositeElement.createCall = function(proto, fnName){
11177         if(!proto[fnName]){
11178             proto[fnName] = function(){
11179                 return this.invoke(fnName, arguments);
11180             };
11181         }
11182     };
11183     for(var fnName in Roo.Element.prototype){
11184         if(typeof Roo.Element.prototype[fnName] == "function"){
11185             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11186         }
11187     };
11188 })();
11189 /*
11190  * Based on:
11191  * Ext JS Library 1.1.1
11192  * Copyright(c) 2006-2007, Ext JS, LLC.
11193  *
11194  * Originally Released Under LGPL - original licence link has changed is not relivant.
11195  *
11196  * Fork - LGPL
11197  * <script type="text/javascript">
11198  */
11199
11200 /**
11201  * @class Roo.CompositeElementLite
11202  * @extends Roo.CompositeElement
11203  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11204  <pre><code>
11205  var els = Roo.select("#some-el div.some-class");
11206  // or select directly from an existing element
11207  var el = Roo.get('some-el');
11208  el.select('div.some-class');
11209
11210  els.setWidth(100); // all elements become 100 width
11211  els.hide(true); // all elements fade out and hide
11212  // or
11213  els.setWidth(100).hide(true);
11214  </code></pre><br><br>
11215  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11216  * actions will be performed on all the elements in this collection.</b>
11217  */
11218 Roo.CompositeElementLite = function(els){
11219     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11220     this.el = new Roo.Element.Flyweight();
11221 };
11222 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11223     addElements : function(els){
11224         if(els){
11225             if(els instanceof Array){
11226                 this.elements = this.elements.concat(els);
11227             }else{
11228                 var yels = this.elements;
11229                 var index = yels.length-1;
11230                 for(var i = 0, len = els.length; i < len; i++) {
11231                     yels[++index] = els[i];
11232                 }
11233             }
11234         }
11235         return this;
11236     },
11237     invoke : function(fn, args){
11238         var els = this.elements;
11239         var el = this.el;
11240         for(var i = 0, len = els.length; i < len; i++) {
11241             el.dom = els[i];
11242                 Roo.Element.prototype[fn].apply(el, args);
11243         }
11244         return this;
11245     },
11246     /**
11247      * Returns a flyweight Element of the dom element object at the specified index
11248      * @param {Number} index
11249      * @return {Roo.Element}
11250      */
11251     item : function(index){
11252         if(!this.elements[index]){
11253             return null;
11254         }
11255         this.el.dom = this.elements[index];
11256         return this.el;
11257     },
11258
11259     // fixes scope with flyweight
11260     addListener : function(eventName, handler, scope, opt){
11261         var els = this.elements;
11262         for(var i = 0, len = els.length; i < len; i++) {
11263             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11264         }
11265         return this;
11266     },
11267
11268     /**
11269     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11270     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11271     * a reference to the dom node, use el.dom.</b>
11272     * @param {Function} fn The function to call
11273     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11274     * @return {CompositeElement} this
11275     */
11276     each : function(fn, scope){
11277         var els = this.elements;
11278         var el = this.el;
11279         for(var i = 0, len = els.length; i < len; i++){
11280             el.dom = els[i];
11281                 if(fn.call(scope || el, el, this, i) === false){
11282                 break;
11283             }
11284         }
11285         return this;
11286     },
11287
11288     indexOf : function(el){
11289         return this.elements.indexOf(Roo.getDom(el));
11290     },
11291
11292     replaceElement : function(el, replacement, domReplace){
11293         var index = typeof el == 'number' ? el : this.indexOf(el);
11294         if(index !== -1){
11295             replacement = Roo.getDom(replacement);
11296             if(domReplace){
11297                 var d = this.elements[index];
11298                 d.parentNode.insertBefore(replacement, d);
11299                 d.parentNode.removeChild(d);
11300             }
11301             this.elements.splice(index, 1, replacement);
11302         }
11303         return this;
11304     }
11305 });
11306 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11307
11308 /*
11309  * Based on:
11310  * Ext JS Library 1.1.1
11311  * Copyright(c) 2006-2007, Ext JS, LLC.
11312  *
11313  * Originally Released Under LGPL - original licence link has changed is not relivant.
11314  *
11315  * Fork - LGPL
11316  * <script type="text/javascript">
11317  */
11318
11319  
11320
11321 /**
11322  * @class Roo.data.Connection
11323  * @extends Roo.util.Observable
11324  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11325  * either to a configured URL, or to a URL specified at request time.<br><br>
11326  * <p>
11327  * Requests made by this class are asynchronous, and will return immediately. No data from
11328  * the server will be available to the statement immediately following the {@link #request} call.
11329  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11330  * <p>
11331  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11332  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11333  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11334  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11335  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11336  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11337  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11338  * standard DOM methods.
11339  * @constructor
11340  * @param {Object} config a configuration object.
11341  */
11342 Roo.data.Connection = function(config){
11343     Roo.apply(this, config);
11344     this.addEvents({
11345         /**
11346          * @event beforerequest
11347          * Fires before a network request is made to retrieve a data object.
11348          * @param {Connection} conn This Connection object.
11349          * @param {Object} options The options config object passed to the {@link #request} method.
11350          */
11351         "beforerequest" : true,
11352         /**
11353          * @event requestcomplete
11354          * Fires if the request was successfully completed.
11355          * @param {Connection} conn This Connection object.
11356          * @param {Object} response The XHR object containing the response data.
11357          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11358          * @param {Object} options The options config object passed to the {@link #request} method.
11359          */
11360         "requestcomplete" : true,
11361         /**
11362          * @event requestexception
11363          * Fires if an error HTTP status was returned from the server.
11364          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11365          * @param {Connection} conn This Connection object.
11366          * @param {Object} response The XHR object containing the response data.
11367          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11368          * @param {Object} options The options config object passed to the {@link #request} method.
11369          */
11370         "requestexception" : true
11371     });
11372     Roo.data.Connection.superclass.constructor.call(this);
11373 };
11374
11375 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11376     /**
11377      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11378      */
11379     /**
11380      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11381      * extra parameters to each request made by this object. (defaults to undefined)
11382      */
11383     /**
11384      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11385      *  to each request made by this object. (defaults to undefined)
11386      */
11387     /**
11388      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11389      */
11390     /**
11391      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11392      */
11393     timeout : 30000,
11394     /**
11395      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11396      * @type Boolean
11397      */
11398     autoAbort:false,
11399
11400     /**
11401      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11402      * @type Boolean
11403      */
11404     disableCaching: true,
11405
11406     /**
11407      * Sends an HTTP request to a remote server.
11408      * @param {Object} options An object which may contain the following properties:<ul>
11409      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11410      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11411      * request, a url encoded string or a function to call to get either.</li>
11412      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11413      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11414      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11415      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11416      * <li>options {Object} The parameter to the request call.</li>
11417      * <li>success {Boolean} True if the request succeeded.</li>
11418      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11419      * </ul></li>
11420      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11421      * The callback is passed the following parameters:<ul>
11422      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11423      * <li>options {Object} The parameter to the request call.</li>
11424      * </ul></li>
11425      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11426      * The callback is passed the following parameters:<ul>
11427      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11428      * <li>options {Object} The parameter to the request call.</li>
11429      * </ul></li>
11430      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11431      * for the callback function. Defaults to the browser window.</li>
11432      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11433      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11434      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11435      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11436      * params for the post data. Any params will be appended to the URL.</li>
11437      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11438      * </ul>
11439      * @return {Number} transactionId
11440      */
11441     request : function(o){
11442         if(this.fireEvent("beforerequest", this, o) !== false){
11443             var p = o.params;
11444
11445             if(typeof p == "function"){
11446                 p = p.call(o.scope||window, o);
11447             }
11448             if(typeof p == "object"){
11449                 p = Roo.urlEncode(o.params);
11450             }
11451             if(this.extraParams){
11452                 var extras = Roo.urlEncode(this.extraParams);
11453                 p = p ? (p + '&' + extras) : extras;
11454             }
11455
11456             var url = o.url || this.url;
11457             if(typeof url == 'function'){
11458                 url = url.call(o.scope||window, o);
11459             }
11460
11461             if(o.form){
11462                 var form = Roo.getDom(o.form);
11463                 url = url || form.action;
11464
11465                 var enctype = form.getAttribute("enctype");
11466                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11467                     return this.doFormUpload(o, p, url);
11468                 }
11469                 var f = Roo.lib.Ajax.serializeForm(form);
11470                 p = p ? (p + '&' + f) : f;
11471             }
11472
11473             var hs = o.headers;
11474             if(this.defaultHeaders){
11475                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11476                 if(!o.headers){
11477                     o.headers = hs;
11478                 }
11479             }
11480
11481             var cb = {
11482                 success: this.handleResponse,
11483                 failure: this.handleFailure,
11484                 scope: this,
11485                 argument: {options: o},
11486                 timeout : o.timeout || this.timeout
11487             };
11488
11489             var method = o.method||this.method||(p ? "POST" : "GET");
11490
11491             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11492                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11493             }
11494
11495             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11496                 if(o.autoAbort){
11497                     this.abort();
11498                 }
11499             }else if(this.autoAbort !== false){
11500                 this.abort();
11501             }
11502
11503             if((method == 'GET' && p) || o.xmlData){
11504                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11505                 p = '';
11506             }
11507             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11508             return this.transId;
11509         }else{
11510             Roo.callback(o.callback, o.scope, [o, null, null]);
11511             return null;
11512         }
11513     },
11514
11515     /**
11516      * Determine whether this object has a request outstanding.
11517      * @param {Number} transactionId (Optional) defaults to the last transaction
11518      * @return {Boolean} True if there is an outstanding request.
11519      */
11520     isLoading : function(transId){
11521         if(transId){
11522             return Roo.lib.Ajax.isCallInProgress(transId);
11523         }else{
11524             return this.transId ? true : false;
11525         }
11526     },
11527
11528     /**
11529      * Aborts any outstanding request.
11530      * @param {Number} transactionId (Optional) defaults to the last transaction
11531      */
11532     abort : function(transId){
11533         if(transId || this.isLoading()){
11534             Roo.lib.Ajax.abort(transId || this.transId);
11535         }
11536     },
11537
11538     // private
11539     handleResponse : function(response){
11540         this.transId = false;
11541         var options = response.argument.options;
11542         response.argument = options ? options.argument : null;
11543         this.fireEvent("requestcomplete", this, response, options);
11544         Roo.callback(options.success, options.scope, [response, options]);
11545         Roo.callback(options.callback, options.scope, [options, true, response]);
11546     },
11547
11548     // private
11549     handleFailure : function(response, e){
11550         this.transId = false;
11551         var options = response.argument.options;
11552         response.argument = options ? options.argument : null;
11553         this.fireEvent("requestexception", this, response, options, e);
11554         Roo.callback(options.failure, options.scope, [response, options]);
11555         Roo.callback(options.callback, options.scope, [options, false, response]);
11556     },
11557
11558     // private
11559     doFormUpload : function(o, ps, url){
11560         var id = Roo.id();
11561         var frame = document.createElement('iframe');
11562         frame.id = id;
11563         frame.name = id;
11564         frame.className = 'x-hidden';
11565         if(Roo.isIE){
11566             frame.src = Roo.SSL_SECURE_URL;
11567         }
11568         document.body.appendChild(frame);
11569
11570         if(Roo.isIE){
11571            document.frames[id].name = id;
11572         }
11573
11574         var form = Roo.getDom(o.form);
11575         form.target = id;
11576         form.method = 'POST';
11577         form.enctype = form.encoding = 'multipart/form-data';
11578         if(url){
11579             form.action = url;
11580         }
11581
11582         var hiddens, hd;
11583         if(ps){ // add dynamic params
11584             hiddens = [];
11585             ps = Roo.urlDecode(ps, false);
11586             for(var k in ps){
11587                 if(ps.hasOwnProperty(k)){
11588                     hd = document.createElement('input');
11589                     hd.type = 'hidden';
11590                     hd.name = k;
11591                     hd.value = ps[k];
11592                     form.appendChild(hd);
11593                     hiddens.push(hd);
11594                 }
11595             }
11596         }
11597
11598         function cb(){
11599             var r = {  // bogus response object
11600                 responseText : '',
11601                 responseXML : null
11602             };
11603
11604             r.argument = o ? o.argument : null;
11605
11606             try { //
11607                 var doc;
11608                 if(Roo.isIE){
11609                     doc = frame.contentWindow.document;
11610                 }else {
11611                     doc = (frame.contentDocument || window.frames[id].document);
11612                 }
11613                 if(doc && doc.body){
11614                     r.responseText = doc.body.innerHTML;
11615                 }
11616                 if(doc && doc.XMLDocument){
11617                     r.responseXML = doc.XMLDocument;
11618                 }else {
11619                     r.responseXML = doc;
11620                 }
11621             }
11622             catch(e) {
11623                 // ignore
11624             }
11625
11626             Roo.EventManager.removeListener(frame, 'load', cb, this);
11627
11628             this.fireEvent("requestcomplete", this, r, o);
11629             Roo.callback(o.success, o.scope, [r, o]);
11630             Roo.callback(o.callback, o.scope, [o, true, r]);
11631
11632             setTimeout(function(){document.body.removeChild(frame);}, 100);
11633         }
11634
11635         Roo.EventManager.on(frame, 'load', cb, this);
11636         form.submit();
11637
11638         if(hiddens){ // remove dynamic params
11639             for(var i = 0, len = hiddens.length; i < len; i++){
11640                 form.removeChild(hiddens[i]);
11641             }
11642         }
11643     }
11644 });
11645 /*
11646  * Based on:
11647  * Ext JS Library 1.1.1
11648  * Copyright(c) 2006-2007, Ext JS, LLC.
11649  *
11650  * Originally Released Under LGPL - original licence link has changed is not relivant.
11651  *
11652  * Fork - LGPL
11653  * <script type="text/javascript">
11654  */
11655  
11656 /**
11657  * Global Ajax request class.
11658  * 
11659  * @class Roo.Ajax
11660  * @extends Roo.data.Connection
11661  * @static
11662  * 
11663  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11664  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11665  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11666  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11667  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11668  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11669  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11670  */
11671 Roo.Ajax = new Roo.data.Connection({
11672     // fix up the docs
11673     /**
11674      * @scope Roo.Ajax
11675      * @type {Boolear} 
11676      */
11677     autoAbort : false,
11678
11679     /**
11680      * Serialize the passed form into a url encoded string
11681      * @scope Roo.Ajax
11682      * @param {String/HTMLElement} form
11683      * @return {String}
11684      */
11685     serializeForm : function(form){
11686         return Roo.lib.Ajax.serializeForm(form);
11687     }
11688 });/*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698
11699  
11700 /**
11701  * @class Roo.UpdateManager
11702  * @extends Roo.util.Observable
11703  * Provides AJAX-style update for Element object.<br><br>
11704  * Usage:<br>
11705  * <pre><code>
11706  * // Get it from a Roo.Element object
11707  * var el = Roo.get("foo");
11708  * var mgr = el.getUpdateManager();
11709  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11710  * ...
11711  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11712  * <br>
11713  * // or directly (returns the same UpdateManager instance)
11714  * var mgr = new Roo.UpdateManager("myElementId");
11715  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11716  * mgr.on("update", myFcnNeedsToKnow);
11717  * <br>
11718    // short handed call directly from the element object
11719    Roo.get("foo").load({
11720         url: "bar.php",
11721         scripts:true,
11722         params: "for=bar",
11723         text: "Loading Foo..."
11724    });
11725  * </code></pre>
11726  * @constructor
11727  * Create new UpdateManager directly.
11728  * @param {String/HTMLElement/Roo.Element} el The element to update
11729  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11730  */
11731 Roo.UpdateManager = function(el, forceNew){
11732     el = Roo.get(el);
11733     if(!forceNew && el.updateManager){
11734         return el.updateManager;
11735     }
11736     /**
11737      * The Element object
11738      * @type Roo.Element
11739      */
11740     this.el = el;
11741     /**
11742      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11743      * @type String
11744      */
11745     this.defaultUrl = null;
11746
11747     this.addEvents({
11748         /**
11749          * @event beforeupdate
11750          * Fired before an update is made, return false from your handler and the update is cancelled.
11751          * @param {Roo.Element} el
11752          * @param {String/Object/Function} url
11753          * @param {String/Object} params
11754          */
11755         "beforeupdate": true,
11756         /**
11757          * @event update
11758          * Fired after successful update is made.
11759          * @param {Roo.Element} el
11760          * @param {Object} oResponseObject The response Object
11761          */
11762         "update": true,
11763         /**
11764          * @event failure
11765          * Fired on update failure.
11766          * @param {Roo.Element} el
11767          * @param {Object} oResponseObject The response Object
11768          */
11769         "failure": true
11770     });
11771     var d = Roo.UpdateManager.defaults;
11772     /**
11773      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11774      * @type String
11775      */
11776     this.sslBlankUrl = d.sslBlankUrl;
11777     /**
11778      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11779      * @type Boolean
11780      */
11781     this.disableCaching = d.disableCaching;
11782     /**
11783      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11784      * @type String
11785      */
11786     this.indicatorText = d.indicatorText;
11787     /**
11788      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11789      * @type String
11790      */
11791     this.showLoadIndicator = d.showLoadIndicator;
11792     /**
11793      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11794      * @type Number
11795      */
11796     this.timeout = d.timeout;
11797
11798     /**
11799      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11800      * @type Boolean
11801      */
11802     this.loadScripts = d.loadScripts;
11803
11804     /**
11805      * Transaction object of current executing transaction
11806      */
11807     this.transaction = null;
11808
11809     /**
11810      * @private
11811      */
11812     this.autoRefreshProcId = null;
11813     /**
11814      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11815      * @type Function
11816      */
11817     this.refreshDelegate = this.refresh.createDelegate(this);
11818     /**
11819      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11820      * @type Function
11821      */
11822     this.updateDelegate = this.update.createDelegate(this);
11823     /**
11824      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11825      * @type Function
11826      */
11827     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11828     /**
11829      * @private
11830      */
11831     this.successDelegate = this.processSuccess.createDelegate(this);
11832     /**
11833      * @private
11834      */
11835     this.failureDelegate = this.processFailure.createDelegate(this);
11836
11837     if(!this.renderer){
11838      /**
11839       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11840       */
11841     this.renderer = new Roo.UpdateManager.BasicRenderer();
11842     }
11843     
11844     Roo.UpdateManager.superclass.constructor.call(this);
11845 };
11846
11847 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11848     /**
11849      * Get the Element this UpdateManager is bound to
11850      * @return {Roo.Element} The element
11851      */
11852     getEl : function(){
11853         return this.el;
11854     },
11855     /**
11856      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11857      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11858 <pre><code>
11859 um.update({<br/>
11860     url: "your-url.php",<br/>
11861     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11862     callback: yourFunction,<br/>
11863     scope: yourObject, //(optional scope)  <br/>
11864     discardUrl: false, <br/>
11865     nocache: false,<br/>
11866     text: "Loading...",<br/>
11867     timeout: 30,<br/>
11868     scripts: false<br/>
11869 });
11870 </code></pre>
11871      * The only required property is url. The optional properties nocache, text and scripts
11872      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11873      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11874      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11875      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11876      */
11877     update : function(url, params, callback, discardUrl){
11878         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11879             var method = this.method,
11880                 cfg;
11881             if(typeof url == "object"){ // must be config object
11882                 cfg = url;
11883                 url = cfg.url;
11884                 params = params || cfg.params;
11885                 callback = callback || cfg.callback;
11886                 discardUrl = discardUrl || cfg.discardUrl;
11887                 if(callback && cfg.scope){
11888                     callback = callback.createDelegate(cfg.scope);
11889                 }
11890                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11891                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11892                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11893                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11894                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11895             }
11896             this.showLoading();
11897             if(!discardUrl){
11898                 this.defaultUrl = url;
11899             }
11900             if(typeof url == "function"){
11901                 url = url.call(this);
11902             }
11903
11904             method = method || (params ? "POST" : "GET");
11905             if(method == "GET"){
11906                 url = this.prepareUrl(url);
11907             }
11908
11909             var o = Roo.apply(cfg ||{}, {
11910                 url : url,
11911                 params: params,
11912                 success: this.successDelegate,
11913                 failure: this.failureDelegate,
11914                 callback: undefined,
11915                 timeout: (this.timeout*1000),
11916                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11917             });
11918             Roo.log("updated manager called with timeout of " + o.timeout);
11919             this.transaction = Roo.Ajax.request(o);
11920         }
11921     },
11922
11923     /**
11924      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11925      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11926      * @param {String/HTMLElement} form The form Id or form element
11927      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11928      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11929      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11930      */
11931     formUpdate : function(form, url, reset, callback){
11932         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11933             if(typeof url == "function"){
11934                 url = url.call(this);
11935             }
11936             form = Roo.getDom(form);
11937             this.transaction = Roo.Ajax.request({
11938                 form: form,
11939                 url:url,
11940                 success: this.successDelegate,
11941                 failure: this.failureDelegate,
11942                 timeout: (this.timeout*1000),
11943                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11944             });
11945             this.showLoading.defer(1, this);
11946         }
11947     },
11948
11949     /**
11950      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11951      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11952      */
11953     refresh : function(callback){
11954         if(this.defaultUrl == null){
11955             return;
11956         }
11957         this.update(this.defaultUrl, null, callback, true);
11958     },
11959
11960     /**
11961      * Set this element to auto refresh.
11962      * @param {Number} interval How often to update (in seconds).
11963      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11964      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11965      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11966      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11967      */
11968     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11969         if(refreshNow){
11970             this.update(url || this.defaultUrl, params, callback, true);
11971         }
11972         if(this.autoRefreshProcId){
11973             clearInterval(this.autoRefreshProcId);
11974         }
11975         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11976     },
11977
11978     /**
11979      * Stop auto refresh on this element.
11980      */
11981      stopAutoRefresh : function(){
11982         if(this.autoRefreshProcId){
11983             clearInterval(this.autoRefreshProcId);
11984             delete this.autoRefreshProcId;
11985         }
11986     },
11987
11988     isAutoRefreshing : function(){
11989        return this.autoRefreshProcId ? true : false;
11990     },
11991     /**
11992      * Called to update the element to "Loading" state. Override to perform custom action.
11993      */
11994     showLoading : function(){
11995         if(this.showLoadIndicator){
11996             this.el.update(this.indicatorText);
11997         }
11998     },
11999
12000     /**
12001      * Adds unique parameter to query string if disableCaching = true
12002      * @private
12003      */
12004     prepareUrl : function(url){
12005         if(this.disableCaching){
12006             var append = "_dc=" + (new Date().getTime());
12007             if(url.indexOf("?") !== -1){
12008                 url += "&" + append;
12009             }else{
12010                 url += "?" + append;
12011             }
12012         }
12013         return url;
12014     },
12015
12016     /**
12017      * @private
12018      */
12019     processSuccess : function(response){
12020         this.transaction = null;
12021         if(response.argument.form && response.argument.reset){
12022             try{ // put in try/catch since some older FF releases had problems with this
12023                 response.argument.form.reset();
12024             }catch(e){}
12025         }
12026         if(this.loadScripts){
12027             this.renderer.render(this.el, response, this,
12028                 this.updateComplete.createDelegate(this, [response]));
12029         }else{
12030             this.renderer.render(this.el, response, this);
12031             this.updateComplete(response);
12032         }
12033     },
12034
12035     updateComplete : function(response){
12036         this.fireEvent("update", this.el, response);
12037         if(typeof response.argument.callback == "function"){
12038             response.argument.callback(this.el, true, response);
12039         }
12040     },
12041
12042     /**
12043      * @private
12044      */
12045     processFailure : function(response){
12046         this.transaction = null;
12047         this.fireEvent("failure", this.el, response);
12048         if(typeof response.argument.callback == "function"){
12049             response.argument.callback(this.el, false, response);
12050         }
12051     },
12052
12053     /**
12054      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12055      * @param {Object} renderer The object implementing the render() method
12056      */
12057     setRenderer : function(renderer){
12058         this.renderer = renderer;
12059     },
12060
12061     getRenderer : function(){
12062        return this.renderer;
12063     },
12064
12065     /**
12066      * Set the defaultUrl used for updates
12067      * @param {String/Function} defaultUrl The url or a function to call to get the url
12068      */
12069     setDefaultUrl : function(defaultUrl){
12070         this.defaultUrl = defaultUrl;
12071     },
12072
12073     /**
12074      * Aborts the executing transaction
12075      */
12076     abort : function(){
12077         if(this.transaction){
12078             Roo.Ajax.abort(this.transaction);
12079         }
12080     },
12081
12082     /**
12083      * Returns true if an update is in progress
12084      * @return {Boolean}
12085      */
12086     isUpdating : function(){
12087         if(this.transaction){
12088             return Roo.Ajax.isLoading(this.transaction);
12089         }
12090         return false;
12091     }
12092 });
12093
12094 /**
12095  * @class Roo.UpdateManager.defaults
12096  * @static (not really - but it helps the doc tool)
12097  * The defaults collection enables customizing the default properties of UpdateManager
12098  */
12099    Roo.UpdateManager.defaults = {
12100        /**
12101          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12102          * @type Number
12103          */
12104          timeout : 30,
12105
12106          /**
12107          * True to process scripts by default (Defaults to false).
12108          * @type Boolean
12109          */
12110         loadScripts : false,
12111
12112         /**
12113         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12114         * @type String
12115         */
12116         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12117         /**
12118          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12119          * @type Boolean
12120          */
12121         disableCaching : false,
12122         /**
12123          * Whether to show indicatorText when loading (Defaults to true).
12124          * @type Boolean
12125          */
12126         showLoadIndicator : true,
12127         /**
12128          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12129          * @type String
12130          */
12131         indicatorText : '<div class="loading-indicator">Loading...</div>'
12132    };
12133
12134 /**
12135  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12136  *Usage:
12137  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12138  * @param {String/HTMLElement/Roo.Element} el The element to update
12139  * @param {String} url The url
12140  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12141  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12142  * @static
12143  * @deprecated
12144  * @member Roo.UpdateManager
12145  */
12146 Roo.UpdateManager.updateElement = function(el, url, params, options){
12147     var um = Roo.get(el, true).getUpdateManager();
12148     Roo.apply(um, options);
12149     um.update(url, params, options ? options.callback : null);
12150 };
12151 // alias for backwards compat
12152 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12153 /**
12154  * @class Roo.UpdateManager.BasicRenderer
12155  * Default Content renderer. Updates the elements innerHTML with the responseText.
12156  */
12157 Roo.UpdateManager.BasicRenderer = function(){};
12158
12159 Roo.UpdateManager.BasicRenderer.prototype = {
12160     /**
12161      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12162      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12163      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12164      * @param {Roo.Element} el The element being rendered
12165      * @param {Object} response The YUI Connect response object
12166      * @param {UpdateManager} updateManager The calling update manager
12167      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12168      */
12169      render : function(el, response, updateManager, callback){
12170         el.update(response.responseText, updateManager.loadScripts, callback);
12171     }
12172 };
12173 /*
12174  * Based on:
12175  * Roo JS
12176  * (c)) Alan Knowles
12177  * Licence : LGPL
12178  */
12179
12180
12181 /**
12182  * @class Roo.DomTemplate
12183  * @extends Roo.Template
12184  * An effort at a dom based template engine..
12185  *
12186  * Similar to XTemplate, except it uses dom parsing to create the template..
12187  *
12188  * Supported features:
12189  *
12190  *  Tags:
12191
12192 <pre><code>
12193       {a_variable} - output encoded.
12194       {a_variable.format:("Y-m-d")} - call a method on the variable
12195       {a_variable:raw} - unencoded output
12196       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12197       {a_variable:this.method_on_template(...)} - call a method on the template object.
12198  
12199 </code></pre>
12200  *  The tpl tag:
12201 <pre><code>
12202         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12203         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12204         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12205         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12206   
12207 </code></pre>
12208  *      
12209  */
12210 Roo.DomTemplate = function()
12211 {
12212      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12213      if (this.html) {
12214         this.compile();
12215      }
12216 };
12217
12218
12219 Roo.extend(Roo.DomTemplate, Roo.Template, {
12220     /**
12221      * id counter for sub templates.
12222      */
12223     id : 0,
12224     /**
12225      * flag to indicate if dom parser is inside a pre,
12226      * it will strip whitespace if not.
12227      */
12228     inPre : false,
12229     
12230     /**
12231      * The various sub templates
12232      */
12233     tpls : false,
12234     
12235     
12236     
12237     /**
12238      *
12239      * basic tag replacing syntax
12240      * WORD:WORD()
12241      *
12242      * // you can fake an object call by doing this
12243      *  x.t:(test,tesT) 
12244      * 
12245      */
12246     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12247     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12248     
12249     iterChild : function (node, method) {
12250         
12251         var oldPre = this.inPre;
12252         if (node.tagName == 'PRE') {
12253             this.inPre = true;
12254         }
12255         for( var i = 0; i < node.childNodes.length; i++) {
12256             method.call(this, node.childNodes[i]);
12257         }
12258         this.inPre = oldPre;
12259     },
12260     
12261     
12262     
12263     /**
12264      * compile the template
12265      *
12266      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12267      *
12268      */
12269     compile: function()
12270     {
12271         var s = this.html;
12272         
12273         // covert the html into DOM...
12274         var doc = false;
12275         var div =false;
12276         try {
12277             doc = document.implementation.createHTMLDocument("");
12278             doc.documentElement.innerHTML =   this.html  ;
12279             div = doc.documentElement;
12280         } catch (e) {
12281             // old IE... - nasty -- it causes all sorts of issues.. with
12282             // images getting pulled from server..
12283             div = document.createElement('div');
12284             div.innerHTML = this.html;
12285         }
12286         //doc.documentElement.innerHTML = htmlBody
12287          
12288         
12289         
12290         this.tpls = [];
12291         var _t = this;
12292         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12293         
12294         var tpls = this.tpls;
12295         
12296         // create a top level template from the snippet..
12297         
12298         //Roo.log(div.innerHTML);
12299         
12300         var tpl = {
12301             uid : 'master',
12302             id : this.id++,
12303             attr : false,
12304             value : false,
12305             body : div.innerHTML,
12306             
12307             forCall : false,
12308             execCall : false,
12309             dom : div,
12310             isTop : true
12311             
12312         };
12313         tpls.unshift(tpl);
12314         
12315         
12316         // compile them...
12317         this.tpls = [];
12318         Roo.each(tpls, function(tp){
12319             this.compileTpl(tp);
12320             this.tpls[tp.id] = tp;
12321         }, this);
12322         
12323         this.master = tpls[0];
12324         return this;
12325         
12326         
12327     },
12328     
12329     compileNode : function(node, istop) {
12330         // test for
12331         //Roo.log(node);
12332         
12333         
12334         // skip anything not a tag..
12335         if (node.nodeType != 1) {
12336             if (node.nodeType == 3 && !this.inPre) {
12337                 // reduce white space..
12338                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12339                 
12340             }
12341             return;
12342         }
12343         
12344         var tpl = {
12345             uid : false,
12346             id : false,
12347             attr : false,
12348             value : false,
12349             body : '',
12350             
12351             forCall : false,
12352             execCall : false,
12353             dom : false,
12354             isTop : istop
12355             
12356             
12357         };
12358         
12359         
12360         switch(true) {
12361             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12362             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12363             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12364             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12365             // no default..
12366         }
12367         
12368         
12369         if (!tpl.attr) {
12370             // just itterate children..
12371             this.iterChild(node,this.compileNode);
12372             return;
12373         }
12374         tpl.uid = this.id++;
12375         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12376         node.removeAttribute('roo-'+ tpl.attr);
12377         if (tpl.attr != 'name') {
12378             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12379             node.parentNode.replaceChild(placeholder,  node);
12380         } else {
12381             
12382             var placeholder =  document.createElement('span');
12383             placeholder.className = 'roo-tpl-' + tpl.value;
12384             node.parentNode.replaceChild(placeholder,  node);
12385         }
12386         
12387         // parent now sees '{domtplXXXX}
12388         this.iterChild(node,this.compileNode);
12389         
12390         // we should now have node body...
12391         var div = document.createElement('div');
12392         div.appendChild(node);
12393         tpl.dom = node;
12394         // this has the unfortunate side effect of converting tagged attributes
12395         // eg. href="{...}" into %7C...%7D
12396         // this has been fixed by searching for those combo's although it's a bit hacky..
12397         
12398         
12399         tpl.body = div.innerHTML;
12400         
12401         
12402          
12403         tpl.id = tpl.uid;
12404         switch(tpl.attr) {
12405             case 'for' :
12406                 switch (tpl.value) {
12407                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12408                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12409                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12410                 }
12411                 break;
12412             
12413             case 'exec':
12414                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12415                 break;
12416             
12417             case 'if':     
12418                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12419                 break;
12420             
12421             case 'name':
12422                 tpl.id  = tpl.value; // replace non characters???
12423                 break;
12424             
12425         }
12426         
12427         
12428         this.tpls.push(tpl);
12429         
12430         
12431         
12432     },
12433     
12434     
12435     
12436     
12437     /**
12438      * Compile a segment of the template into a 'sub-template'
12439      *
12440      * 
12441      * 
12442      *
12443      */
12444     compileTpl : function(tpl)
12445     {
12446         var fm = Roo.util.Format;
12447         var useF = this.disableFormats !== true;
12448         
12449         var sep = Roo.isGecko ? "+\n" : ",\n";
12450         
12451         var undef = function(str) {
12452             Roo.debug && Roo.log("Property not found :"  + str);
12453             return '';
12454         };
12455           
12456         //Roo.log(tpl.body);
12457         
12458         
12459         
12460         var fn = function(m, lbrace, name, format, args)
12461         {
12462             //Roo.log("ARGS");
12463             //Roo.log(arguments);
12464             args = args ? args.replace(/\\'/g,"'") : args;
12465             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12466             if (typeof(format) == 'undefined') {
12467                 format =  'htmlEncode'; 
12468             }
12469             if (format == 'raw' ) {
12470                 format = false;
12471             }
12472             
12473             if(name.substr(0, 6) == 'domtpl'){
12474                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12475             }
12476             
12477             // build an array of options to determine if value is undefined..
12478             
12479             // basically get 'xxxx.yyyy' then do
12480             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12481             //    (function () { Roo.log("Property not found"); return ''; })() :
12482             //    ......
12483             
12484             var udef_ar = [];
12485             var lookfor = '';
12486             Roo.each(name.split('.'), function(st) {
12487                 lookfor += (lookfor.length ? '.': '') + st;
12488                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12489             });
12490             
12491             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12492             
12493             
12494             if(format && useF){
12495                 
12496                 args = args ? ',' + args : "";
12497                  
12498                 if(format.substr(0, 5) != "this."){
12499                     format = "fm." + format + '(';
12500                 }else{
12501                     format = 'this.call("'+ format.substr(5) + '", ';
12502                     args = ", values";
12503                 }
12504                 
12505                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12506             }
12507              
12508             if (args && args.length) {
12509                 // called with xxyx.yuu:(test,test)
12510                 // change to ()
12511                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12512             }
12513             // raw.. - :raw modifier..
12514             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12515             
12516         };
12517         var body;
12518         // branched to use + in gecko and [].join() in others
12519         if(Roo.isGecko){
12520             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12521                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12522                     "';};};";
12523         }else{
12524             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12525             body.push(tpl.body.replace(/(\r\n|\n)/g,
12526                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12527             body.push("'].join('');};};");
12528             body = body.join('');
12529         }
12530         
12531         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12532        
12533         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12534         eval(body);
12535         
12536         return this;
12537     },
12538      
12539     /**
12540      * same as applyTemplate, except it's done to one of the subTemplates
12541      * when using named templates, you can do:
12542      *
12543      * var str = pl.applySubTemplate('your-name', values);
12544      *
12545      * 
12546      * @param {Number} id of the template
12547      * @param {Object} values to apply to template
12548      * @param {Object} parent (normaly the instance of this object)
12549      */
12550     applySubTemplate : function(id, values, parent)
12551     {
12552         
12553         
12554         var t = this.tpls[id];
12555         
12556         
12557         try { 
12558             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12559                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12560                 return '';
12561             }
12562         } catch(e) {
12563             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12564             Roo.log(values);
12565           
12566             return '';
12567         }
12568         try { 
12569             
12570             if(t.execCall && t.execCall.call(this, values, parent)){
12571                 return '';
12572             }
12573         } catch(e) {
12574             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12575             Roo.log(values);
12576             return '';
12577         }
12578         
12579         try {
12580             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12581             parent = t.target ? values : parent;
12582             if(t.forCall && vs instanceof Array){
12583                 var buf = [];
12584                 for(var i = 0, len = vs.length; i < len; i++){
12585                     try {
12586                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12587                     } catch (e) {
12588                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12589                         Roo.log(e.body);
12590                         //Roo.log(t.compiled);
12591                         Roo.log(vs[i]);
12592                     }   
12593                 }
12594                 return buf.join('');
12595             }
12596         } catch (e) {
12597             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12598             Roo.log(values);
12599             return '';
12600         }
12601         try {
12602             return t.compiled.call(this, vs, parent);
12603         } catch (e) {
12604             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12605             Roo.log(e.body);
12606             //Roo.log(t.compiled);
12607             Roo.log(values);
12608             return '';
12609         }
12610     },
12611
12612    
12613
12614     applyTemplate : function(values){
12615         return this.master.compiled.call(this, values, {});
12616         //var s = this.subs;
12617     },
12618
12619     apply : function(){
12620         return this.applyTemplate.apply(this, arguments);
12621     }
12622
12623  });
12624
12625 Roo.DomTemplate.from = function(el){
12626     el = Roo.getDom(el);
12627     return new Roo.Domtemplate(el.value || el.innerHTML);
12628 };/*
12629  * Based on:
12630  * Ext JS Library 1.1.1
12631  * Copyright(c) 2006-2007, Ext JS, LLC.
12632  *
12633  * Originally Released Under LGPL - original licence link has changed is not relivant.
12634  *
12635  * Fork - LGPL
12636  * <script type="text/javascript">
12637  */
12638
12639 /**
12640  * @class Roo.util.DelayedTask
12641  * Provides a convenient method of performing setTimeout where a new
12642  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12643  * You can use this class to buffer
12644  * the keypress events for a certain number of milliseconds, and perform only if they stop
12645  * for that amount of time.
12646  * @constructor The parameters to this constructor serve as defaults and are not required.
12647  * @param {Function} fn (optional) The default function to timeout
12648  * @param {Object} scope (optional) The default scope of that timeout
12649  * @param {Array} args (optional) The default Array of arguments
12650  */
12651 Roo.util.DelayedTask = function(fn, scope, args){
12652     var id = null, d, t;
12653
12654     var call = function(){
12655         var now = new Date().getTime();
12656         if(now - t >= d){
12657             clearInterval(id);
12658             id = null;
12659             fn.apply(scope, args || []);
12660         }
12661     };
12662     /**
12663      * Cancels any pending timeout and queues a new one
12664      * @param {Number} delay The milliseconds to delay
12665      * @param {Function} newFn (optional) Overrides function passed to constructor
12666      * @param {Object} newScope (optional) Overrides scope passed to constructor
12667      * @param {Array} newArgs (optional) Overrides args passed to constructor
12668      */
12669     this.delay = function(delay, newFn, newScope, newArgs){
12670         if(id && delay != d){
12671             this.cancel();
12672         }
12673         d = delay;
12674         t = new Date().getTime();
12675         fn = newFn || fn;
12676         scope = newScope || scope;
12677         args = newArgs || args;
12678         if(!id){
12679             id = setInterval(call, d);
12680         }
12681     };
12682
12683     /**
12684      * Cancel the last queued timeout
12685      */
12686     this.cancel = function(){
12687         if(id){
12688             clearInterval(id);
12689             id = null;
12690         }
12691     };
12692 };/*
12693  * Based on:
12694  * Ext JS Library 1.1.1
12695  * Copyright(c) 2006-2007, Ext JS, LLC.
12696  *
12697  * Originally Released Under LGPL - original licence link has changed is not relivant.
12698  *
12699  * Fork - LGPL
12700  * <script type="text/javascript">
12701  */
12702  
12703  
12704 Roo.util.TaskRunner = function(interval){
12705     interval = interval || 10;
12706     var tasks = [], removeQueue = [];
12707     var id = 0;
12708     var running = false;
12709
12710     var stopThread = function(){
12711         running = false;
12712         clearInterval(id);
12713         id = 0;
12714     };
12715
12716     var startThread = function(){
12717         if(!running){
12718             running = true;
12719             id = setInterval(runTasks, interval);
12720         }
12721     };
12722
12723     var removeTask = function(task){
12724         removeQueue.push(task);
12725         if(task.onStop){
12726             task.onStop();
12727         }
12728     };
12729
12730     var runTasks = function(){
12731         if(removeQueue.length > 0){
12732             for(var i = 0, len = removeQueue.length; i < len; i++){
12733                 tasks.remove(removeQueue[i]);
12734             }
12735             removeQueue = [];
12736             if(tasks.length < 1){
12737                 stopThread();
12738                 return;
12739             }
12740         }
12741         var now = new Date().getTime();
12742         for(var i = 0, len = tasks.length; i < len; ++i){
12743             var t = tasks[i];
12744             var itime = now - t.taskRunTime;
12745             if(t.interval <= itime){
12746                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12747                 t.taskRunTime = now;
12748                 if(rt === false || t.taskRunCount === t.repeat){
12749                     removeTask(t);
12750                     return;
12751                 }
12752             }
12753             if(t.duration && t.duration <= (now - t.taskStartTime)){
12754                 removeTask(t);
12755             }
12756         }
12757     };
12758
12759     /**
12760      * Queues a new task.
12761      * @param {Object} task
12762      */
12763     this.start = function(task){
12764         tasks.push(task);
12765         task.taskStartTime = new Date().getTime();
12766         task.taskRunTime = 0;
12767         task.taskRunCount = 0;
12768         startThread();
12769         return task;
12770     };
12771
12772     this.stop = function(task){
12773         removeTask(task);
12774         return task;
12775     };
12776
12777     this.stopAll = function(){
12778         stopThread();
12779         for(var i = 0, len = tasks.length; i < len; i++){
12780             if(tasks[i].onStop){
12781                 tasks[i].onStop();
12782             }
12783         }
12784         tasks = [];
12785         removeQueue = [];
12786     };
12787 };
12788
12789 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12790  * Based on:
12791  * Ext JS Library 1.1.1
12792  * Copyright(c) 2006-2007, Ext JS, LLC.
12793  *
12794  * Originally Released Under LGPL - original licence link has changed is not relivant.
12795  *
12796  * Fork - LGPL
12797  * <script type="text/javascript">
12798  */
12799
12800  
12801 /**
12802  * @class Roo.util.MixedCollection
12803  * @extends Roo.util.Observable
12804  * A Collection class that maintains both numeric indexes and keys and exposes events.
12805  * @constructor
12806  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12807  * collection (defaults to false)
12808  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12809  * and return the key value for that item.  This is used when available to look up the key on items that
12810  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12811  * equivalent to providing an implementation for the {@link #getKey} method.
12812  */
12813 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12814     this.items = [];
12815     this.map = {};
12816     this.keys = [];
12817     this.length = 0;
12818     this.addEvents({
12819         /**
12820          * @event clear
12821          * Fires when the collection is cleared.
12822          */
12823         "clear" : true,
12824         /**
12825          * @event add
12826          * Fires when an item is added to the collection.
12827          * @param {Number} index The index at which the item was added.
12828          * @param {Object} o The item added.
12829          * @param {String} key The key associated with the added item.
12830          */
12831         "add" : true,
12832         /**
12833          * @event replace
12834          * Fires when an item is replaced in the collection.
12835          * @param {String} key he key associated with the new added.
12836          * @param {Object} old The item being replaced.
12837          * @param {Object} new The new item.
12838          */
12839         "replace" : true,
12840         /**
12841          * @event remove
12842          * Fires when an item is removed from the collection.
12843          * @param {Object} o The item being removed.
12844          * @param {String} key (optional) The key associated with the removed item.
12845          */
12846         "remove" : true,
12847         "sort" : true
12848     });
12849     this.allowFunctions = allowFunctions === true;
12850     if(keyFn){
12851         this.getKey = keyFn;
12852     }
12853     Roo.util.MixedCollection.superclass.constructor.call(this);
12854 };
12855
12856 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12857     allowFunctions : false,
12858     
12859 /**
12860  * Adds an item to the collection.
12861  * @param {String} key The key to associate with the item
12862  * @param {Object} o The item to add.
12863  * @return {Object} The item added.
12864  */
12865     add : function(key, o){
12866         if(arguments.length == 1){
12867             o = arguments[0];
12868             key = this.getKey(o);
12869         }
12870         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         this.owner.fireEvent('editorevent', this, e);
41614       //  this.updateToolbar();
41615         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41616     },
41617
41618     insertTag : function(tg)
41619     {
41620         // could be a bit smarter... -> wrap the current selected tRoo..
41621         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41622             
41623             range = this.createRange(this.getSelection());
41624             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41625             wrappingNode.appendChild(range.extractContents());
41626             range.insertNode(wrappingNode);
41627
41628             return;
41629             
41630             
41631             
41632         }
41633         this.execCmd("formatblock",   tg);
41634         
41635     },
41636     
41637     insertText : function(txt)
41638     {
41639         
41640         
41641         var range = this.createRange();
41642         range.deleteContents();
41643                //alert(Sender.getAttribute('label'));
41644                
41645         range.insertNode(this.doc.createTextNode(txt));
41646     } ,
41647     
41648      
41649
41650     /**
41651      * Executes a Midas editor command on the editor document and performs necessary focus and
41652      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41653      * @param {String} cmd The Midas command
41654      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41655      */
41656     relayCmd : function(cmd, value){
41657         this.win.focus();
41658         this.execCmd(cmd, value);
41659         this.owner.fireEvent('editorevent', this);
41660         //this.updateToolbar();
41661         this.owner.deferFocus();
41662     },
41663
41664     /**
41665      * Executes a Midas editor command directly on the editor document.
41666      * For visual commands, you should use {@link #relayCmd} instead.
41667      * <b>This should only be called after the editor is initialized.</b>
41668      * @param {String} cmd The Midas command
41669      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41670      */
41671     execCmd : function(cmd, value){
41672         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41673         this.syncValue();
41674     },
41675  
41676  
41677    
41678     /**
41679      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41680      * to insert tRoo.
41681      * @param {String} text | dom node.. 
41682      */
41683     insertAtCursor : function(text)
41684     {
41685         
41686         
41687         
41688         if(!this.activated){
41689             return;
41690         }
41691         /*
41692         if(Roo.isIE){
41693             this.win.focus();
41694             var r = this.doc.selection.createRange();
41695             if(r){
41696                 r.collapse(true);
41697                 r.pasteHTML(text);
41698                 this.syncValue();
41699                 this.deferFocus();
41700             
41701             }
41702             return;
41703         }
41704         */
41705         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41706             this.win.focus();
41707             
41708             
41709             // from jquery ui (MIT licenced)
41710             var range, node;
41711             var win = this.win;
41712             
41713             if (win.getSelection && win.getSelection().getRangeAt) {
41714                 range = win.getSelection().getRangeAt(0);
41715                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41716                 range.insertNode(node);
41717             } else if (win.document.selection && win.document.selection.createRange) {
41718                 // no firefox support
41719                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41720                 win.document.selection.createRange().pasteHTML(txt);
41721             } else {
41722                 // no firefox support
41723                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41724                 this.execCmd('InsertHTML', txt);
41725             } 
41726             
41727             this.syncValue();
41728             
41729             this.deferFocus();
41730         }
41731     },
41732  // private
41733     mozKeyPress : function(e){
41734         if(e.ctrlKey){
41735             var c = e.getCharCode(), cmd;
41736           
41737             if(c > 0){
41738                 c = String.fromCharCode(c).toLowerCase();
41739                 switch(c){
41740                     case 'b':
41741                         cmd = 'bold';
41742                         break;
41743                     case 'i':
41744                         cmd = 'italic';
41745                         break;
41746                     
41747                     case 'u':
41748                         cmd = 'underline';
41749                         break;
41750                     
41751                     case 'v':
41752                         this.cleanUpPaste.defer(100, this);
41753                         return;
41754                         
41755                 }
41756                 if(cmd){
41757                     this.win.focus();
41758                     this.execCmd(cmd);
41759                     this.deferFocus();
41760                     e.preventDefault();
41761                 }
41762                 
41763             }
41764         }
41765     },
41766
41767     // private
41768     fixKeys : function(){ // load time branching for fastest keydown performance
41769         if(Roo.isIE){
41770             return function(e){
41771                 var k = e.getKey(), r;
41772                 if(k == e.TAB){
41773                     e.stopEvent();
41774                     r = this.doc.selection.createRange();
41775                     if(r){
41776                         r.collapse(true);
41777                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41778                         this.deferFocus();
41779                     }
41780                     return;
41781                 }
41782                 
41783                 if(k == e.ENTER){
41784                     r = this.doc.selection.createRange();
41785                     if(r){
41786                         var target = r.parentElement();
41787                         if(!target || target.tagName.toLowerCase() != 'li'){
41788                             e.stopEvent();
41789                             r.pasteHTML('<br />');
41790                             r.collapse(false);
41791                             r.select();
41792                         }
41793                     }
41794                 }
41795                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41796                     this.cleanUpPaste.defer(100, this);
41797                     return;
41798                 }
41799                 
41800                 
41801             };
41802         }else if(Roo.isOpera){
41803             return function(e){
41804                 var k = e.getKey();
41805                 if(k == e.TAB){
41806                     e.stopEvent();
41807                     this.win.focus();
41808                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41809                     this.deferFocus();
41810                 }
41811                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41812                     this.cleanUpPaste.defer(100, this);
41813                     return;
41814                 }
41815                 
41816             };
41817         }else if(Roo.isSafari){
41818             return function(e){
41819                 var k = e.getKey();
41820                 
41821                 if(k == e.TAB){
41822                     e.stopEvent();
41823                     this.execCmd('InsertText','\t');
41824                     this.deferFocus();
41825                     return;
41826                 }
41827                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41828                     this.cleanUpPaste.defer(100, this);
41829                     return;
41830                 }
41831                 
41832              };
41833         }
41834     }(),
41835     
41836     getAllAncestors: function()
41837     {
41838         var p = this.getSelectedNode();
41839         var a = [];
41840         if (!p) {
41841             a.push(p); // push blank onto stack..
41842             p = this.getParentElement();
41843         }
41844         
41845         
41846         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41847             a.push(p);
41848             p = p.parentNode;
41849         }
41850         a.push(this.doc.body);
41851         return a;
41852     },
41853     lastSel : false,
41854     lastSelNode : false,
41855     
41856     
41857     getSelection : function() 
41858     {
41859         this.assignDocWin();
41860         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41861     },
41862     
41863     getSelectedNode: function() 
41864     {
41865         // this may only work on Gecko!!!
41866         
41867         // should we cache this!!!!
41868         
41869         
41870         
41871          
41872         var range = this.createRange(this.getSelection()).cloneRange();
41873         
41874         if (Roo.isIE) {
41875             var parent = range.parentElement();
41876             while (true) {
41877                 var testRange = range.duplicate();
41878                 testRange.moveToElementText(parent);
41879                 if (testRange.inRange(range)) {
41880                     break;
41881                 }
41882                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41883                     break;
41884                 }
41885                 parent = parent.parentElement;
41886             }
41887             return parent;
41888         }
41889         
41890         // is ancestor a text element.
41891         var ac =  range.commonAncestorContainer;
41892         if (ac.nodeType == 3) {
41893             ac = ac.parentNode;
41894         }
41895         
41896         var ar = ac.childNodes;
41897          
41898         var nodes = [];
41899         var other_nodes = [];
41900         var has_other_nodes = false;
41901         for (var i=0;i<ar.length;i++) {
41902             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41903                 continue;
41904             }
41905             // fullly contained node.
41906             
41907             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41908                 nodes.push(ar[i]);
41909                 continue;
41910             }
41911             
41912             // probably selected..
41913             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41914                 other_nodes.push(ar[i]);
41915                 continue;
41916             }
41917             // outer..
41918             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41919                 continue;
41920             }
41921             
41922             
41923             has_other_nodes = true;
41924         }
41925         if (!nodes.length && other_nodes.length) {
41926             nodes= other_nodes;
41927         }
41928         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41929             return false;
41930         }
41931         
41932         return nodes[0];
41933     },
41934     createRange: function(sel)
41935     {
41936         // this has strange effects when using with 
41937         // top toolbar - not sure if it's a great idea.
41938         //this.editor.contentWindow.focus();
41939         if (typeof sel != "undefined") {
41940             try {
41941                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41942             } catch(e) {
41943                 return this.doc.createRange();
41944             }
41945         } else {
41946             return this.doc.createRange();
41947         }
41948     },
41949     getParentElement: function()
41950     {
41951         
41952         this.assignDocWin();
41953         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41954         
41955         var range = this.createRange(sel);
41956          
41957         try {
41958             var p = range.commonAncestorContainer;
41959             while (p.nodeType == 3) { // text node
41960                 p = p.parentNode;
41961             }
41962             return p;
41963         } catch (e) {
41964             return null;
41965         }
41966     
41967     },
41968     /***
41969      *
41970      * Range intersection.. the hard stuff...
41971      *  '-1' = before
41972      *  '0' = hits..
41973      *  '1' = after.
41974      *         [ -- selected range --- ]
41975      *   [fail]                        [fail]
41976      *
41977      *    basically..
41978      *      if end is before start or  hits it. fail.
41979      *      if start is after end or hits it fail.
41980      *
41981      *   if either hits (but other is outside. - then it's not 
41982      *   
41983      *    
41984      **/
41985     
41986     
41987     // @see http://www.thismuchiknow.co.uk/?p=64.
41988     rangeIntersectsNode : function(range, node)
41989     {
41990         var nodeRange = node.ownerDocument.createRange();
41991         try {
41992             nodeRange.selectNode(node);
41993         } catch (e) {
41994             nodeRange.selectNodeContents(node);
41995         }
41996     
41997         var rangeStartRange = range.cloneRange();
41998         rangeStartRange.collapse(true);
41999     
42000         var rangeEndRange = range.cloneRange();
42001         rangeEndRange.collapse(false);
42002     
42003         var nodeStartRange = nodeRange.cloneRange();
42004         nodeStartRange.collapse(true);
42005     
42006         var nodeEndRange = nodeRange.cloneRange();
42007         nodeEndRange.collapse(false);
42008     
42009         return rangeStartRange.compareBoundaryPoints(
42010                  Range.START_TO_START, nodeEndRange) == -1 &&
42011                rangeEndRange.compareBoundaryPoints(
42012                  Range.START_TO_START, nodeStartRange) == 1;
42013         
42014          
42015     },
42016     rangeCompareNode : function(range, node)
42017     {
42018         var nodeRange = node.ownerDocument.createRange();
42019         try {
42020             nodeRange.selectNode(node);
42021         } catch (e) {
42022             nodeRange.selectNodeContents(node);
42023         }
42024         
42025         
42026         range.collapse(true);
42027     
42028         nodeRange.collapse(true);
42029      
42030         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42031         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42032          
42033         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42034         
42035         var nodeIsBefore   =  ss == 1;
42036         var nodeIsAfter    = ee == -1;
42037         
42038         if (nodeIsBefore && nodeIsAfter)
42039             return 0; // outer
42040         if (!nodeIsBefore && nodeIsAfter)
42041             return 1; //right trailed.
42042         
42043         if (nodeIsBefore && !nodeIsAfter)
42044             return 2;  // left trailed.
42045         // fully contined.
42046         return 3;
42047     },
42048
42049     // private? - in a new class?
42050     cleanUpPaste :  function()
42051     {
42052         // cleans up the whole document..
42053         Roo.log('cleanuppaste');
42054         
42055         this.cleanUpChildren(this.doc.body);
42056         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42057         if (clean != this.doc.body.innerHTML) {
42058             this.doc.body.innerHTML = clean;
42059         }
42060         
42061     },
42062     
42063     cleanWordChars : function(input) {// change the chars to hex code
42064         var he = Roo.HtmlEditorCore;
42065         
42066         var output = input;
42067         Roo.each(he.swapCodes, function(sw) { 
42068             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42069             
42070             output = output.replace(swapper, sw[1]);
42071         });
42072         
42073         return output;
42074     },
42075     
42076     
42077     cleanUpChildren : function (n)
42078     {
42079         if (!n.childNodes.length) {
42080             return;
42081         }
42082         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42083            this.cleanUpChild(n.childNodes[i]);
42084         }
42085     },
42086     
42087     
42088         
42089     
42090     cleanUpChild : function (node)
42091     {
42092         var ed = this;
42093         //console.log(node);
42094         if (node.nodeName == "#text") {
42095             // clean up silly Windows -- stuff?
42096             return; 
42097         }
42098         if (node.nodeName == "#comment") {
42099             node.parentNode.removeChild(node);
42100             // clean up silly Windows -- stuff?
42101             return; 
42102         }
42103         var lcname = node.tagName.toLowerCase();
42104         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42105         // whitelist of tags..
42106         
42107         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42108             // remove node.
42109             node.parentNode.removeChild(node);
42110             return;
42111             
42112         }
42113         
42114         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42115         
42116         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42117         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42118         
42119         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42120         //    remove_keep_children = true;
42121         //}
42122         
42123         if (remove_keep_children) {
42124             this.cleanUpChildren(node);
42125             // inserts everything just before this node...
42126             while (node.childNodes.length) {
42127                 var cn = node.childNodes[0];
42128                 node.removeChild(cn);
42129                 node.parentNode.insertBefore(cn, node);
42130             }
42131             node.parentNode.removeChild(node);
42132             return;
42133         }
42134         
42135         if (!node.attributes || !node.attributes.length) {
42136             this.cleanUpChildren(node);
42137             return;
42138         }
42139         
42140         function cleanAttr(n,v)
42141         {
42142             
42143             if (v.match(/^\./) || v.match(/^\//)) {
42144                 return;
42145             }
42146             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42147                 return;
42148             }
42149             if (v.match(/^#/)) {
42150                 return;
42151             }
42152 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42153             node.removeAttribute(n);
42154             
42155         }
42156         
42157         var cwhite = this.cwhite;
42158         var cblack = this.cblack;
42159             
42160         function cleanStyle(n,v)
42161         {
42162             if (v.match(/expression/)) { //XSS?? should we even bother..
42163                 node.removeAttribute(n);
42164                 return;
42165             }
42166             
42167             var parts = v.split(/;/);
42168             var clean = [];
42169             
42170             Roo.each(parts, function(p) {
42171                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42172                 if (!p.length) {
42173                     return true;
42174                 }
42175                 var l = p.split(':').shift().replace(/\s+/g,'');
42176                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42177                 
42178                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42179 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42180                     //node.removeAttribute(n);
42181                     return true;
42182                 }
42183                 //Roo.log()
42184                 // only allow 'c whitelisted system attributes'
42185                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42186 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42187                     //node.removeAttribute(n);
42188                     return true;
42189                 }
42190                 
42191                 
42192                  
42193                 
42194                 clean.push(p);
42195                 return true;
42196             });
42197             if (clean.length) { 
42198                 node.setAttribute(n, clean.join(';'));
42199             } else {
42200                 node.removeAttribute(n);
42201             }
42202             
42203         }
42204         
42205         
42206         for (var i = node.attributes.length-1; i > -1 ; i--) {
42207             var a = node.attributes[i];
42208             //console.log(a);
42209             
42210             if (a.name.toLowerCase().substr(0,2)=='on')  {
42211                 node.removeAttribute(a.name);
42212                 continue;
42213             }
42214             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42215                 node.removeAttribute(a.name);
42216                 continue;
42217             }
42218             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42219                 cleanAttr(a.name,a.value); // fixme..
42220                 continue;
42221             }
42222             if (a.name == 'style') {
42223                 cleanStyle(a.name,a.value);
42224                 continue;
42225             }
42226             /// clean up MS crap..
42227             // tecnically this should be a list of valid class'es..
42228             
42229             
42230             if (a.name == 'class') {
42231                 if (a.value.match(/^Mso/)) {
42232                     node.className = '';
42233                 }
42234                 
42235                 if (a.value.match(/body/)) {
42236                     node.className = '';
42237                 }
42238                 continue;
42239             }
42240             
42241             // style cleanup!?
42242             // class cleanup?
42243             
42244         }
42245         
42246         
42247         this.cleanUpChildren(node);
42248         
42249         
42250     },
42251     /**
42252      * Clean up MS wordisms...
42253      */
42254     cleanWord : function(node)
42255     {
42256         var _t = this;
42257         var cleanWordChildren = function()
42258         {
42259             if (!node.childNodes.length) {
42260                 return;
42261             }
42262             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42263                _t.cleanWord(node.childNodes[i]);
42264             }
42265         }
42266         
42267         
42268         if (!node) {
42269             this.cleanWord(this.doc.body);
42270             return;
42271         }
42272         if (node.nodeName == "#text") {
42273             // clean up silly Windows -- stuff?
42274             return; 
42275         }
42276         if (node.nodeName == "#comment") {
42277             node.parentNode.removeChild(node);
42278             // clean up silly Windows -- stuff?
42279             return; 
42280         }
42281         
42282         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42283             node.parentNode.removeChild(node);
42284             return;
42285         }
42286         
42287         // remove - but keep children..
42288         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42289             while (node.childNodes.length) {
42290                 var cn = node.childNodes[0];
42291                 node.removeChild(cn);
42292                 node.parentNode.insertBefore(cn, node);
42293             }
42294             node.parentNode.removeChild(node);
42295             cleanWordChildren();
42296             return;
42297         }
42298         // clean styles
42299         if (node.className.length) {
42300             
42301             var cn = node.className.split(/\W+/);
42302             var cna = [];
42303             Roo.each(cn, function(cls) {
42304                 if (cls.match(/Mso[a-zA-Z]+/)) {
42305                     return;
42306                 }
42307                 cna.push(cls);
42308             });
42309             node.className = cna.length ? cna.join(' ') : '';
42310             if (!cna.length) {
42311                 node.removeAttribute("class");
42312             }
42313         }
42314         
42315         if (node.hasAttribute("lang")) {
42316             node.removeAttribute("lang");
42317         }
42318         
42319         if (node.hasAttribute("style")) {
42320             
42321             var styles = node.getAttribute("style").split(";");
42322             var nstyle = [];
42323             Roo.each(styles, function(s) {
42324                 if (!s.match(/:/)) {
42325                     return;
42326                 }
42327                 var kv = s.split(":");
42328                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42329                     return;
42330                 }
42331                 // what ever is left... we allow.
42332                 nstyle.push(s);
42333             });
42334             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42335             if (!nstyle.length) {
42336                 node.removeAttribute('style');
42337             }
42338         }
42339         
42340         cleanWordChildren();
42341         
42342         
42343     },
42344     domToHTML : function(currentElement, depth, nopadtext) {
42345         
42346         depth = depth || 0;
42347         nopadtext = nopadtext || false;
42348     
42349         if (!currentElement) {
42350             return this.domToHTML(this.doc.body);
42351         }
42352         
42353         //Roo.log(currentElement);
42354         var j;
42355         var allText = false;
42356         var nodeName = currentElement.nodeName;
42357         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42358         
42359         if  (nodeName == '#text') {
42360             
42361             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42362         }
42363         
42364         
42365         var ret = '';
42366         if (nodeName != 'BODY') {
42367              
42368             var i = 0;
42369             // Prints the node tagName, such as <A>, <IMG>, etc
42370             if (tagName) {
42371                 var attr = [];
42372                 for(i = 0; i < currentElement.attributes.length;i++) {
42373                     // quoting?
42374                     var aname = currentElement.attributes.item(i).name;
42375                     if (!currentElement.attributes.item(i).value.length) {
42376                         continue;
42377                     }
42378                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42379                 }
42380                 
42381                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42382             } 
42383             else {
42384                 
42385                 // eack
42386             }
42387         } else {
42388             tagName = false;
42389         }
42390         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42391             return ret;
42392         }
42393         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42394             nopadtext = true;
42395         }
42396         
42397         
42398         // Traverse the tree
42399         i = 0;
42400         var currentElementChild = currentElement.childNodes.item(i);
42401         var allText = true;
42402         var innerHTML  = '';
42403         lastnode = '';
42404         while (currentElementChild) {
42405             // Formatting code (indent the tree so it looks nice on the screen)
42406             var nopad = nopadtext;
42407             if (lastnode == 'SPAN') {
42408                 nopad  = true;
42409             }
42410             // text
42411             if  (currentElementChild.nodeName == '#text') {
42412                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42413                 toadd = nopadtext ? toadd : toadd.trim();
42414                 if (!nopad && toadd.length > 80) {
42415                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42416                 }
42417                 innerHTML  += toadd;
42418                 
42419                 i++;
42420                 currentElementChild = currentElement.childNodes.item(i);
42421                 lastNode = '';
42422                 continue;
42423             }
42424             allText = false;
42425             
42426             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42427                 
42428             // Recursively traverse the tree structure of the child node
42429             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42430             lastnode = currentElementChild.nodeName;
42431             i++;
42432             currentElementChild=currentElement.childNodes.item(i);
42433         }
42434         
42435         ret += innerHTML;
42436         
42437         if (!allText) {
42438                 // The remaining code is mostly for formatting the tree
42439             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42440         }
42441         
42442         
42443         if (tagName) {
42444             ret+= "</"+tagName+">";
42445         }
42446         return ret;
42447         
42448     },
42449         
42450     applyBlacklists : function()
42451     {
42452         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42453         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42454         
42455         this.white = [];
42456         this.black = [];
42457         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42458             if (b.indexOf(tag) > -1) {
42459                 return;
42460             }
42461             this.white.push(tag);
42462             
42463         }, this);
42464         
42465         Roo.each(w, function(tag) {
42466             if (b.indexOf(tag) > -1) {
42467                 return;
42468             }
42469             if (this.white.indexOf(tag) > -1) {
42470                 return;
42471             }
42472             this.white.push(tag);
42473             
42474         }, this);
42475         
42476         
42477         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42478             if (w.indexOf(tag) > -1) {
42479                 return;
42480             }
42481             this.black.push(tag);
42482             
42483         }, this);
42484         
42485         Roo.each(b, function(tag) {
42486             if (w.indexOf(tag) > -1) {
42487                 return;
42488             }
42489             if (this.black.indexOf(tag) > -1) {
42490                 return;
42491             }
42492             this.black.push(tag);
42493             
42494         }, this);
42495         
42496         
42497         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42498         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42499         
42500         this.cwhite = [];
42501         this.cblack = [];
42502         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42503             if (b.indexOf(tag) > -1) {
42504                 return;
42505             }
42506             this.cwhite.push(tag);
42507             
42508         }, this);
42509         
42510         Roo.each(w, function(tag) {
42511             if (b.indexOf(tag) > -1) {
42512                 return;
42513             }
42514             if (this.cwhite.indexOf(tag) > -1) {
42515                 return;
42516             }
42517             this.cwhite.push(tag);
42518             
42519         }, this);
42520         
42521         
42522         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42523             if (w.indexOf(tag) > -1) {
42524                 return;
42525             }
42526             this.cblack.push(tag);
42527             
42528         }, this);
42529         
42530         Roo.each(b, function(tag) {
42531             if (w.indexOf(tag) > -1) {
42532                 return;
42533             }
42534             if (this.cblack.indexOf(tag) > -1) {
42535                 return;
42536             }
42537             this.cblack.push(tag);
42538             
42539         }, this);
42540     },
42541     
42542     setStylesheets : function(stylesheets)
42543     {
42544         if(typeof(stylesheets) == 'string'){
42545             Roo.get(this.iframe.contentDocument.head).createChild({
42546                 tag : 'link',
42547                 rel : 'stylesheet',
42548                 type : 'text/css',
42549                 href : stylesheets
42550             });
42551             
42552             return;
42553         }
42554         var _this = this;
42555      
42556         Roo.each(stylesheets, function(s) {
42557             if(!s.length){
42558                 return;
42559             }
42560             
42561             Roo.get(_this.iframe.contentDocument.head).createChild({
42562                 tag : 'link',
42563                 rel : 'stylesheet',
42564                 type : 'text/css',
42565                 href : s
42566             });
42567         });
42568
42569         
42570     },
42571     
42572     removeStylesheets : function()
42573     {
42574         var _this = this;
42575         
42576         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42577             s.remove();
42578         });
42579     }
42580     
42581     // hide stuff that is not compatible
42582     /**
42583      * @event blur
42584      * @hide
42585      */
42586     /**
42587      * @event change
42588      * @hide
42589      */
42590     /**
42591      * @event focus
42592      * @hide
42593      */
42594     /**
42595      * @event specialkey
42596      * @hide
42597      */
42598     /**
42599      * @cfg {String} fieldClass @hide
42600      */
42601     /**
42602      * @cfg {String} focusClass @hide
42603      */
42604     /**
42605      * @cfg {String} autoCreate @hide
42606      */
42607     /**
42608      * @cfg {String} inputType @hide
42609      */
42610     /**
42611      * @cfg {String} invalidClass @hide
42612      */
42613     /**
42614      * @cfg {String} invalidText @hide
42615      */
42616     /**
42617      * @cfg {String} msgFx @hide
42618      */
42619     /**
42620      * @cfg {String} validateOnBlur @hide
42621      */
42622 });
42623
42624 Roo.HtmlEditorCore.white = [
42625         'area', 'br', 'img', 'input', 'hr', 'wbr',
42626         
42627        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42628        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42629        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42630        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42631        'table',   'ul',         'xmp', 
42632        
42633        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42634       'thead',   'tr', 
42635      
42636       'dir', 'menu', 'ol', 'ul', 'dl',
42637        
42638       'embed',  'object'
42639 ];
42640
42641
42642 Roo.HtmlEditorCore.black = [
42643     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42644         'applet', // 
42645         'base',   'basefont', 'bgsound', 'blink',  'body', 
42646         'frame',  'frameset', 'head',    'html',   'ilayer', 
42647         'iframe', 'layer',  'link',     'meta',    'object',   
42648         'script', 'style' ,'title',  'xml' // clean later..
42649 ];
42650 Roo.HtmlEditorCore.clean = [
42651     'script', 'style', 'title', 'xml'
42652 ];
42653 Roo.HtmlEditorCore.remove = [
42654     'font'
42655 ];
42656 // attributes..
42657
42658 Roo.HtmlEditorCore.ablack = [
42659     'on'
42660 ];
42661     
42662 Roo.HtmlEditorCore.aclean = [ 
42663     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42664 ];
42665
42666 // protocols..
42667 Roo.HtmlEditorCore.pwhite= [
42668         'http',  'https',  'mailto'
42669 ];
42670
42671 // white listed style attributes.
42672 Roo.HtmlEditorCore.cwhite= [
42673       //  'text-align', /// default is to allow most things..
42674       
42675          
42676 //        'font-size'//??
42677 ];
42678
42679 // black listed style attributes.
42680 Roo.HtmlEditorCore.cblack= [
42681       //  'font-size' -- this can be set by the project 
42682 ];
42683
42684
42685 Roo.HtmlEditorCore.swapCodes   =[ 
42686     [    8211, "--" ], 
42687     [    8212, "--" ], 
42688     [    8216,  "'" ],  
42689     [    8217, "'" ],  
42690     [    8220, '"' ],  
42691     [    8221, '"' ],  
42692     [    8226, "*" ],  
42693     [    8230, "..." ]
42694 ]; 
42695
42696     //<script type="text/javascript">
42697
42698 /*
42699  * Ext JS Library 1.1.1
42700  * Copyright(c) 2006-2007, Ext JS, LLC.
42701  * Licence LGPL
42702  * 
42703  */
42704  
42705  
42706 Roo.form.HtmlEditor = function(config){
42707     
42708     
42709     
42710     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42711     
42712     if (!this.toolbars) {
42713         this.toolbars = [];
42714     }
42715     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42716     
42717     
42718 };
42719
42720 /**
42721  * @class Roo.form.HtmlEditor
42722  * @extends Roo.form.Field
42723  * Provides a lightweight HTML Editor component.
42724  *
42725  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42726  * 
42727  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42728  * supported by this editor.</b><br/><br/>
42729  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42730  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42731  */
42732 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42733     /**
42734      * @cfg {Boolean} clearUp
42735      */
42736     clearUp : true,
42737       /**
42738      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42739      */
42740     toolbars : false,
42741    
42742      /**
42743      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42744      *                        Roo.resizable.
42745      */
42746     resizable : false,
42747      /**
42748      * @cfg {Number} height (in pixels)
42749      */   
42750     height: 300,
42751    /**
42752      * @cfg {Number} width (in pixels)
42753      */   
42754     width: 500,
42755     
42756     /**
42757      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42758      * 
42759      */
42760     stylesheets: false,
42761     
42762     
42763      /**
42764      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42765      * 
42766      */
42767     cblack: false,
42768     /**
42769      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42770      * 
42771      */
42772     cwhite: false,
42773     
42774      /**
42775      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42776      * 
42777      */
42778     black: false,
42779     /**
42780      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42781      * 
42782      */
42783     white: false,
42784     
42785     // id of frame..
42786     frameId: false,
42787     
42788     // private properties
42789     validationEvent : false,
42790     deferHeight: true,
42791     initialized : false,
42792     activated : false,
42793     
42794     onFocus : Roo.emptyFn,
42795     iframePad:3,
42796     hideMode:'offsets',
42797     
42798     actionMode : 'container', // defaults to hiding it...
42799     
42800     defaultAutoCreate : { // modified by initCompnoent..
42801         tag: "textarea",
42802         style:"width:500px;height:300px;",
42803         autocomplete: "new-password"
42804     },
42805
42806     // private
42807     initComponent : function(){
42808         this.addEvents({
42809             /**
42810              * @event initialize
42811              * Fires when the editor is fully initialized (including the iframe)
42812              * @param {HtmlEditor} this
42813              */
42814             initialize: true,
42815             /**
42816              * @event activate
42817              * Fires when the editor is first receives the focus. Any insertion must wait
42818              * until after this event.
42819              * @param {HtmlEditor} this
42820              */
42821             activate: true,
42822              /**
42823              * @event beforesync
42824              * Fires before the textarea is updated with content from the editor iframe. Return false
42825              * to cancel the sync.
42826              * @param {HtmlEditor} this
42827              * @param {String} html
42828              */
42829             beforesync: true,
42830              /**
42831              * @event beforepush
42832              * Fires before the iframe editor is updated with content from the textarea. Return false
42833              * to cancel the push.
42834              * @param {HtmlEditor} this
42835              * @param {String} html
42836              */
42837             beforepush: true,
42838              /**
42839              * @event sync
42840              * Fires when the textarea is updated with content from the editor iframe.
42841              * @param {HtmlEditor} this
42842              * @param {String} html
42843              */
42844             sync: true,
42845              /**
42846              * @event push
42847              * Fires when the iframe editor is updated with content from the textarea.
42848              * @param {HtmlEditor} this
42849              * @param {String} html
42850              */
42851             push: true,
42852              /**
42853              * @event editmodechange
42854              * Fires when the editor switches edit modes
42855              * @param {HtmlEditor} this
42856              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42857              */
42858             editmodechange: true,
42859             /**
42860              * @event editorevent
42861              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42862              * @param {HtmlEditor} this
42863              */
42864             editorevent: true,
42865             /**
42866              * @event firstfocus
42867              * Fires when on first focus - needed by toolbars..
42868              * @param {HtmlEditor} this
42869              */
42870             firstfocus: true,
42871             /**
42872              * @event autosave
42873              * Auto save the htmlEditor value as a file into Events
42874              * @param {HtmlEditor} this
42875              */
42876             autosave: true,
42877             /**
42878              * @event savedpreview
42879              * preview the saved version of htmlEditor
42880              * @param {HtmlEditor} this
42881              */
42882             savedpreview: true,
42883             
42884             /**
42885             * @event stylesheetsclick
42886             * Fires when press the Sytlesheets button
42887             * @param {Roo.HtmlEditorCore} this
42888             */
42889             stylesheetsclick: true
42890         });
42891         this.defaultAutoCreate =  {
42892             tag: "textarea",
42893             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42894             autocomplete: "new-password"
42895         };
42896     },
42897
42898     /**
42899      * Protected method that will not generally be called directly. It
42900      * is called when the editor creates its toolbar. Override this method if you need to
42901      * add custom toolbar buttons.
42902      * @param {HtmlEditor} editor
42903      */
42904     createToolbar : function(editor){
42905         Roo.log("create toolbars");
42906         if (!editor.toolbars || !editor.toolbars.length) {
42907             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42908         }
42909         
42910         for (var i =0 ; i < editor.toolbars.length;i++) {
42911             editor.toolbars[i] = Roo.factory(
42912                     typeof(editor.toolbars[i]) == 'string' ?
42913                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42914                 Roo.form.HtmlEditor);
42915             editor.toolbars[i].init(editor);
42916         }
42917          
42918         
42919     },
42920
42921      
42922     // private
42923     onRender : function(ct, position)
42924     {
42925         var _t = this;
42926         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42927         
42928         this.wrap = this.el.wrap({
42929             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42930         });
42931         
42932         this.editorcore.onRender(ct, position);
42933          
42934         if (this.resizable) {
42935             this.resizeEl = new Roo.Resizable(this.wrap, {
42936                 pinned : true,
42937                 wrap: true,
42938                 dynamic : true,
42939                 minHeight : this.height,
42940                 height: this.height,
42941                 handles : this.resizable,
42942                 width: this.width,
42943                 listeners : {
42944                     resize : function(r, w, h) {
42945                         _t.onResize(w,h); // -something
42946                     }
42947                 }
42948             });
42949             
42950         }
42951         this.createToolbar(this);
42952        
42953         
42954         if(!this.width){
42955             this.setSize(this.wrap.getSize());
42956         }
42957         if (this.resizeEl) {
42958             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42959             // should trigger onReize..
42960         }
42961         
42962         this.keyNav = new Roo.KeyNav(this.el, {
42963             
42964             "tab" : function(e){
42965                 e.preventDefault();
42966                 
42967                 var value = this.getValue();
42968                 
42969                 var start = this.el.dom.selectionStart;
42970                 var end = this.el.dom.selectionEnd;
42971                 
42972                 if(!e.shiftKey){
42973                     
42974                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
42975                     this.el.dom.setSelectionRange(end + 1, end + 1);
42976                     return;
42977                 }
42978                 
42979                 var f = value.substring(0, start).split("\t");
42980                 
42981                 if(f.pop().length != 0){
42982                     return;
42983                 }
42984                 
42985                 this.setValue(f.join("\t") + value.substring(end));
42986                 this.el.dom.setSelectionRange(start - 1, start - 1);
42987                 
42988             },
42989             
42990             "home" : function(e){
42991                 e.preventDefault();
42992                 
42993                 var curr = this.el.dom.selectionStart;
42994                 var lines = this.getValue().split("\n");
42995                 
42996                 if(!lines.length){
42997                     return;
42998                 }
42999                 
43000                 if(e.ctrlKey){
43001                     this.el.dom.setSelectionRange(0, 0);
43002                     return;
43003                 }
43004                 
43005                 var pos = 0;
43006                 
43007                 for (var i = 0; i < lines.length;i++) {
43008                     pos += lines[i].length;
43009                     
43010                     if(i != 0){
43011                         pos += 1;
43012                     }
43013                     
43014                     if(pos < curr){
43015                         continue;
43016                     }
43017                     
43018                     pos -= lines[i].length;
43019                     
43020                     break;
43021                 }
43022                 
43023                 if(!e.shiftKey){
43024                     this.el.dom.setSelectionRange(pos, pos);
43025                     return;
43026                 }
43027                 
43028                 this.el.dom.selectionStart = pos;
43029                 this.el.dom.selectionEnd = curr;
43030             },
43031             
43032             "end" : function(e){
43033                 e.preventDefault();
43034                 
43035                 var curr = this.el.dom.selectionStart;
43036                 var lines = this.getValue().split("\n");
43037                 
43038                 if(!lines.length){
43039                     return;
43040                 }
43041                 
43042                 if(e.ctrlKey){
43043                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43044                     return;
43045                 }
43046                 
43047                 var pos = 0;
43048                 
43049                 for (var i = 0; i < lines.length;i++) {
43050                     
43051                     pos += lines[i].length;
43052                     
43053                     if(i != 0){
43054                         pos += 1;
43055                     }
43056                     
43057                     if(pos < curr){
43058                         continue;
43059                     }
43060                     
43061                     break;
43062                 }
43063                 
43064                 if(!e.shiftKey){
43065                     this.el.dom.setSelectionRange(pos, pos);
43066                     return;
43067                 }
43068                 
43069                 this.el.dom.selectionStart = curr;
43070                 this.el.dom.selectionEnd = pos;
43071             },
43072
43073             scope : this,
43074
43075             doRelay : function(foo, bar, hname){
43076                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43077             },
43078
43079             forceKeyDown: true
43080         });
43081         
43082 //        if(this.autosave && this.w){
43083 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43084 //        }
43085     },
43086
43087     // private
43088     onResize : function(w, h)
43089     {
43090         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43091         var ew = false;
43092         var eh = false;
43093         
43094         if(this.el ){
43095             if(typeof w == 'number'){
43096                 var aw = w - this.wrap.getFrameWidth('lr');
43097                 this.el.setWidth(this.adjustWidth('textarea', aw));
43098                 ew = aw;
43099             }
43100             if(typeof h == 'number'){
43101                 var tbh = 0;
43102                 for (var i =0; i < this.toolbars.length;i++) {
43103                     // fixme - ask toolbars for heights?
43104                     tbh += this.toolbars[i].tb.el.getHeight();
43105                     if (this.toolbars[i].footer) {
43106                         tbh += this.toolbars[i].footer.el.getHeight();
43107                     }
43108                 }
43109                 
43110                 
43111                 
43112                 
43113                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43114                 ah -= 5; // knock a few pixes off for look..
43115 //                Roo.log(ah);
43116                 this.el.setHeight(this.adjustWidth('textarea', ah));
43117                 var eh = ah;
43118             }
43119         }
43120         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43121         this.editorcore.onResize(ew,eh);
43122         
43123     },
43124
43125     /**
43126      * Toggles the editor between standard and source edit mode.
43127      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43128      */
43129     toggleSourceEdit : function(sourceEditMode)
43130     {
43131         this.editorcore.toggleSourceEdit(sourceEditMode);
43132         
43133         if(this.editorcore.sourceEditMode){
43134             Roo.log('editor - showing textarea');
43135             
43136 //            Roo.log('in');
43137 //            Roo.log(this.syncValue());
43138             this.editorcore.syncValue();
43139             this.el.removeClass('x-hidden');
43140             this.el.dom.removeAttribute('tabIndex');
43141             this.el.focus();
43142             
43143             for (var i = 0; i < this.toolbars.length; i++) {
43144                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43145                     this.toolbars[i].tb.hide();
43146                     this.toolbars[i].footer.hide();
43147                 }
43148             }
43149             
43150         }else{
43151             Roo.log('editor - hiding textarea');
43152 //            Roo.log('out')
43153 //            Roo.log(this.pushValue()); 
43154             this.editorcore.pushValue();
43155             
43156             this.el.addClass('x-hidden');
43157             this.el.dom.setAttribute('tabIndex', -1);
43158             
43159             for (var i = 0; i < this.toolbars.length; i++) {
43160                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43161                     this.toolbars[i].tb.show();
43162                     this.toolbars[i].footer.show();
43163                 }
43164             }
43165             
43166             //this.deferFocus();
43167         }
43168         
43169         this.setSize(this.wrap.getSize());
43170         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43171         
43172         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43173     },
43174  
43175     // private (for BoxComponent)
43176     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43177
43178     // private (for BoxComponent)
43179     getResizeEl : function(){
43180         return this.wrap;
43181     },
43182
43183     // private (for BoxComponent)
43184     getPositionEl : function(){
43185         return this.wrap;
43186     },
43187
43188     // private
43189     initEvents : function(){
43190         this.originalValue = this.getValue();
43191     },
43192
43193     /**
43194      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43195      * @method
43196      */
43197     markInvalid : Roo.emptyFn,
43198     /**
43199      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43200      * @method
43201      */
43202     clearInvalid : Roo.emptyFn,
43203
43204     setValue : function(v){
43205         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43206         this.editorcore.pushValue();
43207     },
43208
43209      
43210     // private
43211     deferFocus : function(){
43212         this.focus.defer(10, this);
43213     },
43214
43215     // doc'ed in Field
43216     focus : function(){
43217         this.editorcore.focus();
43218         
43219     },
43220       
43221
43222     // private
43223     onDestroy : function(){
43224         
43225         
43226         
43227         if(this.rendered){
43228             
43229             for (var i =0; i < this.toolbars.length;i++) {
43230                 // fixme - ask toolbars for heights?
43231                 this.toolbars[i].onDestroy();
43232             }
43233             
43234             this.wrap.dom.innerHTML = '';
43235             this.wrap.remove();
43236         }
43237     },
43238
43239     // private
43240     onFirstFocus : function(){
43241         //Roo.log("onFirstFocus");
43242         this.editorcore.onFirstFocus();
43243          for (var i =0; i < this.toolbars.length;i++) {
43244             this.toolbars[i].onFirstFocus();
43245         }
43246         
43247     },
43248     
43249     // private
43250     syncValue : function()
43251     {
43252         this.editorcore.syncValue();
43253     },
43254     
43255     pushValue : function()
43256     {
43257         this.editorcore.pushValue();
43258     },
43259     
43260     setStylesheets : function(stylesheets)
43261     {
43262         this.editorcore.setStylesheets(stylesheets);
43263     },
43264     
43265     removeStylesheets : function()
43266     {
43267         this.editorcore.removeStylesheets();
43268     }
43269      
43270     
43271     // hide stuff that is not compatible
43272     /**
43273      * @event blur
43274      * @hide
43275      */
43276     /**
43277      * @event change
43278      * @hide
43279      */
43280     /**
43281      * @event focus
43282      * @hide
43283      */
43284     /**
43285      * @event specialkey
43286      * @hide
43287      */
43288     /**
43289      * @cfg {String} fieldClass @hide
43290      */
43291     /**
43292      * @cfg {String} focusClass @hide
43293      */
43294     /**
43295      * @cfg {String} autoCreate @hide
43296      */
43297     /**
43298      * @cfg {String} inputType @hide
43299      */
43300     /**
43301      * @cfg {String} invalidClass @hide
43302      */
43303     /**
43304      * @cfg {String} invalidText @hide
43305      */
43306     /**
43307      * @cfg {String} msgFx @hide
43308      */
43309     /**
43310      * @cfg {String} validateOnBlur @hide
43311      */
43312 });
43313  
43314     // <script type="text/javascript">
43315 /*
43316  * Based on
43317  * Ext JS Library 1.1.1
43318  * Copyright(c) 2006-2007, Ext JS, LLC.
43319  *  
43320  
43321  */
43322
43323 /**
43324  * @class Roo.form.HtmlEditorToolbar1
43325  * Basic Toolbar
43326  * 
43327  * Usage:
43328  *
43329  new Roo.form.HtmlEditor({
43330     ....
43331     toolbars : [
43332         new Roo.form.HtmlEditorToolbar1({
43333             disable : { fonts: 1 , format: 1, ..., ... , ...],
43334             btns : [ .... ]
43335         })
43336     }
43337      
43338  * 
43339  * @cfg {Object} disable List of elements to disable..
43340  * @cfg {Array} btns List of additional buttons.
43341  * 
43342  * 
43343  * NEEDS Extra CSS? 
43344  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43345  */
43346  
43347 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43348 {
43349     
43350     Roo.apply(this, config);
43351     
43352     // default disabled, based on 'good practice'..
43353     this.disable = this.disable || {};
43354     Roo.applyIf(this.disable, {
43355         fontSize : true,
43356         colors : true,
43357         specialElements : true
43358     });
43359     
43360     
43361     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43362     // dont call parent... till later.
43363 }
43364
43365 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43366     
43367     tb: false,
43368     
43369     rendered: false,
43370     
43371     editor : false,
43372     editorcore : false,
43373     /**
43374      * @cfg {Object} disable  List of toolbar elements to disable
43375          
43376      */
43377     disable : false,
43378     
43379     
43380      /**
43381      * @cfg {String} createLinkText The default text for the create link prompt
43382      */
43383     createLinkText : 'Please enter the URL for the link:',
43384     /**
43385      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43386      */
43387     defaultLinkValue : 'http:/'+'/',
43388    
43389     
43390       /**
43391      * @cfg {Array} fontFamilies An array of available font families
43392      */
43393     fontFamilies : [
43394         'Arial',
43395         'Courier New',
43396         'Tahoma',
43397         'Times New Roman',
43398         'Verdana'
43399     ],
43400     
43401     specialChars : [
43402            "&#169;",
43403           "&#174;",     
43404           "&#8482;",    
43405           "&#163;" ,    
43406          // "&#8212;",    
43407           "&#8230;",    
43408           "&#247;" ,    
43409         //  "&#225;" ,     ?? a acute?
43410            "&#8364;"    , //Euro
43411        //   "&#8220;"    ,
43412         //  "&#8221;"    ,
43413         //  "&#8226;"    ,
43414           "&#176;"  //   , // degrees
43415
43416          // "&#233;"     , // e ecute
43417          // "&#250;"     , // u ecute?
43418     ],
43419     
43420     specialElements : [
43421         {
43422             text: "Insert Table",
43423             xtype: 'MenuItem',
43424             xns : Roo.Menu,
43425             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43426                 
43427         },
43428         {    
43429             text: "Insert Image",
43430             xtype: 'MenuItem',
43431             xns : Roo.Menu,
43432             ihtml : '<img src="about:blank"/>'
43433             
43434         }
43435         
43436          
43437     ],
43438     
43439     
43440     inputElements : [ 
43441             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43442             "input:submit", "input:button", "select", "textarea", "label" ],
43443     formats : [
43444         ["p"] ,  
43445         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43446         ["pre"],[ "code"], 
43447         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43448         ['div'],['span']
43449     ],
43450     
43451     cleanStyles : [
43452         "font-size"
43453     ],
43454      /**
43455      * @cfg {String} defaultFont default font to use.
43456      */
43457     defaultFont: 'tahoma',
43458    
43459     fontSelect : false,
43460     
43461     
43462     formatCombo : false,
43463     
43464     init : function(editor)
43465     {
43466         this.editor = editor;
43467         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43468         var editorcore = this.editorcore;
43469         
43470         var _t = this;
43471         
43472         var fid = editorcore.frameId;
43473         var etb = this;
43474         function btn(id, toggle, handler){
43475             var xid = fid + '-'+ id ;
43476             return {
43477                 id : xid,
43478                 cmd : id,
43479                 cls : 'x-btn-icon x-edit-'+id,
43480                 enableToggle:toggle !== false,
43481                 scope: _t, // was editor...
43482                 handler:handler||_t.relayBtnCmd,
43483                 clickEvent:'mousedown',
43484                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43485                 tabIndex:-1
43486             };
43487         }
43488         
43489         
43490         
43491         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43492         this.tb = tb;
43493          // stop form submits
43494         tb.el.on('click', function(e){
43495             e.preventDefault(); // what does this do?
43496         });
43497
43498         if(!this.disable.font) { // && !Roo.isSafari){
43499             /* why no safari for fonts 
43500             editor.fontSelect = tb.el.createChild({
43501                 tag:'select',
43502                 tabIndex: -1,
43503                 cls:'x-font-select',
43504                 html: this.createFontOptions()
43505             });
43506             
43507             editor.fontSelect.on('change', function(){
43508                 var font = editor.fontSelect.dom.value;
43509                 editor.relayCmd('fontname', font);
43510                 editor.deferFocus();
43511             }, editor);
43512             
43513             tb.add(
43514                 editor.fontSelect.dom,
43515                 '-'
43516             );
43517             */
43518             
43519         };
43520         if(!this.disable.formats){
43521             this.formatCombo = new Roo.form.ComboBox({
43522                 store: new Roo.data.SimpleStore({
43523                     id : 'tag',
43524                     fields: ['tag'],
43525                     data : this.formats // from states.js
43526                 }),
43527                 blockFocus : true,
43528                 name : '',
43529                 //autoCreate : {tag: "div",  size: "20"},
43530                 displayField:'tag',
43531                 typeAhead: false,
43532                 mode: 'local',
43533                 editable : false,
43534                 triggerAction: 'all',
43535                 emptyText:'Add tag',
43536                 selectOnFocus:true,
43537                 width:135,
43538                 listeners : {
43539                     'select': function(c, r, i) {
43540                         editorcore.insertTag(r.get('tag'));
43541                         editor.focus();
43542                     }
43543                 }
43544
43545             });
43546             tb.addField(this.formatCombo);
43547             
43548         }
43549         
43550         if(!this.disable.format){
43551             tb.add(
43552                 btn('bold'),
43553                 btn('italic'),
43554                 btn('underline')
43555             );
43556         };
43557         if(!this.disable.fontSize){
43558             tb.add(
43559                 '-',
43560                 
43561                 
43562                 btn('increasefontsize', false, editorcore.adjustFont),
43563                 btn('decreasefontsize', false, editorcore.adjustFont)
43564             );
43565         };
43566         
43567         
43568         if(!this.disable.colors){
43569             tb.add(
43570                 '-', {
43571                     id:editorcore.frameId +'-forecolor',
43572                     cls:'x-btn-icon x-edit-forecolor',
43573                     clickEvent:'mousedown',
43574                     tooltip: this.buttonTips['forecolor'] || undefined,
43575                     tabIndex:-1,
43576                     menu : new Roo.menu.ColorMenu({
43577                         allowReselect: true,
43578                         focus: Roo.emptyFn,
43579                         value:'000000',
43580                         plain:true,
43581                         selectHandler: function(cp, color){
43582                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43583                             editor.deferFocus();
43584                         },
43585                         scope: editorcore,
43586                         clickEvent:'mousedown'
43587                     })
43588                 }, {
43589                     id:editorcore.frameId +'backcolor',
43590                     cls:'x-btn-icon x-edit-backcolor',
43591                     clickEvent:'mousedown',
43592                     tooltip: this.buttonTips['backcolor'] || undefined,
43593                     tabIndex:-1,
43594                     menu : new Roo.menu.ColorMenu({
43595                         focus: Roo.emptyFn,
43596                         value:'FFFFFF',
43597                         plain:true,
43598                         allowReselect: true,
43599                         selectHandler: function(cp, color){
43600                             if(Roo.isGecko){
43601                                 editorcore.execCmd('useCSS', false);
43602                                 editorcore.execCmd('hilitecolor', color);
43603                                 editorcore.execCmd('useCSS', true);
43604                                 editor.deferFocus();
43605                             }else{
43606                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43607                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43608                                 editor.deferFocus();
43609                             }
43610                         },
43611                         scope:editorcore,
43612                         clickEvent:'mousedown'
43613                     })
43614                 }
43615             );
43616         };
43617         // now add all the items...
43618         
43619
43620         if(!this.disable.alignments){
43621             tb.add(
43622                 '-',
43623                 btn('justifyleft'),
43624                 btn('justifycenter'),
43625                 btn('justifyright')
43626             );
43627         };
43628
43629         //if(!Roo.isSafari){
43630             if(!this.disable.links){
43631                 tb.add(
43632                     '-',
43633                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43634                 );
43635             };
43636
43637             if(!this.disable.lists){
43638                 tb.add(
43639                     '-',
43640                     btn('insertorderedlist'),
43641                     btn('insertunorderedlist')
43642                 );
43643             }
43644             if(!this.disable.sourceEdit){
43645                 tb.add(
43646                     '-',
43647                     btn('sourceedit', true, function(btn){
43648                         this.toggleSourceEdit(btn.pressed);
43649                     })
43650                 );
43651             }
43652         //}
43653         
43654         var smenu = { };
43655         // special menu.. - needs to be tidied up..
43656         if (!this.disable.special) {
43657             smenu = {
43658                 text: "&#169;",
43659                 cls: 'x-edit-none',
43660                 
43661                 menu : {
43662                     items : []
43663                 }
43664             };
43665             for (var i =0; i < this.specialChars.length; i++) {
43666                 smenu.menu.items.push({
43667                     
43668                     html: this.specialChars[i],
43669                     handler: function(a,b) {
43670                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43671                         //editor.insertAtCursor(a.html);
43672                         
43673                     },
43674                     tabIndex:-1
43675                 });
43676             }
43677             
43678             
43679             tb.add(smenu);
43680             
43681             
43682         }
43683         
43684         var cmenu = { };
43685         if (!this.disable.cleanStyles) {
43686             cmenu = {
43687                 cls: 'x-btn-icon x-btn-clear',
43688                 
43689                 menu : {
43690                     items : []
43691                 }
43692             };
43693             for (var i =0; i < this.cleanStyles.length; i++) {
43694                 cmenu.menu.items.push({
43695                     actiontype : this.cleanStyles[i],
43696                     html: 'Remove ' + this.cleanStyles[i],
43697                     handler: function(a,b) {
43698 //                        Roo.log(a);
43699 //                        Roo.log(b);
43700                         var c = Roo.get(editorcore.doc.body);
43701                         c.select('[style]').each(function(s) {
43702                             s.dom.style.removeProperty(a.actiontype);
43703                         });
43704                         editorcore.syncValue();
43705                     },
43706                     tabIndex:-1
43707                 });
43708             }
43709             cmenu.menu.items.push({
43710                 actiontype : 'word',
43711                 html: 'Remove MS Word Formating',
43712                 handler: function(a,b) {
43713                     editorcore.cleanWord();
43714                     editorcore.syncValue();
43715                 },
43716                 tabIndex:-1
43717             });
43718             
43719             cmenu.menu.items.push({
43720                 actiontype : 'all',
43721                 html: 'Remove All Styles',
43722                 handler: function(a,b) {
43723                     
43724                     var c = Roo.get(editorcore.doc.body);
43725                     c.select('[style]').each(function(s) {
43726                         s.dom.removeAttribute('style');
43727                     });
43728                     editorcore.syncValue();
43729                 },
43730                 tabIndex:-1
43731             });
43732              cmenu.menu.items.push({
43733                 actiontype : 'word',
43734                 html: 'Tidy HTML Source',
43735                 handler: function(a,b) {
43736                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43737                     editorcore.syncValue();
43738                 },
43739                 tabIndex:-1
43740             });
43741             
43742             
43743             tb.add(cmenu);
43744         }
43745          
43746         if (!this.disable.specialElements) {
43747             var semenu = {
43748                 text: "Other;",
43749                 cls: 'x-edit-none',
43750                 menu : {
43751                     items : []
43752                 }
43753             };
43754             for (var i =0; i < this.specialElements.length; i++) {
43755                 semenu.menu.items.push(
43756                     Roo.apply({ 
43757                         handler: function(a,b) {
43758                             editor.insertAtCursor(this.ihtml);
43759                         }
43760                     }, this.specialElements[i])
43761                 );
43762                     
43763             }
43764             
43765             tb.add(semenu);
43766             
43767             
43768         }
43769          
43770         
43771         if (this.btns) {
43772             for(var i =0; i< this.btns.length;i++) {
43773                 var b = Roo.factory(this.btns[i],Roo.form);
43774                 b.cls =  'x-edit-none';
43775                 
43776                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43777                     b.cls += ' x-init-enable';
43778                 }
43779                 
43780                 b.scope = editorcore;
43781                 tb.add(b);
43782             }
43783         
43784         }
43785         
43786         
43787         
43788         // disable everything...
43789         
43790         this.tb.items.each(function(item){
43791             
43792            if(
43793                 item.id != editorcore.frameId+ '-sourceedit' && 
43794                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43795             ){
43796                 
43797                 item.disable();
43798             }
43799         });
43800         this.rendered = true;
43801         
43802         // the all the btns;
43803         editor.on('editorevent', this.updateToolbar, this);
43804         // other toolbars need to implement this..
43805         //editor.on('editmodechange', this.updateToolbar, this);
43806     },
43807     
43808     
43809     relayBtnCmd : function(btn) {
43810         this.editorcore.relayCmd(btn.cmd);
43811     },
43812     // private used internally
43813     createLink : function(){
43814         Roo.log("create link?");
43815         var url = prompt(this.createLinkText, this.defaultLinkValue);
43816         if(url && url != 'http:/'+'/'){
43817             this.editorcore.relayCmd('createlink', url);
43818         }
43819     },
43820
43821     
43822     /**
43823      * Protected method that will not generally be called directly. It triggers
43824      * a toolbar update by reading the markup state of the current selection in the editor.
43825      */
43826     updateToolbar: function(){
43827
43828         if(!this.editorcore.activated){
43829             this.editor.onFirstFocus();
43830             return;
43831         }
43832
43833         var btns = this.tb.items.map, 
43834             doc = this.editorcore.doc,
43835             frameId = this.editorcore.frameId;
43836
43837         if(!this.disable.font && !Roo.isSafari){
43838             /*
43839             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43840             if(name != this.fontSelect.dom.value){
43841                 this.fontSelect.dom.value = name;
43842             }
43843             */
43844         }
43845         if(!this.disable.format){
43846             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43847             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43848             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43849         }
43850         if(!this.disable.alignments){
43851             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43852             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43853             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43854         }
43855         if(!Roo.isSafari && !this.disable.lists){
43856             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43857             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43858         }
43859         
43860         var ans = this.editorcore.getAllAncestors();
43861         if (this.formatCombo) {
43862             
43863             
43864             var store = this.formatCombo.store;
43865             this.formatCombo.setValue("");
43866             for (var i =0; i < ans.length;i++) {
43867                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43868                     // select it..
43869                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43870                     break;
43871                 }
43872             }
43873         }
43874         
43875         
43876         
43877         // hides menus... - so this cant be on a menu...
43878         Roo.menu.MenuMgr.hideAll();
43879
43880         //this.editorsyncValue();
43881     },
43882    
43883     
43884     createFontOptions : function(){
43885         var buf = [], fs = this.fontFamilies, ff, lc;
43886         
43887         
43888         
43889         for(var i = 0, len = fs.length; i< len; i++){
43890             ff = fs[i];
43891             lc = ff.toLowerCase();
43892             buf.push(
43893                 '<option value="',lc,'" style="font-family:',ff,';"',
43894                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43895                     ff,
43896                 '</option>'
43897             );
43898         }
43899         return buf.join('');
43900     },
43901     
43902     toggleSourceEdit : function(sourceEditMode){
43903         
43904         Roo.log("toolbar toogle");
43905         if(sourceEditMode === undefined){
43906             sourceEditMode = !this.sourceEditMode;
43907         }
43908         this.sourceEditMode = sourceEditMode === true;
43909         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43910         // just toggle the button?
43911         if(btn.pressed !== this.sourceEditMode){
43912             btn.toggle(this.sourceEditMode);
43913             return;
43914         }
43915         
43916         if(sourceEditMode){
43917             Roo.log("disabling buttons");
43918             this.tb.items.each(function(item){
43919                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43920                     item.disable();
43921                 }
43922             });
43923           
43924         }else{
43925             Roo.log("enabling buttons");
43926             if(this.editorcore.initialized){
43927                 this.tb.items.each(function(item){
43928                     item.enable();
43929                 });
43930             }
43931             
43932         }
43933         Roo.log("calling toggole on editor");
43934         // tell the editor that it's been pressed..
43935         this.editor.toggleSourceEdit(sourceEditMode);
43936        
43937     },
43938      /**
43939      * Object collection of toolbar tooltips for the buttons in the editor. The key
43940      * is the command id associated with that button and the value is a valid QuickTips object.
43941      * For example:
43942 <pre><code>
43943 {
43944     bold : {
43945         title: 'Bold (Ctrl+B)',
43946         text: 'Make the selected text bold.',
43947         cls: 'x-html-editor-tip'
43948     },
43949     italic : {
43950         title: 'Italic (Ctrl+I)',
43951         text: 'Make the selected text italic.',
43952         cls: 'x-html-editor-tip'
43953     },
43954     ...
43955 </code></pre>
43956     * @type Object
43957      */
43958     buttonTips : {
43959         bold : {
43960             title: 'Bold (Ctrl+B)',
43961             text: 'Make the selected text bold.',
43962             cls: 'x-html-editor-tip'
43963         },
43964         italic : {
43965             title: 'Italic (Ctrl+I)',
43966             text: 'Make the selected text italic.',
43967             cls: 'x-html-editor-tip'
43968         },
43969         underline : {
43970             title: 'Underline (Ctrl+U)',
43971             text: 'Underline the selected text.',
43972             cls: 'x-html-editor-tip'
43973         },
43974         increasefontsize : {
43975             title: 'Grow Text',
43976             text: 'Increase the font size.',
43977             cls: 'x-html-editor-tip'
43978         },
43979         decreasefontsize : {
43980             title: 'Shrink Text',
43981             text: 'Decrease the font size.',
43982             cls: 'x-html-editor-tip'
43983         },
43984         backcolor : {
43985             title: 'Text Highlight Color',
43986             text: 'Change the background color of the selected text.',
43987             cls: 'x-html-editor-tip'
43988         },
43989         forecolor : {
43990             title: 'Font Color',
43991             text: 'Change the color of the selected text.',
43992             cls: 'x-html-editor-tip'
43993         },
43994         justifyleft : {
43995             title: 'Align Text Left',
43996             text: 'Align text to the left.',
43997             cls: 'x-html-editor-tip'
43998         },
43999         justifycenter : {
44000             title: 'Center Text',
44001             text: 'Center text in the editor.',
44002             cls: 'x-html-editor-tip'
44003         },
44004         justifyright : {
44005             title: 'Align Text Right',
44006             text: 'Align text to the right.',
44007             cls: 'x-html-editor-tip'
44008         },
44009         insertunorderedlist : {
44010             title: 'Bullet List',
44011             text: 'Start a bulleted list.',
44012             cls: 'x-html-editor-tip'
44013         },
44014         insertorderedlist : {
44015             title: 'Numbered List',
44016             text: 'Start a numbered list.',
44017             cls: 'x-html-editor-tip'
44018         },
44019         createlink : {
44020             title: 'Hyperlink',
44021             text: 'Make the selected text a hyperlink.',
44022             cls: 'x-html-editor-tip'
44023         },
44024         sourceedit : {
44025             title: 'Source Edit',
44026             text: 'Switch to source editing mode.',
44027             cls: 'x-html-editor-tip'
44028         }
44029     },
44030     // private
44031     onDestroy : function(){
44032         if(this.rendered){
44033             
44034             this.tb.items.each(function(item){
44035                 if(item.menu){
44036                     item.menu.removeAll();
44037                     if(item.menu.el){
44038                         item.menu.el.destroy();
44039                     }
44040                 }
44041                 item.destroy();
44042             });
44043              
44044         }
44045     },
44046     onFirstFocus: function() {
44047         this.tb.items.each(function(item){
44048            item.enable();
44049         });
44050     }
44051 });
44052
44053
44054
44055
44056 // <script type="text/javascript">
44057 /*
44058  * Based on
44059  * Ext JS Library 1.1.1
44060  * Copyright(c) 2006-2007, Ext JS, LLC.
44061  *  
44062  
44063  */
44064
44065  
44066 /**
44067  * @class Roo.form.HtmlEditor.ToolbarContext
44068  * Context Toolbar
44069  * 
44070  * Usage:
44071  *
44072  new Roo.form.HtmlEditor({
44073     ....
44074     toolbars : [
44075         { xtype: 'ToolbarStandard', styles : {} }
44076         { xtype: 'ToolbarContext', disable : {} }
44077     ]
44078 })
44079
44080      
44081  * 
44082  * @config : {Object} disable List of elements to disable.. (not done yet.)
44083  * @config : {Object} styles  Map of styles available.
44084  * 
44085  */
44086
44087 Roo.form.HtmlEditor.ToolbarContext = function(config)
44088 {
44089     
44090     Roo.apply(this, config);
44091     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44092     // dont call parent... till later.
44093     this.styles = this.styles || {};
44094 }
44095
44096  
44097
44098 Roo.form.HtmlEditor.ToolbarContext.types = {
44099     'IMG' : {
44100         width : {
44101             title: "Width",
44102             width: 40
44103         },
44104         height:  {
44105             title: "Height",
44106             width: 40
44107         },
44108         align: {
44109             title: "Align",
44110             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44111             width : 80
44112             
44113         },
44114         border: {
44115             title: "Border",
44116             width: 40
44117         },
44118         alt: {
44119             title: "Alt",
44120             width: 120
44121         },
44122         src : {
44123             title: "Src",
44124             width: 220
44125         }
44126         
44127     },
44128     'A' : {
44129         name : {
44130             title: "Name",
44131             width: 50
44132         },
44133         target:  {
44134             title: "Target",
44135             width: 120
44136         },
44137         href:  {
44138             title: "Href",
44139             width: 220
44140         } // border?
44141         
44142     },
44143     'TABLE' : {
44144         rows : {
44145             title: "Rows",
44146             width: 20
44147         },
44148         cols : {
44149             title: "Cols",
44150             width: 20
44151         },
44152         width : {
44153             title: "Width",
44154             width: 40
44155         },
44156         height : {
44157             title: "Height",
44158             width: 40
44159         },
44160         border : {
44161             title: "Border",
44162             width: 20
44163         }
44164     },
44165     'TD' : {
44166         width : {
44167             title: "Width",
44168             width: 40
44169         },
44170         height : {
44171             title: "Height",
44172             width: 40
44173         },   
44174         align: {
44175             title: "Align",
44176             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44177             width: 80
44178         },
44179         valign: {
44180             title: "Valign",
44181             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44182             width: 80
44183         },
44184         colspan: {
44185             title: "Colspan",
44186             width: 20
44187             
44188         },
44189          'font-family'  : {
44190             title : "Font",
44191             style : 'fontFamily',
44192             displayField: 'display',
44193             optname : 'font-family',
44194             width: 140
44195         }
44196     },
44197     'INPUT' : {
44198         name : {
44199             title: "name",
44200             width: 120
44201         },
44202         value : {
44203             title: "Value",
44204             width: 120
44205         },
44206         width : {
44207             title: "Width",
44208             width: 40
44209         }
44210     },
44211     'LABEL' : {
44212         'for' : {
44213             title: "For",
44214             width: 120
44215         }
44216     },
44217     'TEXTAREA' : {
44218           name : {
44219             title: "name",
44220             width: 120
44221         },
44222         rows : {
44223             title: "Rows",
44224             width: 20
44225         },
44226         cols : {
44227             title: "Cols",
44228             width: 20
44229         }
44230     },
44231     'SELECT' : {
44232         name : {
44233             title: "name",
44234             width: 120
44235         },
44236         selectoptions : {
44237             title: "Options",
44238             width: 200
44239         }
44240     },
44241     
44242     // should we really allow this??
44243     // should this just be 
44244     'BODY' : {
44245         title : {
44246             title: "Title",
44247             width: 200,
44248             disabled : true
44249         }
44250     },
44251     'SPAN' : {
44252         'font-family'  : {
44253             title : "Font",
44254             style : 'fontFamily',
44255             displayField: 'display',
44256             optname : 'font-family',
44257             width: 140
44258         }
44259     },
44260     'DIV' : {
44261         'font-family'  : {
44262             title : "Font",
44263             style : 'fontFamily',
44264             displayField: 'display',
44265             optname : 'font-family',
44266             width: 140
44267         }
44268     },
44269      'P' : {
44270         'font-family'  : {
44271             title : "Font",
44272             style : 'fontFamily',
44273             displayField: 'display',
44274             optname : 'font-family',
44275             width: 140
44276         }
44277     },
44278     
44279     '*' : {
44280         // empty..
44281     }
44282
44283 };
44284
44285 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44286 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44287
44288 Roo.form.HtmlEditor.ToolbarContext.options = {
44289         'font-family'  : [ 
44290                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44291                 [ 'Courier New', 'Courier New'],
44292                 [ 'Tahoma', 'Tahoma'],
44293                 [ 'Times New Roman,serif', 'Times'],
44294                 [ 'Verdana','Verdana' ]
44295         ]
44296 };
44297
44298 // fixme - these need to be configurable..
44299  
44300
44301 Roo.form.HtmlEditor.ToolbarContext.types
44302
44303
44304 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44305     
44306     tb: false,
44307     
44308     rendered: false,
44309     
44310     editor : false,
44311     editorcore : false,
44312     /**
44313      * @cfg {Object} disable  List of toolbar elements to disable
44314          
44315      */
44316     disable : false,
44317     /**
44318      * @cfg {Object} styles List of styles 
44319      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44320      *
44321      * These must be defined in the page, so they get rendered correctly..
44322      * .headline { }
44323      * TD.underline { }
44324      * 
44325      */
44326     styles : false,
44327     
44328     options: false,
44329     
44330     toolbars : false,
44331     
44332     init : function(editor)
44333     {
44334         this.editor = editor;
44335         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44336         var editorcore = this.editorcore;
44337         
44338         var fid = editorcore.frameId;
44339         var etb = this;
44340         function btn(id, toggle, handler){
44341             var xid = fid + '-'+ id ;
44342             return {
44343                 id : xid,
44344                 cmd : id,
44345                 cls : 'x-btn-icon x-edit-'+id,
44346                 enableToggle:toggle !== false,
44347                 scope: editorcore, // was editor...
44348                 handler:handler||editorcore.relayBtnCmd,
44349                 clickEvent:'mousedown',
44350                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44351                 tabIndex:-1
44352             };
44353         }
44354         // create a new element.
44355         var wdiv = editor.wrap.createChild({
44356                 tag: 'div'
44357             }, editor.wrap.dom.firstChild.nextSibling, true);
44358         
44359         // can we do this more than once??
44360         
44361          // stop form submits
44362       
44363  
44364         // disable everything...
44365         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44366         this.toolbars = {};
44367            
44368         for (var i in  ty) {
44369           
44370             this.toolbars[i] = this.buildToolbar(ty[i],i);
44371         }
44372         this.tb = this.toolbars.BODY;
44373         this.tb.el.show();
44374         this.buildFooter();
44375         this.footer.show();
44376         editor.on('hide', function( ) { this.footer.hide() }, this);
44377         editor.on('show', function( ) { this.footer.show() }, this);
44378         
44379          
44380         this.rendered = true;
44381         
44382         // the all the btns;
44383         editor.on('editorevent', this.updateToolbar, this);
44384         // other toolbars need to implement this..
44385         //editor.on('editmodechange', this.updateToolbar, this);
44386     },
44387     
44388     
44389     
44390     /**
44391      * Protected method that will not generally be called directly. It triggers
44392      * a toolbar update by reading the markup state of the current selection in the editor.
44393      */
44394     updateToolbar: function(editor,ev,sel){
44395
44396         //Roo.log(ev);
44397         // capture mouse up - this is handy for selecting images..
44398         // perhaps should go somewhere else...
44399         if(!this.editorcore.activated){
44400              this.editor.onFirstFocus();
44401             return;
44402         }
44403         
44404         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44405         // selectNode - might want to handle IE?
44406         if (ev &&
44407             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44408             ev.target && ev.target.tagName == 'IMG') {
44409             // they have click on an image...
44410             // let's see if we can change the selection...
44411             sel = ev.target;
44412          
44413               var nodeRange = sel.ownerDocument.createRange();
44414             try {
44415                 nodeRange.selectNode(sel);
44416             } catch (e) {
44417                 nodeRange.selectNodeContents(sel);
44418             }
44419             //nodeRange.collapse(true);
44420             var s = this.editorcore.win.getSelection();
44421             s.removeAllRanges();
44422             s.addRange(nodeRange);
44423         }  
44424         
44425       
44426         var updateFooter = sel ? false : true;
44427         
44428         
44429         var ans = this.editorcore.getAllAncestors();
44430         
44431         // pick
44432         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44433         
44434         if (!sel) { 
44435             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44436             sel = sel ? sel : this.editorcore.doc.body;
44437             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44438             
44439         }
44440         // pick a menu that exists..
44441         var tn = sel.tagName.toUpperCase();
44442         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44443         
44444         tn = sel.tagName.toUpperCase();
44445         
44446         var lastSel = this.tb.selectedNode
44447         
44448         this.tb.selectedNode = sel;
44449         
44450         // if current menu does not match..
44451         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44452                 
44453             this.tb.el.hide();
44454             ///console.log("show: " + tn);
44455             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44456             this.tb.el.show();
44457             // update name
44458             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44459             
44460             
44461             // update attributes
44462             if (this.tb.fields) {
44463                 this.tb.fields.each(function(e) {
44464                     if (e.stylename) {
44465                         e.setValue(sel.style[e.stylename]);
44466                         return;
44467                     } 
44468                    e.setValue(sel.getAttribute(e.attrname));
44469                 });
44470             }
44471             
44472             var hasStyles = false;
44473             for(var i in this.styles) {
44474                 hasStyles = true;
44475                 break;
44476             }
44477             
44478             // update styles
44479             if (hasStyles) { 
44480                 var st = this.tb.fields.item(0);
44481                 
44482                 st.store.removeAll();
44483                
44484                 
44485                 var cn = sel.className.split(/\s+/);
44486                 
44487                 var avs = [];
44488                 if (this.styles['*']) {
44489                     
44490                     Roo.each(this.styles['*'], function(v) {
44491                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44492                     });
44493                 }
44494                 if (this.styles[tn]) { 
44495                     Roo.each(this.styles[tn], function(v) {
44496                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44497                     });
44498                 }
44499                 
44500                 st.store.loadData(avs);
44501                 st.collapse();
44502                 st.setValue(cn);
44503             }
44504             // flag our selected Node.
44505             this.tb.selectedNode = sel;
44506            
44507            
44508             Roo.menu.MenuMgr.hideAll();
44509
44510         }
44511         
44512         if (!updateFooter) {
44513             //this.footDisp.dom.innerHTML = ''; 
44514             return;
44515         }
44516         // update the footer
44517         //
44518         var html = '';
44519         
44520         this.footerEls = ans.reverse();
44521         Roo.each(this.footerEls, function(a,i) {
44522             if (!a) { return; }
44523             html += html.length ? ' &gt; '  :  '';
44524             
44525             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44526             
44527         });
44528        
44529         // 
44530         var sz = this.footDisp.up('td').getSize();
44531         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44532         this.footDisp.dom.style.marginLeft = '5px';
44533         
44534         this.footDisp.dom.style.overflow = 'hidden';
44535         
44536         this.footDisp.dom.innerHTML = html;
44537             
44538         //this.editorsyncValue();
44539     },
44540      
44541     
44542    
44543        
44544     // private
44545     onDestroy : function(){
44546         if(this.rendered){
44547             
44548             this.tb.items.each(function(item){
44549                 if(item.menu){
44550                     item.menu.removeAll();
44551                     if(item.menu.el){
44552                         item.menu.el.destroy();
44553                     }
44554                 }
44555                 item.destroy();
44556             });
44557              
44558         }
44559     },
44560     onFirstFocus: function() {
44561         // need to do this for all the toolbars..
44562         this.tb.items.each(function(item){
44563            item.enable();
44564         });
44565     },
44566     buildToolbar: function(tlist, nm)
44567     {
44568         var editor = this.editor;
44569         var editorcore = this.editorcore;
44570          // create a new element.
44571         var wdiv = editor.wrap.createChild({
44572                 tag: 'div'
44573             }, editor.wrap.dom.firstChild.nextSibling, true);
44574         
44575        
44576         var tb = new Roo.Toolbar(wdiv);
44577         // add the name..
44578         
44579         tb.add(nm+ ":&nbsp;");
44580         
44581         var styles = [];
44582         for(var i in this.styles) {
44583             styles.push(i);
44584         }
44585         
44586         // styles...
44587         if (styles && styles.length) {
44588             
44589             // this needs a multi-select checkbox...
44590             tb.addField( new Roo.form.ComboBox({
44591                 store: new Roo.data.SimpleStore({
44592                     id : 'val',
44593                     fields: ['val', 'selected'],
44594                     data : [] 
44595                 }),
44596                 name : '-roo-edit-className',
44597                 attrname : 'className',
44598                 displayField: 'val',
44599                 typeAhead: false,
44600                 mode: 'local',
44601                 editable : false,
44602                 triggerAction: 'all',
44603                 emptyText:'Select Style',
44604                 selectOnFocus:true,
44605                 width: 130,
44606                 listeners : {
44607                     'select': function(c, r, i) {
44608                         // initial support only for on class per el..
44609                         tb.selectedNode.className =  r ? r.get('val') : '';
44610                         editorcore.syncValue();
44611                     }
44612                 }
44613     
44614             }));
44615         }
44616         
44617         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44618         var tbops = tbc.options;
44619         
44620         for (var i in tlist) {
44621             
44622             var item = tlist[i];
44623             tb.add(item.title + ":&nbsp;");
44624             
44625             
44626             //optname == used so you can configure the options available..
44627             var opts = item.opts ? item.opts : false;
44628             if (item.optname) {
44629                 opts = tbops[item.optname];
44630            
44631             }
44632             
44633             if (opts) {
44634                 // opts == pulldown..
44635                 tb.addField( new Roo.form.ComboBox({
44636                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44637                         id : 'val',
44638                         fields: ['val', 'display'],
44639                         data : opts  
44640                     }),
44641                     name : '-roo-edit-' + i,
44642                     attrname : i,
44643                     stylename : item.style ? item.style : false,
44644                     displayField: item.displayField ? item.displayField : 'val',
44645                     valueField :  'val',
44646                     typeAhead: false,
44647                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44648                     editable : false,
44649                     triggerAction: 'all',
44650                     emptyText:'Select',
44651                     selectOnFocus:true,
44652                     width: item.width ? item.width  : 130,
44653                     listeners : {
44654                         'select': function(c, r, i) {
44655                             if (c.stylename) {
44656                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44657                                 return;
44658                             }
44659                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44660                         }
44661                     }
44662
44663                 }));
44664                 continue;
44665                     
44666                  
44667                 
44668                 tb.addField( new Roo.form.TextField({
44669                     name: i,
44670                     width: 100,
44671                     //allowBlank:false,
44672                     value: ''
44673                 }));
44674                 continue;
44675             }
44676             tb.addField( new Roo.form.TextField({
44677                 name: '-roo-edit-' + i,
44678                 attrname : i,
44679                 
44680                 width: item.width,
44681                 //allowBlank:true,
44682                 value: '',
44683                 listeners: {
44684                     'change' : function(f, nv, ov) {
44685                         tb.selectedNode.setAttribute(f.attrname, nv);
44686                     }
44687                 }
44688             }));
44689              
44690         }
44691         
44692         var _this = this;
44693         
44694         if(nm == 'BODY'){
44695             tb.addSeparator();
44696         
44697             tb.addButton( {
44698                 text: 'Stylesheets',
44699
44700                 listeners : {
44701                     click : function ()
44702                     {
44703                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44704                     }
44705                 }
44706             });
44707         }
44708         
44709         tb.addFill();
44710         tb.addButton( {
44711             text: 'Remove Tag',
44712     
44713             listeners : {
44714                 click : function ()
44715                 {
44716                     // remove
44717                     // undo does not work.
44718                      
44719                     var sn = tb.selectedNode;
44720                     
44721                     var pn = sn.parentNode;
44722                     
44723                     var stn =  sn.childNodes[0];
44724                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44725                     while (sn.childNodes.length) {
44726                         var node = sn.childNodes[0];
44727                         sn.removeChild(node);
44728                         //Roo.log(node);
44729                         pn.insertBefore(node, sn);
44730                         
44731                     }
44732                     pn.removeChild(sn);
44733                     var range = editorcore.createRange();
44734         
44735                     range.setStart(stn,0);
44736                     range.setEnd(en,0); //????
44737                     //range.selectNode(sel);
44738                     
44739                     
44740                     var selection = editorcore.getSelection();
44741                     selection.removeAllRanges();
44742                     selection.addRange(range);
44743                     
44744                     
44745                     
44746                     //_this.updateToolbar(null, null, pn);
44747                     _this.updateToolbar(null, null, null);
44748                     _this.footDisp.dom.innerHTML = ''; 
44749                 }
44750             }
44751             
44752                     
44753                 
44754             
44755         });
44756         
44757         
44758         tb.el.on('click', function(e){
44759             e.preventDefault(); // what does this do?
44760         });
44761         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44762         tb.el.hide();
44763         tb.name = nm;
44764         // dont need to disable them... as they will get hidden
44765         return tb;
44766          
44767         
44768     },
44769     buildFooter : function()
44770     {
44771         
44772         var fel = this.editor.wrap.createChild();
44773         this.footer = new Roo.Toolbar(fel);
44774         // toolbar has scrolly on left / right?
44775         var footDisp= new Roo.Toolbar.Fill();
44776         var _t = this;
44777         this.footer.add(
44778             {
44779                 text : '&lt;',
44780                 xtype: 'Button',
44781                 handler : function() {
44782                     _t.footDisp.scrollTo('left',0,true)
44783                 }
44784             }
44785         );
44786         this.footer.add( footDisp );
44787         this.footer.add( 
44788             {
44789                 text : '&gt;',
44790                 xtype: 'Button',
44791                 handler : function() {
44792                     // no animation..
44793                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44794                 }
44795             }
44796         );
44797         var fel = Roo.get(footDisp.el);
44798         fel.addClass('x-editor-context');
44799         this.footDispWrap = fel; 
44800         this.footDispWrap.overflow  = 'hidden';
44801         
44802         this.footDisp = fel.createChild();
44803         this.footDispWrap.on('click', this.onContextClick, this)
44804         
44805         
44806     },
44807     onContextClick : function (ev,dom)
44808     {
44809         ev.preventDefault();
44810         var  cn = dom.className;
44811         //Roo.log(cn);
44812         if (!cn.match(/x-ed-loc-/)) {
44813             return;
44814         }
44815         var n = cn.split('-').pop();
44816         var ans = this.footerEls;
44817         var sel = ans[n];
44818         
44819          // pick
44820         var range = this.editorcore.createRange();
44821         
44822         range.selectNodeContents(sel);
44823         //range.selectNode(sel);
44824         
44825         
44826         var selection = this.editorcore.getSelection();
44827         selection.removeAllRanges();
44828         selection.addRange(range);
44829         
44830         
44831         
44832         this.updateToolbar(null, null, sel);
44833         
44834         
44835     }
44836     
44837     
44838     
44839     
44840     
44841 });
44842
44843
44844
44845
44846
44847 /*
44848  * Based on:
44849  * Ext JS Library 1.1.1
44850  * Copyright(c) 2006-2007, Ext JS, LLC.
44851  *
44852  * Originally Released Under LGPL - original licence link has changed is not relivant.
44853  *
44854  * Fork - LGPL
44855  * <script type="text/javascript">
44856  */
44857  
44858 /**
44859  * @class Roo.form.BasicForm
44860  * @extends Roo.util.Observable
44861  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44862  * @constructor
44863  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44864  * @param {Object} config Configuration options
44865  */
44866 Roo.form.BasicForm = function(el, config){
44867     this.allItems = [];
44868     this.childForms = [];
44869     Roo.apply(this, config);
44870     /*
44871      * The Roo.form.Field items in this form.
44872      * @type MixedCollection
44873      */
44874      
44875      
44876     this.items = new Roo.util.MixedCollection(false, function(o){
44877         return o.id || (o.id = Roo.id());
44878     });
44879     this.addEvents({
44880         /**
44881          * @event beforeaction
44882          * Fires before any action is performed. Return false to cancel the action.
44883          * @param {Form} this
44884          * @param {Action} action The action to be performed
44885          */
44886         beforeaction: true,
44887         /**
44888          * @event actionfailed
44889          * Fires when an action fails.
44890          * @param {Form} this
44891          * @param {Action} action The action that failed
44892          */
44893         actionfailed : true,
44894         /**
44895          * @event actioncomplete
44896          * Fires when an action is completed.
44897          * @param {Form} this
44898          * @param {Action} action The action that completed
44899          */
44900         actioncomplete : true
44901     });
44902     if(el){
44903         this.initEl(el);
44904     }
44905     Roo.form.BasicForm.superclass.constructor.call(this);
44906 };
44907
44908 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44909     /**
44910      * @cfg {String} method
44911      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44912      */
44913     /**
44914      * @cfg {DataReader} reader
44915      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44916      * This is optional as there is built-in support for processing JSON.
44917      */
44918     /**
44919      * @cfg {DataReader} errorReader
44920      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44921      * This is completely optional as there is built-in support for processing JSON.
44922      */
44923     /**
44924      * @cfg {String} url
44925      * The URL to use for form actions if one isn't supplied in the action options.
44926      */
44927     /**
44928      * @cfg {Boolean} fileUpload
44929      * Set to true if this form is a file upload.
44930      */
44931      
44932     /**
44933      * @cfg {Object} baseParams
44934      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44935      */
44936      /**
44937      
44938     /**
44939      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44940      */
44941     timeout: 30,
44942
44943     // private
44944     activeAction : null,
44945
44946     /**
44947      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44948      * or setValues() data instead of when the form was first created.
44949      */
44950     trackResetOnLoad : false,
44951     
44952     
44953     /**
44954      * childForms - used for multi-tab forms
44955      * @type {Array}
44956      */
44957     childForms : false,
44958     
44959     /**
44960      * allItems - full list of fields.
44961      * @type {Array}
44962      */
44963     allItems : false,
44964     
44965     /**
44966      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44967      * element by passing it or its id or mask the form itself by passing in true.
44968      * @type Mixed
44969      */
44970     waitMsgTarget : false,
44971
44972     // private
44973     initEl : function(el){
44974         this.el = Roo.get(el);
44975         this.id = this.el.id || Roo.id();
44976         this.el.on('submit', this.onSubmit, this);
44977         this.el.addClass('x-form');
44978     },
44979
44980     // private
44981     onSubmit : function(e){
44982         e.stopEvent();
44983     },
44984
44985     /**
44986      * Returns true if client-side validation on the form is successful.
44987      * @return Boolean
44988      */
44989     isValid : function(){
44990         var valid = true;
44991         this.items.each(function(f){
44992            if(!f.validate()){
44993                valid = false;
44994            }
44995         });
44996         return valid;
44997     },
44998
44999     /**
45000      * Returns true if any fields in this form have changed since their original load.
45001      * @return Boolean
45002      */
45003     isDirty : function(){
45004         var dirty = false;
45005         this.items.each(function(f){
45006            if(f.isDirty()){
45007                dirty = true;
45008                return false;
45009            }
45010         });
45011         return dirty;
45012     },
45013
45014     /**
45015      * Performs a predefined action (submit or load) or custom actions you define on this form.
45016      * @param {String} actionName The name of the action type
45017      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45018      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45019      * accept other config options):
45020      * <pre>
45021 Property          Type             Description
45022 ----------------  ---------------  ----------------------------------------------------------------------------------
45023 url               String           The url for the action (defaults to the form's url)
45024 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45025 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45026 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45027                                    validate the form on the client (defaults to false)
45028      * </pre>
45029      * @return {BasicForm} this
45030      */
45031     doAction : function(action, options){
45032         if(typeof action == 'string'){
45033             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45034         }
45035         if(this.fireEvent('beforeaction', this, action) !== false){
45036             this.beforeAction(action);
45037             action.run.defer(100, action);
45038         }
45039         return this;
45040     },
45041
45042     /**
45043      * Shortcut to do a submit action.
45044      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45045      * @return {BasicForm} this
45046      */
45047     submit : function(options){
45048         this.doAction('submit', options);
45049         return this;
45050     },
45051
45052     /**
45053      * Shortcut to do a load action.
45054      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45055      * @return {BasicForm} this
45056      */
45057     load : function(options){
45058         this.doAction('load', options);
45059         return this;
45060     },
45061
45062     /**
45063      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45064      * @param {Record} record The record to edit
45065      * @return {BasicForm} this
45066      */
45067     updateRecord : function(record){
45068         record.beginEdit();
45069         var fs = record.fields;
45070         fs.each(function(f){
45071             var field = this.findField(f.name);
45072             if(field){
45073                 record.set(f.name, field.getValue());
45074             }
45075         }, this);
45076         record.endEdit();
45077         return this;
45078     },
45079
45080     /**
45081      * Loads an Roo.data.Record into this form.
45082      * @param {Record} record The record to load
45083      * @return {BasicForm} this
45084      */
45085     loadRecord : function(record){
45086         this.setValues(record.data);
45087         return this;
45088     },
45089
45090     // private
45091     beforeAction : function(action){
45092         var o = action.options;
45093         
45094        
45095         if(this.waitMsgTarget === true){
45096             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45097         }else if(this.waitMsgTarget){
45098             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45099             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45100         }else {
45101             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45102         }
45103          
45104     },
45105
45106     // private
45107     afterAction : function(action, success){
45108         this.activeAction = null;
45109         var o = action.options;
45110         
45111         if(this.waitMsgTarget === true){
45112             this.el.unmask();
45113         }else if(this.waitMsgTarget){
45114             this.waitMsgTarget.unmask();
45115         }else{
45116             Roo.MessageBox.updateProgress(1);
45117             Roo.MessageBox.hide();
45118         }
45119          
45120         if(success){
45121             if(o.reset){
45122                 this.reset();
45123             }
45124             Roo.callback(o.success, o.scope, [this, action]);
45125             this.fireEvent('actioncomplete', this, action);
45126             
45127         }else{
45128             
45129             // failure condition..
45130             // we have a scenario where updates need confirming.
45131             // eg. if a locking scenario exists..
45132             // we look for { errors : { needs_confirm : true }} in the response.
45133             if (
45134                 (typeof(action.result) != 'undefined')  &&
45135                 (typeof(action.result.errors) != 'undefined')  &&
45136                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45137            ){
45138                 var _t = this;
45139                 Roo.MessageBox.confirm(
45140                     "Change requires confirmation",
45141                     action.result.errorMsg,
45142                     function(r) {
45143                         if (r != 'yes') {
45144                             return;
45145                         }
45146                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45147                     }
45148                     
45149                 );
45150                 
45151                 
45152                 
45153                 return;
45154             }
45155             
45156             Roo.callback(o.failure, o.scope, [this, action]);
45157             // show an error message if no failed handler is set..
45158             if (!this.hasListener('actionfailed')) {
45159                 Roo.MessageBox.alert("Error",
45160                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45161                         action.result.errorMsg :
45162                         "Saving Failed, please check your entries or try again"
45163                 );
45164             }
45165             
45166             this.fireEvent('actionfailed', this, action);
45167         }
45168         
45169     },
45170
45171     /**
45172      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45173      * @param {String} id The value to search for
45174      * @return Field
45175      */
45176     findField : function(id){
45177         var field = this.items.get(id);
45178         if(!field){
45179             this.items.each(function(f){
45180                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45181                     field = f;
45182                     return false;
45183                 }
45184             });
45185         }
45186         return field || null;
45187     },
45188
45189     /**
45190      * Add a secondary form to this one, 
45191      * Used to provide tabbed forms. One form is primary, with hidden values 
45192      * which mirror the elements from the other forms.
45193      * 
45194      * @param {Roo.form.Form} form to add.
45195      * 
45196      */
45197     addForm : function(form)
45198     {
45199        
45200         if (this.childForms.indexOf(form) > -1) {
45201             // already added..
45202             return;
45203         }
45204         this.childForms.push(form);
45205         var n = '';
45206         Roo.each(form.allItems, function (fe) {
45207             
45208             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45209             if (this.findField(n)) { // already added..
45210                 return;
45211             }
45212             var add = new Roo.form.Hidden({
45213                 name : n
45214             });
45215             add.render(this.el);
45216             
45217             this.add( add );
45218         }, this);
45219         
45220     },
45221     /**
45222      * Mark fields in this form invalid in bulk.
45223      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45224      * @return {BasicForm} this
45225      */
45226     markInvalid : function(errors){
45227         if(errors instanceof Array){
45228             for(var i = 0, len = errors.length; i < len; i++){
45229                 var fieldError = errors[i];
45230                 var f = this.findField(fieldError.id);
45231                 if(f){
45232                     f.markInvalid(fieldError.msg);
45233                 }
45234             }
45235         }else{
45236             var field, id;
45237             for(id in errors){
45238                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45239                     field.markInvalid(errors[id]);
45240                 }
45241             }
45242         }
45243         Roo.each(this.childForms || [], function (f) {
45244             f.markInvalid(errors);
45245         });
45246         
45247         return this;
45248     },
45249
45250     /**
45251      * Set values for fields in this form in bulk.
45252      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45253      * @return {BasicForm} this
45254      */
45255     setValues : function(values){
45256         if(values instanceof Array){ // array of objects
45257             for(var i = 0, len = values.length; i < len; i++){
45258                 var v = values[i];
45259                 var f = this.findField(v.id);
45260                 if(f){
45261                     f.setValue(v.value);
45262                     if(this.trackResetOnLoad){
45263                         f.originalValue = f.getValue();
45264                     }
45265                 }
45266             }
45267         }else{ // object hash
45268             var field, id;
45269             for(id in values){
45270                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45271                     
45272                     if (field.setFromData && 
45273                         field.valueField && 
45274                         field.displayField &&
45275                         // combos' with local stores can 
45276                         // be queried via setValue()
45277                         // to set their value..
45278                         (field.store && !field.store.isLocal)
45279                         ) {
45280                         // it's a combo
45281                         var sd = { };
45282                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45283                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45284                         field.setFromData(sd);
45285                         
45286                     } else {
45287                         field.setValue(values[id]);
45288                     }
45289                     
45290                     
45291                     if(this.trackResetOnLoad){
45292                         field.originalValue = field.getValue();
45293                     }
45294                 }
45295             }
45296         }
45297          
45298         Roo.each(this.childForms || [], function (f) {
45299             f.setValues(values);
45300         });
45301                 
45302         return this;
45303     },
45304
45305     /**
45306      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45307      * they are returned as an array.
45308      * @param {Boolean} asString
45309      * @return {Object}
45310      */
45311     getValues : function(asString){
45312         if (this.childForms) {
45313             // copy values from the child forms
45314             Roo.each(this.childForms, function (f) {
45315                 this.setValues(f.getValues());
45316             }, this);
45317         }
45318         
45319         
45320         
45321         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45322         if(asString === true){
45323             return fs;
45324         }
45325         return Roo.urlDecode(fs);
45326     },
45327     
45328     /**
45329      * Returns the fields in this form as an object with key/value pairs. 
45330      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45331      * @return {Object}
45332      */
45333     getFieldValues : function(with_hidden)
45334     {
45335         if (this.childForms) {
45336             // copy values from the child forms
45337             // should this call getFieldValues - probably not as we do not currently copy
45338             // hidden fields when we generate..
45339             Roo.each(this.childForms, function (f) {
45340                 this.setValues(f.getValues());
45341             }, this);
45342         }
45343         
45344         var ret = {};
45345         this.items.each(function(f){
45346             if (!f.getName()) {
45347                 return;
45348             }
45349             var v = f.getValue();
45350             if (f.inputType =='radio') {
45351                 if (typeof(ret[f.getName()]) == 'undefined') {
45352                     ret[f.getName()] = ''; // empty..
45353                 }
45354                 
45355                 if (!f.el.dom.checked) {
45356                     return;
45357                     
45358                 }
45359                 v = f.el.dom.value;
45360                 
45361             }
45362             
45363             // not sure if this supported any more..
45364             if ((typeof(v) == 'object') && f.getRawValue) {
45365                 v = f.getRawValue() ; // dates..
45366             }
45367             // combo boxes where name != hiddenName...
45368             if (f.name != f.getName()) {
45369                 ret[f.name] = f.getRawValue();
45370             }
45371             ret[f.getName()] = v;
45372         });
45373         
45374         return ret;
45375     },
45376
45377     /**
45378      * Clears all invalid messages in this form.
45379      * @return {BasicForm} this
45380      */
45381     clearInvalid : function(){
45382         this.items.each(function(f){
45383            f.clearInvalid();
45384         });
45385         
45386         Roo.each(this.childForms || [], function (f) {
45387             f.clearInvalid();
45388         });
45389         
45390         
45391         return this;
45392     },
45393
45394     /**
45395      * Resets this form.
45396      * @return {BasicForm} this
45397      */
45398     reset : function(){
45399         this.items.each(function(f){
45400             f.reset();
45401         });
45402         
45403         Roo.each(this.childForms || [], function (f) {
45404             f.reset();
45405         });
45406        
45407         
45408         return this;
45409     },
45410
45411     /**
45412      * Add Roo.form components to this form.
45413      * @param {Field} field1
45414      * @param {Field} field2 (optional)
45415      * @param {Field} etc (optional)
45416      * @return {BasicForm} this
45417      */
45418     add : function(){
45419         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45420         return this;
45421     },
45422
45423
45424     /**
45425      * Removes a field from the items collection (does NOT remove its markup).
45426      * @param {Field} field
45427      * @return {BasicForm} this
45428      */
45429     remove : function(field){
45430         this.items.remove(field);
45431         return this;
45432     },
45433
45434     /**
45435      * Looks at the fields in this form, checks them for an id attribute,
45436      * and calls applyTo on the existing dom element with that id.
45437      * @return {BasicForm} this
45438      */
45439     render : function(){
45440         this.items.each(function(f){
45441             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45442                 f.applyTo(f.id);
45443             }
45444         });
45445         return this;
45446     },
45447
45448     /**
45449      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45450      * @param {Object} values
45451      * @return {BasicForm} this
45452      */
45453     applyToFields : function(o){
45454         this.items.each(function(f){
45455            Roo.apply(f, o);
45456         });
45457         return this;
45458     },
45459
45460     /**
45461      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45462      * @param {Object} values
45463      * @return {BasicForm} this
45464      */
45465     applyIfToFields : function(o){
45466         this.items.each(function(f){
45467            Roo.applyIf(f, o);
45468         });
45469         return this;
45470     }
45471 });
45472
45473 // back compat
45474 Roo.BasicForm = Roo.form.BasicForm;/*
45475  * Based on:
45476  * Ext JS Library 1.1.1
45477  * Copyright(c) 2006-2007, Ext JS, LLC.
45478  *
45479  * Originally Released Under LGPL - original licence link has changed is not relivant.
45480  *
45481  * Fork - LGPL
45482  * <script type="text/javascript">
45483  */
45484
45485 /**
45486  * @class Roo.form.Form
45487  * @extends Roo.form.BasicForm
45488  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45489  * @constructor
45490  * @param {Object} config Configuration options
45491  */
45492 Roo.form.Form = function(config){
45493     var xitems =  [];
45494     if (config.items) {
45495         xitems = config.items;
45496         delete config.items;
45497     }
45498    
45499     
45500     Roo.form.Form.superclass.constructor.call(this, null, config);
45501     this.url = this.url || this.action;
45502     if(!this.root){
45503         this.root = new Roo.form.Layout(Roo.applyIf({
45504             id: Roo.id()
45505         }, config));
45506     }
45507     this.active = this.root;
45508     /**
45509      * Array of all the buttons that have been added to this form via {@link addButton}
45510      * @type Array
45511      */
45512     this.buttons = [];
45513     this.allItems = [];
45514     this.addEvents({
45515         /**
45516          * @event clientvalidation
45517          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45518          * @param {Form} this
45519          * @param {Boolean} valid true if the form has passed client-side validation
45520          */
45521         clientvalidation: true,
45522         /**
45523          * @event rendered
45524          * Fires when the form is rendered
45525          * @param {Roo.form.Form} form
45526          */
45527         rendered : true
45528     });
45529     
45530     if (this.progressUrl) {
45531             // push a hidden field onto the list of fields..
45532             this.addxtype( {
45533                     xns: Roo.form, 
45534                     xtype : 'Hidden', 
45535                     name : 'UPLOAD_IDENTIFIER' 
45536             });
45537         }
45538         
45539     
45540     Roo.each(xitems, this.addxtype, this);
45541     
45542     
45543     
45544 };
45545
45546 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45547     /**
45548      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45549      */
45550     /**
45551      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45552      */
45553     /**
45554      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45555      */
45556     buttonAlign:'center',
45557
45558     /**
45559      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45560      */
45561     minButtonWidth:75,
45562
45563     /**
45564      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45565      * This property cascades to child containers if not set.
45566      */
45567     labelAlign:'left',
45568
45569     /**
45570      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45571      * fires a looping event with that state. This is required to bind buttons to the valid
45572      * state using the config value formBind:true on the button.
45573      */
45574     monitorValid : false,
45575
45576     /**
45577      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45578      */
45579     monitorPoll : 200,
45580     
45581     /**
45582      * @cfg {String} progressUrl - Url to return progress data 
45583      */
45584     
45585     progressUrl : false,
45586   
45587     /**
45588      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45589      * fields are added and the column is closed. If no fields are passed the column remains open
45590      * until end() is called.
45591      * @param {Object} config The config to pass to the column
45592      * @param {Field} field1 (optional)
45593      * @param {Field} field2 (optional)
45594      * @param {Field} etc (optional)
45595      * @return Column The column container object
45596      */
45597     column : function(c){
45598         var col = new Roo.form.Column(c);
45599         this.start(col);
45600         if(arguments.length > 1){ // duplicate code required because of Opera
45601             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45602             this.end();
45603         }
45604         return col;
45605     },
45606
45607     /**
45608      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45609      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45610      * until end() is called.
45611      * @param {Object} config The config to pass to the fieldset
45612      * @param {Field} field1 (optional)
45613      * @param {Field} field2 (optional)
45614      * @param {Field} etc (optional)
45615      * @return FieldSet The fieldset container object
45616      */
45617     fieldset : function(c){
45618         var fs = new Roo.form.FieldSet(c);
45619         this.start(fs);
45620         if(arguments.length > 1){ // duplicate code required because of Opera
45621             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45622             this.end();
45623         }
45624         return fs;
45625     },
45626
45627     /**
45628      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45629      * fields are added and the container is closed. If no fields are passed the container remains open
45630      * until end() is called.
45631      * @param {Object} config The config to pass to the Layout
45632      * @param {Field} field1 (optional)
45633      * @param {Field} field2 (optional)
45634      * @param {Field} etc (optional)
45635      * @return Layout The container object
45636      */
45637     container : function(c){
45638         var l = new Roo.form.Layout(c);
45639         this.start(l);
45640         if(arguments.length > 1){ // duplicate code required because of Opera
45641             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45642             this.end();
45643         }
45644         return l;
45645     },
45646
45647     /**
45648      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45649      * @param {Object} container A Roo.form.Layout or subclass of Layout
45650      * @return {Form} this
45651      */
45652     start : function(c){
45653         // cascade label info
45654         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45655         this.active.stack.push(c);
45656         c.ownerCt = this.active;
45657         this.active = c;
45658         return this;
45659     },
45660
45661     /**
45662      * Closes the current open container
45663      * @return {Form} this
45664      */
45665     end : function(){
45666         if(this.active == this.root){
45667             return this;
45668         }
45669         this.active = this.active.ownerCt;
45670         return this;
45671     },
45672
45673     /**
45674      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45675      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45676      * as the label of the field.
45677      * @param {Field} field1
45678      * @param {Field} field2 (optional)
45679      * @param {Field} etc. (optional)
45680      * @return {Form} this
45681      */
45682     add : function(){
45683         this.active.stack.push.apply(this.active.stack, arguments);
45684         this.allItems.push.apply(this.allItems,arguments);
45685         var r = [];
45686         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45687             if(a[i].isFormField){
45688                 r.push(a[i]);
45689             }
45690         }
45691         if(r.length > 0){
45692             Roo.form.Form.superclass.add.apply(this, r);
45693         }
45694         return this;
45695     },
45696     
45697
45698     
45699     
45700     
45701      /**
45702      * Find any element that has been added to a form, using it's ID or name
45703      * This can include framesets, columns etc. along with regular fields..
45704      * @param {String} id - id or name to find.
45705      
45706      * @return {Element} e - or false if nothing found.
45707      */
45708     findbyId : function(id)
45709     {
45710         var ret = false;
45711         if (!id) {
45712             return ret;
45713         }
45714         Roo.each(this.allItems, function(f){
45715             if (f.id == id || f.name == id ){
45716                 ret = f;
45717                 return false;
45718             }
45719         });
45720         return ret;
45721     },
45722
45723     
45724     
45725     /**
45726      * Render this form into the passed container. This should only be called once!
45727      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45728      * @return {Form} this
45729      */
45730     render : function(ct)
45731     {
45732         
45733         
45734         
45735         ct = Roo.get(ct);
45736         var o = this.autoCreate || {
45737             tag: 'form',
45738             method : this.method || 'POST',
45739             id : this.id || Roo.id()
45740         };
45741         this.initEl(ct.createChild(o));
45742
45743         this.root.render(this.el);
45744         
45745        
45746              
45747         this.items.each(function(f){
45748             f.render('x-form-el-'+f.id);
45749         });
45750
45751         if(this.buttons.length > 0){
45752             // tables are required to maintain order and for correct IE layout
45753             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45754                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45755                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45756             }}, null, true);
45757             var tr = tb.getElementsByTagName('tr')[0];
45758             for(var i = 0, len = this.buttons.length; i < len; i++) {
45759                 var b = this.buttons[i];
45760                 var td = document.createElement('td');
45761                 td.className = 'x-form-btn-td';
45762                 b.render(tr.appendChild(td));
45763             }
45764         }
45765         if(this.monitorValid){ // initialize after render
45766             this.startMonitoring();
45767         }
45768         this.fireEvent('rendered', this);
45769         return this;
45770     },
45771
45772     /**
45773      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45774      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45775      * object or a valid Roo.DomHelper element config
45776      * @param {Function} handler The function called when the button is clicked
45777      * @param {Object} scope (optional) The scope of the handler function
45778      * @return {Roo.Button}
45779      */
45780     addButton : function(config, handler, scope){
45781         var bc = {
45782             handler: handler,
45783             scope: scope,
45784             minWidth: this.minButtonWidth,
45785             hideParent:true
45786         };
45787         if(typeof config == "string"){
45788             bc.text = config;
45789         }else{
45790             Roo.apply(bc, config);
45791         }
45792         var btn = new Roo.Button(null, bc);
45793         this.buttons.push(btn);
45794         return btn;
45795     },
45796
45797      /**
45798      * Adds a series of form elements (using the xtype property as the factory method.
45799      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45800      * @param {Object} config 
45801      */
45802     
45803     addxtype : function()
45804     {
45805         var ar = Array.prototype.slice.call(arguments, 0);
45806         var ret = false;
45807         for(var i = 0; i < ar.length; i++) {
45808             if (!ar[i]) {
45809                 continue; // skip -- if this happends something invalid got sent, we 
45810                 // should ignore it, as basically that interface element will not show up
45811                 // and that should be pretty obvious!!
45812             }
45813             
45814             if (Roo.form[ar[i].xtype]) {
45815                 ar[i].form = this;
45816                 var fe = Roo.factory(ar[i], Roo.form);
45817                 if (!ret) {
45818                     ret = fe;
45819                 }
45820                 fe.form = this;
45821                 if (fe.store) {
45822                     fe.store.form = this;
45823                 }
45824                 if (fe.isLayout) {  
45825                          
45826                     this.start(fe);
45827                     this.allItems.push(fe);
45828                     if (fe.items && fe.addxtype) {
45829                         fe.addxtype.apply(fe, fe.items);
45830                         delete fe.items;
45831                     }
45832                      this.end();
45833                     continue;
45834                 }
45835                 
45836                 
45837                  
45838                 this.add(fe);
45839               //  console.log('adding ' + ar[i].xtype);
45840             }
45841             if (ar[i].xtype == 'Button') {  
45842                 //console.log('adding button');
45843                 //console.log(ar[i]);
45844                 this.addButton(ar[i]);
45845                 this.allItems.push(fe);
45846                 continue;
45847             }
45848             
45849             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45850                 alert('end is not supported on xtype any more, use items');
45851             //    this.end();
45852             //    //console.log('adding end');
45853             }
45854             
45855         }
45856         return ret;
45857     },
45858     
45859     /**
45860      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45861      * option "monitorValid"
45862      */
45863     startMonitoring : function(){
45864         if(!this.bound){
45865             this.bound = true;
45866             Roo.TaskMgr.start({
45867                 run : this.bindHandler,
45868                 interval : this.monitorPoll || 200,
45869                 scope: this
45870             });
45871         }
45872     },
45873
45874     /**
45875      * Stops monitoring of the valid state of this form
45876      */
45877     stopMonitoring : function(){
45878         this.bound = false;
45879     },
45880
45881     // private
45882     bindHandler : function(){
45883         if(!this.bound){
45884             return false; // stops binding
45885         }
45886         var valid = true;
45887         this.items.each(function(f){
45888             if(!f.isValid(true)){
45889                 valid = false;
45890                 return false;
45891             }
45892         });
45893         for(var i = 0, len = this.buttons.length; i < len; i++){
45894             var btn = this.buttons[i];
45895             if(btn.formBind === true && btn.disabled === valid){
45896                 btn.setDisabled(!valid);
45897             }
45898         }
45899         this.fireEvent('clientvalidation', this, valid);
45900     }
45901     
45902     
45903     
45904     
45905     
45906     
45907     
45908     
45909 });
45910
45911
45912 // back compat
45913 Roo.Form = Roo.form.Form;
45914 /*
45915  * Based on:
45916  * Ext JS Library 1.1.1
45917  * Copyright(c) 2006-2007, Ext JS, LLC.
45918  *
45919  * Originally Released Under LGPL - original licence link has changed is not relivant.
45920  *
45921  * Fork - LGPL
45922  * <script type="text/javascript">
45923  */
45924
45925 // as we use this in bootstrap.
45926 Roo.namespace('Roo.form');
45927  /**
45928  * @class Roo.form.Action
45929  * Internal Class used to handle form actions
45930  * @constructor
45931  * @param {Roo.form.BasicForm} el The form element or its id
45932  * @param {Object} config Configuration options
45933  */
45934
45935  
45936  
45937 // define the action interface
45938 Roo.form.Action = function(form, options){
45939     this.form = form;
45940     this.options = options || {};
45941 };
45942 /**
45943  * Client Validation Failed
45944  * @const 
45945  */
45946 Roo.form.Action.CLIENT_INVALID = 'client';
45947 /**
45948  * Server Validation Failed
45949  * @const 
45950  */
45951 Roo.form.Action.SERVER_INVALID = 'server';
45952  /**
45953  * Connect to Server Failed
45954  * @const 
45955  */
45956 Roo.form.Action.CONNECT_FAILURE = 'connect';
45957 /**
45958  * Reading Data from Server Failed
45959  * @const 
45960  */
45961 Roo.form.Action.LOAD_FAILURE = 'load';
45962
45963 Roo.form.Action.prototype = {
45964     type : 'default',
45965     failureType : undefined,
45966     response : undefined,
45967     result : undefined,
45968
45969     // interface method
45970     run : function(options){
45971
45972     },
45973
45974     // interface method
45975     success : function(response){
45976
45977     },
45978
45979     // interface method
45980     handleResponse : function(response){
45981
45982     },
45983
45984     // default connection failure
45985     failure : function(response){
45986         
45987         this.response = response;
45988         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45989         this.form.afterAction(this, false);
45990     },
45991
45992     processResponse : function(response){
45993         this.response = response;
45994         if(!response.responseText){
45995             return true;
45996         }
45997         this.result = this.handleResponse(response);
45998         return this.result;
45999     },
46000
46001     // utility functions used internally
46002     getUrl : function(appendParams){
46003         var url = this.options.url || this.form.url || this.form.el.dom.action;
46004         if(appendParams){
46005             var p = this.getParams();
46006             if(p){
46007                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46008             }
46009         }
46010         return url;
46011     },
46012
46013     getMethod : function(){
46014         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46015     },
46016
46017     getParams : function(){
46018         var bp = this.form.baseParams;
46019         var p = this.options.params;
46020         if(p){
46021             if(typeof p == "object"){
46022                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46023             }else if(typeof p == 'string' && bp){
46024                 p += '&' + Roo.urlEncode(bp);
46025             }
46026         }else if(bp){
46027             p = Roo.urlEncode(bp);
46028         }
46029         return p;
46030     },
46031
46032     createCallback : function(){
46033         return {
46034             success: this.success,
46035             failure: this.failure,
46036             scope: this,
46037             timeout: (this.form.timeout*1000),
46038             upload: this.form.fileUpload ? this.success : undefined
46039         };
46040     }
46041 };
46042
46043 Roo.form.Action.Submit = function(form, options){
46044     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46045 };
46046
46047 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46048     type : 'submit',
46049
46050     haveProgress : false,
46051     uploadComplete : false,
46052     
46053     // uploadProgress indicator.
46054     uploadProgress : function()
46055     {
46056         if (!this.form.progressUrl) {
46057             return;
46058         }
46059         
46060         if (!this.haveProgress) {
46061             Roo.MessageBox.progress("Uploading", "Uploading");
46062         }
46063         if (this.uploadComplete) {
46064            Roo.MessageBox.hide();
46065            return;
46066         }
46067         
46068         this.haveProgress = true;
46069    
46070         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46071         
46072         var c = new Roo.data.Connection();
46073         c.request({
46074             url : this.form.progressUrl,
46075             params: {
46076                 id : uid
46077             },
46078             method: 'GET',
46079             success : function(req){
46080                //console.log(data);
46081                 var rdata = false;
46082                 var edata;
46083                 try  {
46084                    rdata = Roo.decode(req.responseText)
46085                 } catch (e) {
46086                     Roo.log("Invalid data from server..");
46087                     Roo.log(edata);
46088                     return;
46089                 }
46090                 if (!rdata || !rdata.success) {
46091                     Roo.log(rdata);
46092                     Roo.MessageBox.alert(Roo.encode(rdata));
46093                     return;
46094                 }
46095                 var data = rdata.data;
46096                 
46097                 if (this.uploadComplete) {
46098                    Roo.MessageBox.hide();
46099                    return;
46100                 }
46101                    
46102                 if (data){
46103                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46104                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46105                     );
46106                 }
46107                 this.uploadProgress.defer(2000,this);
46108             },
46109        
46110             failure: function(data) {
46111                 Roo.log('progress url failed ');
46112                 Roo.log(data);
46113             },
46114             scope : this
46115         });
46116            
46117     },
46118     
46119     
46120     run : function()
46121     {
46122         // run get Values on the form, so it syncs any secondary forms.
46123         this.form.getValues();
46124         
46125         var o = this.options;
46126         var method = this.getMethod();
46127         var isPost = method == 'POST';
46128         if(o.clientValidation === false || this.form.isValid()){
46129             
46130             if (this.form.progressUrl) {
46131                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46132                     (new Date() * 1) + '' + Math.random());
46133                     
46134             } 
46135             
46136             
46137             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46138                 form:this.form.el.dom,
46139                 url:this.getUrl(!isPost),
46140                 method: method,
46141                 params:isPost ? this.getParams() : null,
46142                 isUpload: this.form.fileUpload
46143             }));
46144             
46145             this.uploadProgress();
46146
46147         }else if (o.clientValidation !== false){ // client validation failed
46148             this.failureType = Roo.form.Action.CLIENT_INVALID;
46149             this.form.afterAction(this, false);
46150         }
46151     },
46152
46153     success : function(response)
46154     {
46155         this.uploadComplete= true;
46156         if (this.haveProgress) {
46157             Roo.MessageBox.hide();
46158         }
46159         
46160         
46161         var result = this.processResponse(response);
46162         if(result === true || result.success){
46163             this.form.afterAction(this, true);
46164             return;
46165         }
46166         if(result.errors){
46167             this.form.markInvalid(result.errors);
46168             this.failureType = Roo.form.Action.SERVER_INVALID;
46169         }
46170         this.form.afterAction(this, false);
46171     },
46172     failure : function(response)
46173     {
46174         this.uploadComplete= true;
46175         if (this.haveProgress) {
46176             Roo.MessageBox.hide();
46177         }
46178         
46179         this.response = response;
46180         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46181         this.form.afterAction(this, false);
46182     },
46183     
46184     handleResponse : function(response){
46185         if(this.form.errorReader){
46186             var rs = this.form.errorReader.read(response);
46187             var errors = [];
46188             if(rs.records){
46189                 for(var i = 0, len = rs.records.length; i < len; i++) {
46190                     var r = rs.records[i];
46191                     errors[i] = r.data;
46192                 }
46193             }
46194             if(errors.length < 1){
46195                 errors = null;
46196             }
46197             return {
46198                 success : rs.success,
46199                 errors : errors
46200             };
46201         }
46202         var ret = false;
46203         try {
46204             ret = Roo.decode(response.responseText);
46205         } catch (e) {
46206             ret = {
46207                 success: false,
46208                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46209                 errors : []
46210             };
46211         }
46212         return ret;
46213         
46214     }
46215 });
46216
46217
46218 Roo.form.Action.Load = function(form, options){
46219     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46220     this.reader = this.form.reader;
46221 };
46222
46223 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46224     type : 'load',
46225
46226     run : function(){
46227         
46228         Roo.Ajax.request(Roo.apply(
46229                 this.createCallback(), {
46230                     method:this.getMethod(),
46231                     url:this.getUrl(false),
46232                     params:this.getParams()
46233         }));
46234     },
46235
46236     success : function(response){
46237         
46238         var result = this.processResponse(response);
46239         if(result === true || !result.success || !result.data){
46240             this.failureType = Roo.form.Action.LOAD_FAILURE;
46241             this.form.afterAction(this, false);
46242             return;
46243         }
46244         this.form.clearInvalid();
46245         this.form.setValues(result.data);
46246         this.form.afterAction(this, true);
46247     },
46248
46249     handleResponse : function(response){
46250         if(this.form.reader){
46251             var rs = this.form.reader.read(response);
46252             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46253             return {
46254                 success : rs.success,
46255                 data : data
46256             };
46257         }
46258         return Roo.decode(response.responseText);
46259     }
46260 });
46261
46262 Roo.form.Action.ACTION_TYPES = {
46263     'load' : Roo.form.Action.Load,
46264     'submit' : Roo.form.Action.Submit
46265 };/*
46266  * Based on:
46267  * Ext JS Library 1.1.1
46268  * Copyright(c) 2006-2007, Ext JS, LLC.
46269  *
46270  * Originally Released Under LGPL - original licence link has changed is not relivant.
46271  *
46272  * Fork - LGPL
46273  * <script type="text/javascript">
46274  */
46275  
46276 /**
46277  * @class Roo.form.Layout
46278  * @extends Roo.Component
46279  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46280  * @constructor
46281  * @param {Object} config Configuration options
46282  */
46283 Roo.form.Layout = function(config){
46284     var xitems = [];
46285     if (config.items) {
46286         xitems = config.items;
46287         delete config.items;
46288     }
46289     Roo.form.Layout.superclass.constructor.call(this, config);
46290     this.stack = [];
46291     Roo.each(xitems, this.addxtype, this);
46292      
46293 };
46294
46295 Roo.extend(Roo.form.Layout, Roo.Component, {
46296     /**
46297      * @cfg {String/Object} autoCreate
46298      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46299      */
46300     /**
46301      * @cfg {String/Object/Function} style
46302      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46303      * a function which returns such a specification.
46304      */
46305     /**
46306      * @cfg {String} labelAlign
46307      * Valid values are "left," "top" and "right" (defaults to "left")
46308      */
46309     /**
46310      * @cfg {Number} labelWidth
46311      * Fixed width in pixels of all field labels (defaults to undefined)
46312      */
46313     /**
46314      * @cfg {Boolean} clear
46315      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46316      */
46317     clear : true,
46318     /**
46319      * @cfg {String} labelSeparator
46320      * The separator to use after field labels (defaults to ':')
46321      */
46322     labelSeparator : ':',
46323     /**
46324      * @cfg {Boolean} hideLabels
46325      * True to suppress the display of field labels in this layout (defaults to false)
46326      */
46327     hideLabels : false,
46328
46329     // private
46330     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46331     
46332     isLayout : true,
46333     
46334     // private
46335     onRender : function(ct, position){
46336         if(this.el){ // from markup
46337             this.el = Roo.get(this.el);
46338         }else {  // generate
46339             var cfg = this.getAutoCreate();
46340             this.el = ct.createChild(cfg, position);
46341         }
46342         if(this.style){
46343             this.el.applyStyles(this.style);
46344         }
46345         if(this.labelAlign){
46346             this.el.addClass('x-form-label-'+this.labelAlign);
46347         }
46348         if(this.hideLabels){
46349             this.labelStyle = "display:none";
46350             this.elementStyle = "padding-left:0;";
46351         }else{
46352             if(typeof this.labelWidth == 'number'){
46353                 this.labelStyle = "width:"+this.labelWidth+"px;";
46354                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46355             }
46356             if(this.labelAlign == 'top'){
46357                 this.labelStyle = "width:auto;";
46358                 this.elementStyle = "padding-left:0;";
46359             }
46360         }
46361         var stack = this.stack;
46362         var slen = stack.length;
46363         if(slen > 0){
46364             if(!this.fieldTpl){
46365                 var t = new Roo.Template(
46366                     '<div class="x-form-item {5}">',
46367                         '<label for="{0}" style="{2}">{1}{4}</label>',
46368                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46369                         '</div>',
46370                     '</div><div class="x-form-clear-left"></div>'
46371                 );
46372                 t.disableFormats = true;
46373                 t.compile();
46374                 Roo.form.Layout.prototype.fieldTpl = t;
46375             }
46376             for(var i = 0; i < slen; i++) {
46377                 if(stack[i].isFormField){
46378                     this.renderField(stack[i]);
46379                 }else{
46380                     this.renderComponent(stack[i]);
46381                 }
46382             }
46383         }
46384         if(this.clear){
46385             this.el.createChild({cls:'x-form-clear'});
46386         }
46387     },
46388
46389     // private
46390     renderField : function(f){
46391         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46392                f.id, //0
46393                f.fieldLabel, //1
46394                f.labelStyle||this.labelStyle||'', //2
46395                this.elementStyle||'', //3
46396                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46397                f.itemCls||this.itemCls||''  //5
46398        ], true).getPrevSibling());
46399     },
46400
46401     // private
46402     renderComponent : function(c){
46403         c.render(c.isLayout ? this.el : this.el.createChild());    
46404     },
46405     /**
46406      * Adds a object form elements (using the xtype property as the factory method.)
46407      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46408      * @param {Object} config 
46409      */
46410     addxtype : function(o)
46411     {
46412         // create the lement.
46413         o.form = this.form;
46414         var fe = Roo.factory(o, Roo.form);
46415         this.form.allItems.push(fe);
46416         this.stack.push(fe);
46417         
46418         if (fe.isFormField) {
46419             this.form.items.add(fe);
46420         }
46421          
46422         return fe;
46423     }
46424 });
46425
46426 /**
46427  * @class Roo.form.Column
46428  * @extends Roo.form.Layout
46429  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46430  * @constructor
46431  * @param {Object} config Configuration options
46432  */
46433 Roo.form.Column = function(config){
46434     Roo.form.Column.superclass.constructor.call(this, config);
46435 };
46436
46437 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46438     /**
46439      * @cfg {Number/String} width
46440      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46441      */
46442     /**
46443      * @cfg {String/Object} autoCreate
46444      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46445      */
46446
46447     // private
46448     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46449
46450     // private
46451     onRender : function(ct, position){
46452         Roo.form.Column.superclass.onRender.call(this, ct, position);
46453         if(this.width){
46454             this.el.setWidth(this.width);
46455         }
46456     }
46457 });
46458
46459
46460 /**
46461  * @class Roo.form.Row
46462  * @extends Roo.form.Layout
46463  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46464  * @constructor
46465  * @param {Object} config Configuration options
46466  */
46467
46468  
46469 Roo.form.Row = function(config){
46470     Roo.form.Row.superclass.constructor.call(this, config);
46471 };
46472  
46473 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46474       /**
46475      * @cfg {Number/String} width
46476      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46477      */
46478     /**
46479      * @cfg {Number/String} height
46480      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46481      */
46482     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46483     
46484     padWidth : 20,
46485     // private
46486     onRender : function(ct, position){
46487         //console.log('row render');
46488         if(!this.rowTpl){
46489             var t = new Roo.Template(
46490                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46491                     '<label for="{0}" style="{2}">{1}{4}</label>',
46492                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46493                     '</div>',
46494                 '</div>'
46495             );
46496             t.disableFormats = true;
46497             t.compile();
46498             Roo.form.Layout.prototype.rowTpl = t;
46499         }
46500         this.fieldTpl = this.rowTpl;
46501         
46502         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46503         var labelWidth = 100;
46504         
46505         if ((this.labelAlign != 'top')) {
46506             if (typeof this.labelWidth == 'number') {
46507                 labelWidth = this.labelWidth
46508             }
46509             this.padWidth =  20 + labelWidth;
46510             
46511         }
46512         
46513         Roo.form.Column.superclass.onRender.call(this, ct, position);
46514         if(this.width){
46515             this.el.setWidth(this.width);
46516         }
46517         if(this.height){
46518             this.el.setHeight(this.height);
46519         }
46520     },
46521     
46522     // private
46523     renderField : function(f){
46524         f.fieldEl = this.fieldTpl.append(this.el, [
46525                f.id, f.fieldLabel,
46526                f.labelStyle||this.labelStyle||'',
46527                this.elementStyle||'',
46528                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46529                f.itemCls||this.itemCls||'',
46530                f.width ? f.width + this.padWidth : 160 + this.padWidth
46531        ],true);
46532     }
46533 });
46534  
46535
46536 /**
46537  * @class Roo.form.FieldSet
46538  * @extends Roo.form.Layout
46539  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46540  * @constructor
46541  * @param {Object} config Configuration options
46542  */
46543 Roo.form.FieldSet = function(config){
46544     Roo.form.FieldSet.superclass.constructor.call(this, config);
46545 };
46546
46547 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46548     /**
46549      * @cfg {String} legend
46550      * The text to display as the legend for the FieldSet (defaults to '')
46551      */
46552     /**
46553      * @cfg {String/Object} autoCreate
46554      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46555      */
46556
46557     // private
46558     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46559
46560     // private
46561     onRender : function(ct, position){
46562         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46563         if(this.legend){
46564             this.setLegend(this.legend);
46565         }
46566     },
46567
46568     // private
46569     setLegend : function(text){
46570         if(this.rendered){
46571             this.el.child('legend').update(text);
46572         }
46573     }
46574 });/*
46575  * Based on:
46576  * Ext JS Library 1.1.1
46577  * Copyright(c) 2006-2007, Ext JS, LLC.
46578  *
46579  * Originally Released Under LGPL - original licence link has changed is not relivant.
46580  *
46581  * Fork - LGPL
46582  * <script type="text/javascript">
46583  */
46584 /**
46585  * @class Roo.form.VTypes
46586  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46587  * @singleton
46588  */
46589 Roo.form.VTypes = function(){
46590     // closure these in so they are only created once.
46591     var alpha = /^[a-zA-Z_]+$/;
46592     var alphanum = /^[a-zA-Z0-9_]+$/;
46593     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46594     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46595
46596     // All these messages and functions are configurable
46597     return {
46598         /**
46599          * The function used to validate email addresses
46600          * @param {String} value The email address
46601          */
46602         'email' : function(v){
46603             return email.test(v);
46604         },
46605         /**
46606          * The error text to display when the email validation function returns false
46607          * @type String
46608          */
46609         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46610         /**
46611          * The keystroke filter mask to be applied on email input
46612          * @type RegExp
46613          */
46614         'emailMask' : /[a-z0-9_\.\-@]/i,
46615
46616         /**
46617          * The function used to validate URLs
46618          * @param {String} value The URL
46619          */
46620         'url' : function(v){
46621             return url.test(v);
46622         },
46623         /**
46624          * The error text to display when the url validation function returns false
46625          * @type String
46626          */
46627         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46628         
46629         /**
46630          * The function used to validate alpha values
46631          * @param {String} value The value
46632          */
46633         'alpha' : function(v){
46634             return alpha.test(v);
46635         },
46636         /**
46637          * The error text to display when the alpha validation function returns false
46638          * @type String
46639          */
46640         'alphaText' : 'This field should only contain letters and _',
46641         /**
46642          * The keystroke filter mask to be applied on alpha input
46643          * @type RegExp
46644          */
46645         'alphaMask' : /[a-z_]/i,
46646
46647         /**
46648          * The function used to validate alphanumeric values
46649          * @param {String} value The value
46650          */
46651         'alphanum' : function(v){
46652             return alphanum.test(v);
46653         },
46654         /**
46655          * The error text to display when the alphanumeric validation function returns false
46656          * @type String
46657          */
46658         'alphanumText' : 'This field should only contain letters, numbers and _',
46659         /**
46660          * The keystroke filter mask to be applied on alphanumeric input
46661          * @type RegExp
46662          */
46663         'alphanumMask' : /[a-z0-9_]/i
46664     };
46665 }();//<script type="text/javascript">
46666
46667 /**
46668  * @class Roo.form.FCKeditor
46669  * @extends Roo.form.TextArea
46670  * Wrapper around the FCKEditor http://www.fckeditor.net
46671  * @constructor
46672  * Creates a new FCKeditor
46673  * @param {Object} config Configuration options
46674  */
46675 Roo.form.FCKeditor = function(config){
46676     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46677     this.addEvents({
46678          /**
46679          * @event editorinit
46680          * Fired when the editor is initialized - you can add extra handlers here..
46681          * @param {FCKeditor} this
46682          * @param {Object} the FCK object.
46683          */
46684         editorinit : true
46685     });
46686     
46687     
46688 };
46689 Roo.form.FCKeditor.editors = { };
46690 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46691 {
46692     //defaultAutoCreate : {
46693     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46694     //},
46695     // private
46696     /**
46697      * @cfg {Object} fck options - see fck manual for details.
46698      */
46699     fckconfig : false,
46700     
46701     /**
46702      * @cfg {Object} fck toolbar set (Basic or Default)
46703      */
46704     toolbarSet : 'Basic',
46705     /**
46706      * @cfg {Object} fck BasePath
46707      */ 
46708     basePath : '/fckeditor/',
46709     
46710     
46711     frame : false,
46712     
46713     value : '',
46714     
46715    
46716     onRender : function(ct, position)
46717     {
46718         if(!this.el){
46719             this.defaultAutoCreate = {
46720                 tag: "textarea",
46721                 style:"width:300px;height:60px;",
46722                 autocomplete: "new-password"
46723             };
46724         }
46725         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46726         /*
46727         if(this.grow){
46728             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46729             if(this.preventScrollbars){
46730                 this.el.setStyle("overflow", "hidden");
46731             }
46732             this.el.setHeight(this.growMin);
46733         }
46734         */
46735         //console.log('onrender' + this.getId() );
46736         Roo.form.FCKeditor.editors[this.getId()] = this;
46737          
46738
46739         this.replaceTextarea() ;
46740         
46741     },
46742     
46743     getEditor : function() {
46744         return this.fckEditor;
46745     },
46746     /**
46747      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46748      * @param {Mixed} value The value to set
46749      */
46750     
46751     
46752     setValue : function(value)
46753     {
46754         //console.log('setValue: ' + value);
46755         
46756         if(typeof(value) == 'undefined') { // not sure why this is happending...
46757             return;
46758         }
46759         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46760         
46761         //if(!this.el || !this.getEditor()) {
46762         //    this.value = value;
46763             //this.setValue.defer(100,this,[value]);    
46764         //    return;
46765         //} 
46766         
46767         if(!this.getEditor()) {
46768             return;
46769         }
46770         
46771         this.getEditor().SetData(value);
46772         
46773         //
46774
46775     },
46776
46777     /**
46778      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46779      * @return {Mixed} value The field value
46780      */
46781     getValue : function()
46782     {
46783         
46784         if (this.frame && this.frame.dom.style.display == 'none') {
46785             return Roo.form.FCKeditor.superclass.getValue.call(this);
46786         }
46787         
46788         if(!this.el || !this.getEditor()) {
46789            
46790            // this.getValue.defer(100,this); 
46791             return this.value;
46792         }
46793        
46794         
46795         var value=this.getEditor().GetData();
46796         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46797         return Roo.form.FCKeditor.superclass.getValue.call(this);
46798         
46799
46800     },
46801
46802     /**
46803      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46804      * @return {Mixed} value The field value
46805      */
46806     getRawValue : function()
46807     {
46808         if (this.frame && this.frame.dom.style.display == 'none') {
46809             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46810         }
46811         
46812         if(!this.el || !this.getEditor()) {
46813             //this.getRawValue.defer(100,this); 
46814             return this.value;
46815             return;
46816         }
46817         
46818         
46819         
46820         var value=this.getEditor().GetData();
46821         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46822         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46823          
46824     },
46825     
46826     setSize : function(w,h) {
46827         
46828         
46829         
46830         //if (this.frame && this.frame.dom.style.display == 'none') {
46831         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46832         //    return;
46833         //}
46834         //if(!this.el || !this.getEditor()) {
46835         //    this.setSize.defer(100,this, [w,h]); 
46836         //    return;
46837         //}
46838         
46839         
46840         
46841         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46842         
46843         this.frame.dom.setAttribute('width', w);
46844         this.frame.dom.setAttribute('height', h);
46845         this.frame.setSize(w,h);
46846         
46847     },
46848     
46849     toggleSourceEdit : function(value) {
46850         
46851       
46852          
46853         this.el.dom.style.display = value ? '' : 'none';
46854         this.frame.dom.style.display = value ?  'none' : '';
46855         
46856     },
46857     
46858     
46859     focus: function(tag)
46860     {
46861         if (this.frame.dom.style.display == 'none') {
46862             return Roo.form.FCKeditor.superclass.focus.call(this);
46863         }
46864         if(!this.el || !this.getEditor()) {
46865             this.focus.defer(100,this, [tag]); 
46866             return;
46867         }
46868         
46869         
46870         
46871         
46872         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46873         this.getEditor().Focus();
46874         if (tgs.length) {
46875             if (!this.getEditor().Selection.GetSelection()) {
46876                 this.focus.defer(100,this, [tag]); 
46877                 return;
46878             }
46879             
46880             
46881             var r = this.getEditor().EditorDocument.createRange();
46882             r.setStart(tgs[0],0);
46883             r.setEnd(tgs[0],0);
46884             this.getEditor().Selection.GetSelection().removeAllRanges();
46885             this.getEditor().Selection.GetSelection().addRange(r);
46886             this.getEditor().Focus();
46887         }
46888         
46889     },
46890     
46891     
46892     
46893     replaceTextarea : function()
46894     {
46895         if ( document.getElementById( this.getId() + '___Frame' ) )
46896             return ;
46897         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46898         //{
46899             // We must check the elements firstly using the Id and then the name.
46900         var oTextarea = document.getElementById( this.getId() );
46901         
46902         var colElementsByName = document.getElementsByName( this.getId() ) ;
46903          
46904         oTextarea.style.display = 'none' ;
46905
46906         if ( oTextarea.tabIndex ) {            
46907             this.TabIndex = oTextarea.tabIndex ;
46908         }
46909         
46910         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46911         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46912         this.frame = Roo.get(this.getId() + '___Frame')
46913     },
46914     
46915     _getConfigHtml : function()
46916     {
46917         var sConfig = '' ;
46918
46919         for ( var o in this.fckconfig ) {
46920             sConfig += sConfig.length > 0  ? '&amp;' : '';
46921             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46922         }
46923
46924         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46925     },
46926     
46927     
46928     _getIFrameHtml : function()
46929     {
46930         var sFile = 'fckeditor.html' ;
46931         /* no idea what this is about..
46932         try
46933         {
46934             if ( (/fcksource=true/i).test( window.top.location.search ) )
46935                 sFile = 'fckeditor.original.html' ;
46936         }
46937         catch (e) { 
46938         */
46939
46940         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46941         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46942         
46943         
46944         var html = '<iframe id="' + this.getId() +
46945             '___Frame" src="' + sLink +
46946             '" width="' + this.width +
46947             '" height="' + this.height + '"' +
46948             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46949             ' frameborder="0" scrolling="no"></iframe>' ;
46950
46951         return html ;
46952     },
46953     
46954     _insertHtmlBefore : function( html, element )
46955     {
46956         if ( element.insertAdjacentHTML )       {
46957             // IE
46958             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46959         } else { // Gecko
46960             var oRange = document.createRange() ;
46961             oRange.setStartBefore( element ) ;
46962             var oFragment = oRange.createContextualFragment( html );
46963             element.parentNode.insertBefore( oFragment, element ) ;
46964         }
46965     }
46966     
46967     
46968   
46969     
46970     
46971     
46972     
46973
46974 });
46975
46976 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46977
46978 function FCKeditor_OnComplete(editorInstance){
46979     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46980     f.fckEditor = editorInstance;
46981     //console.log("loaded");
46982     f.fireEvent('editorinit', f, editorInstance);
46983
46984   
46985
46986  
46987
46988
46989
46990
46991
46992
46993
46994
46995
46996
46997
46998
46999
47000
47001
47002 //<script type="text/javascript">
47003 /**
47004  * @class Roo.form.GridField
47005  * @extends Roo.form.Field
47006  * Embed a grid (or editable grid into a form)
47007  * STATUS ALPHA
47008  * 
47009  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47010  * it needs 
47011  * xgrid.store = Roo.data.Store
47012  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47013  * xgrid.store.reader = Roo.data.JsonReader 
47014  * 
47015  * 
47016  * @constructor
47017  * Creates a new GridField
47018  * @param {Object} config Configuration options
47019  */
47020 Roo.form.GridField = function(config){
47021     Roo.form.GridField.superclass.constructor.call(this, config);
47022      
47023 };
47024
47025 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47026     /**
47027      * @cfg {Number} width  - used to restrict width of grid..
47028      */
47029     width : 100,
47030     /**
47031      * @cfg {Number} height - used to restrict height of grid..
47032      */
47033     height : 50,
47034      /**
47035      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47036          * 
47037          *}
47038      */
47039     xgrid : false, 
47040     /**
47041      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47042      * {tag: "input", type: "checkbox", autocomplete: "off"})
47043      */
47044    // defaultAutoCreate : { tag: 'div' },
47045     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47046     /**
47047      * @cfg {String} addTitle Text to include for adding a title.
47048      */
47049     addTitle : false,
47050     //
47051     onResize : function(){
47052         Roo.form.Field.superclass.onResize.apply(this, arguments);
47053     },
47054
47055     initEvents : function(){
47056         // Roo.form.Checkbox.superclass.initEvents.call(this);
47057         // has no events...
47058        
47059     },
47060
47061
47062     getResizeEl : function(){
47063         return this.wrap;
47064     },
47065
47066     getPositionEl : function(){
47067         return this.wrap;
47068     },
47069
47070     // private
47071     onRender : function(ct, position){
47072         
47073         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47074         var style = this.style;
47075         delete this.style;
47076         
47077         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47078         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47079         this.viewEl = this.wrap.createChild({ tag: 'div' });
47080         if (style) {
47081             this.viewEl.applyStyles(style);
47082         }
47083         if (this.width) {
47084             this.viewEl.setWidth(this.width);
47085         }
47086         if (this.height) {
47087             this.viewEl.setHeight(this.height);
47088         }
47089         //if(this.inputValue !== undefined){
47090         //this.setValue(this.value);
47091         
47092         
47093         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47094         
47095         
47096         this.grid.render();
47097         this.grid.getDataSource().on('remove', this.refreshValue, this);
47098         this.grid.getDataSource().on('update', this.refreshValue, this);
47099         this.grid.on('afteredit', this.refreshValue, this);
47100  
47101     },
47102      
47103     
47104     /**
47105      * Sets the value of the item. 
47106      * @param {String} either an object  or a string..
47107      */
47108     setValue : function(v){
47109         //this.value = v;
47110         v = v || []; // empty set..
47111         // this does not seem smart - it really only affects memoryproxy grids..
47112         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47113             var ds = this.grid.getDataSource();
47114             // assumes a json reader..
47115             var data = {}
47116             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47117             ds.loadData( data);
47118         }
47119         // clear selection so it does not get stale.
47120         if (this.grid.sm) { 
47121             this.grid.sm.clearSelections();
47122         }
47123         
47124         Roo.form.GridField.superclass.setValue.call(this, v);
47125         this.refreshValue();
47126         // should load data in the grid really....
47127     },
47128     
47129     // private
47130     refreshValue: function() {
47131          var val = [];
47132         this.grid.getDataSource().each(function(r) {
47133             val.push(r.data);
47134         });
47135         this.el.dom.value = Roo.encode(val);
47136     }
47137     
47138      
47139     
47140     
47141 });/*
47142  * Based on:
47143  * Ext JS Library 1.1.1
47144  * Copyright(c) 2006-2007, Ext JS, LLC.
47145  *
47146  * Originally Released Under LGPL - original licence link has changed is not relivant.
47147  *
47148  * Fork - LGPL
47149  * <script type="text/javascript">
47150  */
47151 /**
47152  * @class Roo.form.DisplayField
47153  * @extends Roo.form.Field
47154  * A generic Field to display non-editable data.
47155  * @constructor
47156  * Creates a new Display Field item.
47157  * @param {Object} config Configuration options
47158  */
47159 Roo.form.DisplayField = function(config){
47160     Roo.form.DisplayField.superclass.constructor.call(this, config);
47161     
47162 };
47163
47164 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47165     inputType:      'hidden',
47166     allowBlank:     true,
47167     readOnly:         true,
47168     
47169  
47170     /**
47171      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47172      */
47173     focusClass : undefined,
47174     /**
47175      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47176      */
47177     fieldClass: 'x-form-field',
47178     
47179      /**
47180      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47181      */
47182     valueRenderer: undefined,
47183     
47184     width: 100,
47185     /**
47186      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47187      * {tag: "input", type: "checkbox", autocomplete: "off"})
47188      */
47189      
47190  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47191
47192     onResize : function(){
47193         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47194         
47195     },
47196
47197     initEvents : function(){
47198         // Roo.form.Checkbox.superclass.initEvents.call(this);
47199         // has no events...
47200        
47201     },
47202
47203
47204     getResizeEl : function(){
47205         return this.wrap;
47206     },
47207
47208     getPositionEl : function(){
47209         return this.wrap;
47210     },
47211
47212     // private
47213     onRender : function(ct, position){
47214         
47215         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47216         //if(this.inputValue !== undefined){
47217         this.wrap = this.el.wrap();
47218         
47219         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47220         
47221         if (this.bodyStyle) {
47222             this.viewEl.applyStyles(this.bodyStyle);
47223         }
47224         //this.viewEl.setStyle('padding', '2px');
47225         
47226         this.setValue(this.value);
47227         
47228     },
47229 /*
47230     // private
47231     initValue : Roo.emptyFn,
47232
47233   */
47234
47235         // private
47236     onClick : function(){
47237         
47238     },
47239
47240     /**
47241      * Sets the checked state of the checkbox.
47242      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47243      */
47244     setValue : function(v){
47245         this.value = v;
47246         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47247         // this might be called before we have a dom element..
47248         if (!this.viewEl) {
47249             return;
47250         }
47251         this.viewEl.dom.innerHTML = html;
47252         Roo.form.DisplayField.superclass.setValue.call(this, v);
47253
47254     }
47255 });/*
47256  * 
47257  * Licence- LGPL
47258  * 
47259  */
47260
47261 /**
47262  * @class Roo.form.DayPicker
47263  * @extends Roo.form.Field
47264  * A Day picker show [M] [T] [W] ....
47265  * @constructor
47266  * Creates a new Day Picker
47267  * @param {Object} config Configuration options
47268  */
47269 Roo.form.DayPicker= function(config){
47270     Roo.form.DayPicker.superclass.constructor.call(this, config);
47271      
47272 };
47273
47274 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47275     /**
47276      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47277      */
47278     focusClass : undefined,
47279     /**
47280      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47281      */
47282     fieldClass: "x-form-field",
47283    
47284     /**
47285      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47286      * {tag: "input", type: "checkbox", autocomplete: "off"})
47287      */
47288     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47289     
47290    
47291     actionMode : 'viewEl', 
47292     //
47293     // private
47294  
47295     inputType : 'hidden',
47296     
47297      
47298     inputElement: false, // real input element?
47299     basedOn: false, // ????
47300     
47301     isFormField: true, // not sure where this is needed!!!!
47302
47303     onResize : function(){
47304         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47305         if(!this.boxLabel){
47306             this.el.alignTo(this.wrap, 'c-c');
47307         }
47308     },
47309
47310     initEvents : function(){
47311         Roo.form.Checkbox.superclass.initEvents.call(this);
47312         this.el.on("click", this.onClick,  this);
47313         this.el.on("change", this.onClick,  this);
47314     },
47315
47316
47317     getResizeEl : function(){
47318         return this.wrap;
47319     },
47320
47321     getPositionEl : function(){
47322         return this.wrap;
47323     },
47324
47325     
47326     // private
47327     onRender : function(ct, position){
47328         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47329        
47330         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47331         
47332         var r1 = '<table><tr>';
47333         var r2 = '<tr class="x-form-daypick-icons">';
47334         for (var i=0; i < 7; i++) {
47335             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47336             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47337         }
47338         
47339         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47340         viewEl.select('img').on('click', this.onClick, this);
47341         this.viewEl = viewEl;   
47342         
47343         
47344         // this will not work on Chrome!!!
47345         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47346         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47347         
47348         
47349           
47350
47351     },
47352
47353     // private
47354     initValue : Roo.emptyFn,
47355
47356     /**
47357      * Returns the checked state of the checkbox.
47358      * @return {Boolean} True if checked, else false
47359      */
47360     getValue : function(){
47361         return this.el.dom.value;
47362         
47363     },
47364
47365         // private
47366     onClick : function(e){ 
47367         //this.setChecked(!this.checked);
47368         Roo.get(e.target).toggleClass('x-menu-item-checked');
47369         this.refreshValue();
47370         //if(this.el.dom.checked != this.checked){
47371         //    this.setValue(this.el.dom.checked);
47372        // }
47373     },
47374     
47375     // private
47376     refreshValue : function()
47377     {
47378         var val = '';
47379         this.viewEl.select('img',true).each(function(e,i,n)  {
47380             val += e.is(".x-menu-item-checked") ? String(n) : '';
47381         });
47382         this.setValue(val, true);
47383     },
47384
47385     /**
47386      * Sets the checked state of the checkbox.
47387      * On is always based on a string comparison between inputValue and the param.
47388      * @param {Boolean/String} value - the value to set 
47389      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47390      */
47391     setValue : function(v,suppressEvent){
47392         if (!this.el.dom) {
47393             return;
47394         }
47395         var old = this.el.dom.value ;
47396         this.el.dom.value = v;
47397         if (suppressEvent) {
47398             return ;
47399         }
47400          
47401         // update display..
47402         this.viewEl.select('img',true).each(function(e,i,n)  {
47403             
47404             var on = e.is(".x-menu-item-checked");
47405             var newv = v.indexOf(String(n)) > -1;
47406             if (on != newv) {
47407                 e.toggleClass('x-menu-item-checked');
47408             }
47409             
47410         });
47411         
47412         
47413         this.fireEvent('change', this, v, old);
47414         
47415         
47416     },
47417    
47418     // handle setting of hidden value by some other method!!?!?
47419     setFromHidden: function()
47420     {
47421         if(!this.el){
47422             return;
47423         }
47424         //console.log("SET FROM HIDDEN");
47425         //alert('setFrom hidden');
47426         this.setValue(this.el.dom.value);
47427     },
47428     
47429     onDestroy : function()
47430     {
47431         if(this.viewEl){
47432             Roo.get(this.viewEl).remove();
47433         }
47434          
47435         Roo.form.DayPicker.superclass.onDestroy.call(this);
47436     }
47437
47438 });/*
47439  * RooJS Library 1.1.1
47440  * Copyright(c) 2008-2011  Alan Knowles
47441  *
47442  * License - LGPL
47443  */
47444  
47445
47446 /**
47447  * @class Roo.form.ComboCheck
47448  * @extends Roo.form.ComboBox
47449  * A combobox for multiple select items.
47450  *
47451  * FIXME - could do with a reset button..
47452  * 
47453  * @constructor
47454  * Create a new ComboCheck
47455  * @param {Object} config Configuration options
47456  */
47457 Roo.form.ComboCheck = function(config){
47458     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47459     // should verify some data...
47460     // like
47461     // hiddenName = required..
47462     // displayField = required
47463     // valudField == required
47464     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47465     var _t = this;
47466     Roo.each(req, function(e) {
47467         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47468             throw "Roo.form.ComboCheck : missing value for: " + e;
47469         }
47470     });
47471     
47472     
47473 };
47474
47475 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47476      
47477      
47478     editable : false,
47479      
47480     selectedClass: 'x-menu-item-checked', 
47481     
47482     // private
47483     onRender : function(ct, position){
47484         var _t = this;
47485         
47486         
47487         
47488         if(!this.tpl){
47489             var cls = 'x-combo-list';
47490
47491             
47492             this.tpl =  new Roo.Template({
47493                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47494                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47495                    '<span>{' + this.displayField + '}</span>' +
47496                     '</div>' 
47497                 
47498             });
47499         }
47500  
47501         
47502         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47503         this.view.singleSelect = false;
47504         this.view.multiSelect = true;
47505         this.view.toggleSelect = true;
47506         this.pageTb.add(new Roo.Toolbar.Fill(), {
47507             
47508             text: 'Done',
47509             handler: function()
47510             {
47511                 _t.collapse();
47512             }
47513         });
47514     },
47515     
47516     onViewOver : function(e, t){
47517         // do nothing...
47518         return;
47519         
47520     },
47521     
47522     onViewClick : function(doFocus,index){
47523         return;
47524         
47525     },
47526     select: function () {
47527         //Roo.log("SELECT CALLED");
47528     },
47529      
47530     selectByValue : function(xv, scrollIntoView){
47531         var ar = this.getValueArray();
47532         var sels = [];
47533         
47534         Roo.each(ar, function(v) {
47535             if(v === undefined || v === null){
47536                 return;
47537             }
47538             var r = this.findRecord(this.valueField, v);
47539             if(r){
47540                 sels.push(this.store.indexOf(r))
47541                 
47542             }
47543         },this);
47544         this.view.select(sels);
47545         return false;
47546     },
47547     
47548     
47549     
47550     onSelect : function(record, index){
47551        // Roo.log("onselect Called");
47552        // this is only called by the clear button now..
47553         this.view.clearSelections();
47554         this.setValue('[]');
47555         if (this.value != this.valueBefore) {
47556             this.fireEvent('change', this, this.value, this.valueBefore);
47557             this.valueBefore = this.value;
47558         }
47559     },
47560     getValueArray : function()
47561     {
47562         var ar = [] ;
47563         
47564         try {
47565             //Roo.log(this.value);
47566             if (typeof(this.value) == 'undefined') {
47567                 return [];
47568             }
47569             var ar = Roo.decode(this.value);
47570             return  ar instanceof Array ? ar : []; //?? valid?
47571             
47572         } catch(e) {
47573             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47574             return [];
47575         }
47576          
47577     },
47578     expand : function ()
47579     {
47580         
47581         Roo.form.ComboCheck.superclass.expand.call(this);
47582         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47583         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47584         
47585
47586     },
47587     
47588     collapse : function(){
47589         Roo.form.ComboCheck.superclass.collapse.call(this);
47590         var sl = this.view.getSelectedIndexes();
47591         var st = this.store;
47592         var nv = [];
47593         var tv = [];
47594         var r;
47595         Roo.each(sl, function(i) {
47596             r = st.getAt(i);
47597             nv.push(r.get(this.valueField));
47598         },this);
47599         this.setValue(Roo.encode(nv));
47600         if (this.value != this.valueBefore) {
47601
47602             this.fireEvent('change', this, this.value, this.valueBefore);
47603             this.valueBefore = this.value;
47604         }
47605         
47606     },
47607     
47608     setValue : function(v){
47609         // Roo.log(v);
47610         this.value = v;
47611         
47612         var vals = this.getValueArray();
47613         var tv = [];
47614         Roo.each(vals, function(k) {
47615             var r = this.findRecord(this.valueField, k);
47616             if(r){
47617                 tv.push(r.data[this.displayField]);
47618             }else if(this.valueNotFoundText !== undefined){
47619                 tv.push( this.valueNotFoundText );
47620             }
47621         },this);
47622        // Roo.log(tv);
47623         
47624         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47625         this.hiddenField.value = v;
47626         this.value = v;
47627     }
47628     
47629 });/*
47630  * Based on:
47631  * Ext JS Library 1.1.1
47632  * Copyright(c) 2006-2007, Ext JS, LLC.
47633  *
47634  * Originally Released Under LGPL - original licence link has changed is not relivant.
47635  *
47636  * Fork - LGPL
47637  * <script type="text/javascript">
47638  */
47639  
47640 /**
47641  * @class Roo.form.Signature
47642  * @extends Roo.form.Field
47643  * Signature field.  
47644  * @constructor
47645  * 
47646  * @param {Object} config Configuration options
47647  */
47648
47649 Roo.form.Signature = function(config){
47650     Roo.form.Signature.superclass.constructor.call(this, config);
47651     
47652     this.addEvents({// not in used??
47653          /**
47654          * @event confirm
47655          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47656              * @param {Roo.form.Signature} combo This combo box
47657              */
47658         'confirm' : true,
47659         /**
47660          * @event reset
47661          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47662              * @param {Roo.form.ComboBox} combo This combo box
47663              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47664              */
47665         'reset' : true
47666     });
47667 };
47668
47669 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47670     /**
47671      * @cfg {Object} labels Label to use when rendering a form.
47672      * defaults to 
47673      * labels : { 
47674      *      clear : "Clear",
47675      *      confirm : "Confirm"
47676      *  }
47677      */
47678     labels : { 
47679         clear : "Clear",
47680         confirm : "Confirm"
47681     },
47682     /**
47683      * @cfg {Number} width The signature panel width (defaults to 300)
47684      */
47685     width: 300,
47686     /**
47687      * @cfg {Number} height The signature panel height (defaults to 100)
47688      */
47689     height : 100,
47690     /**
47691      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47692      */
47693     allowBlank : false,
47694     
47695     //private
47696     // {Object} signPanel The signature SVG panel element (defaults to {})
47697     signPanel : {},
47698     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47699     isMouseDown : false,
47700     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47701     isConfirmed : false,
47702     // {String} signatureTmp SVG mapping string (defaults to empty string)
47703     signatureTmp : '',
47704     
47705     
47706     defaultAutoCreate : { // modified by initCompnoent..
47707         tag: "input",
47708         type:"hidden"
47709     },
47710
47711     // private
47712     onRender : function(ct, position){
47713         
47714         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47715         
47716         this.wrap = this.el.wrap({
47717             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47718         });
47719         
47720         this.createToolbar(this);
47721         this.signPanel = this.wrap.createChild({
47722                 tag: 'div',
47723                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47724             }, this.el
47725         );
47726             
47727         this.svgID = Roo.id();
47728         this.svgEl = this.signPanel.createChild({
47729               xmlns : 'http://www.w3.org/2000/svg',
47730               tag : 'svg',
47731               id : this.svgID + "-svg",
47732               width: this.width,
47733               height: this.height,
47734               viewBox: '0 0 '+this.width+' '+this.height,
47735               cn : [
47736                 {
47737                     tag: "rect",
47738                     id: this.svgID + "-svg-r",
47739                     width: this.width,
47740                     height: this.height,
47741                     fill: "#ffa"
47742                 },
47743                 {
47744                     tag: "line",
47745                     id: this.svgID + "-svg-l",
47746                     x1: "0", // start
47747                     y1: (this.height*0.8), // start set the line in 80% of height
47748                     x2: this.width, // end
47749                     y2: (this.height*0.8), // end set the line in 80% of height
47750                     'stroke': "#666",
47751                     'stroke-width': "1",
47752                     'stroke-dasharray': "3",
47753                     'shape-rendering': "crispEdges",
47754                     'pointer-events': "none"
47755                 },
47756                 {
47757                     tag: "path",
47758                     id: this.svgID + "-svg-p",
47759                     'stroke': "navy",
47760                     'stroke-width': "3",
47761                     'fill': "none",
47762                     'pointer-events': 'none'
47763                 }
47764               ]
47765         });
47766         this.createSVG();
47767         this.svgBox = this.svgEl.dom.getScreenCTM();
47768     },
47769     createSVG : function(){ 
47770         var svg = this.signPanel;
47771         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47772         var t = this;
47773
47774         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47775         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47776         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47777         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47778         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47779         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47780         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47781         
47782     },
47783     isTouchEvent : function(e){
47784         return e.type.match(/^touch/);
47785     },
47786     getCoords : function (e) {
47787         var pt    = this.svgEl.dom.createSVGPoint();
47788         pt.x = e.clientX; 
47789         pt.y = e.clientY;
47790         if (this.isTouchEvent(e)) {
47791             pt.x =  e.targetTouches[0].clientX 
47792             pt.y = e.targetTouches[0].clientY;
47793         }
47794         var a = this.svgEl.dom.getScreenCTM();
47795         var b = a.inverse();
47796         var mx = pt.matrixTransform(b);
47797         return mx.x + ',' + mx.y;
47798     },
47799     //mouse event headler 
47800     down : function (e) {
47801         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47802         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47803         
47804         this.isMouseDown = true;
47805         
47806         e.preventDefault();
47807     },
47808     move : function (e) {
47809         if (this.isMouseDown) {
47810             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47811             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47812         }
47813         
47814         e.preventDefault();
47815     },
47816     up : function (e) {
47817         this.isMouseDown = false;
47818         var sp = this.signatureTmp.split(' ');
47819         
47820         if(sp.length > 1){
47821             if(!sp[sp.length-2].match(/^L/)){
47822                 sp.pop();
47823                 sp.pop();
47824                 sp.push("");
47825                 this.signatureTmp = sp.join(" ");
47826             }
47827         }
47828         if(this.getValue() != this.signatureTmp){
47829             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47830             this.isConfirmed = false;
47831         }
47832         e.preventDefault();
47833     },
47834     
47835     /**
47836      * Protected method that will not generally be called directly. It
47837      * is called when the editor creates its toolbar. Override this method if you need to
47838      * add custom toolbar buttons.
47839      * @param {HtmlEditor} editor
47840      */
47841     createToolbar : function(editor){
47842          function btn(id, toggle, handler){
47843             var xid = fid + '-'+ id ;
47844             return {
47845                 id : xid,
47846                 cmd : id,
47847                 cls : 'x-btn-icon x-edit-'+id,
47848                 enableToggle:toggle !== false,
47849                 scope: editor, // was editor...
47850                 handler:handler||editor.relayBtnCmd,
47851                 clickEvent:'mousedown',
47852                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47853                 tabIndex:-1
47854             };
47855         }
47856         
47857         
47858         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47859         this.tb = tb;
47860         this.tb.add(
47861            {
47862                 cls : ' x-signature-btn x-signature-'+id,
47863                 scope: editor, // was editor...
47864                 handler: this.reset,
47865                 clickEvent:'mousedown',
47866                 text: this.labels.clear
47867             },
47868             {
47869                  xtype : 'Fill',
47870                  xns: Roo.Toolbar
47871             }, 
47872             {
47873                 cls : '  x-signature-btn x-signature-'+id,
47874                 scope: editor, // was editor...
47875                 handler: this.confirmHandler,
47876                 clickEvent:'mousedown',
47877                 text: this.labels.confirm
47878             }
47879         );
47880     
47881     },
47882     //public
47883     /**
47884      * when user is clicked confirm then show this image.....
47885      * 
47886      * @return {String} Image Data URI
47887      */
47888     getImageDataURI : function(){
47889         var svg = this.svgEl.dom.parentNode.innerHTML;
47890         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47891         return src; 
47892     },
47893     /**
47894      * 
47895      * @return {Boolean} this.isConfirmed
47896      */
47897     getConfirmed : function(){
47898         return this.isConfirmed;
47899     },
47900     /**
47901      * 
47902      * @return {Number} this.width
47903      */
47904     getWidth : function(){
47905         return this.width;
47906     },
47907     /**
47908      * 
47909      * @return {Number} this.height
47910      */
47911     getHeight : function(){
47912         return this.height;
47913     },
47914     // private
47915     getSignature : function(){
47916         return this.signatureTmp;
47917     },
47918     // private
47919     reset : function(){
47920         this.signatureTmp = '';
47921         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47922         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47923         this.isConfirmed = false;
47924         Roo.form.Signature.superclass.reset.call(this);
47925     },
47926     setSignature : function(s){
47927         this.signatureTmp = s;
47928         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47929         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47930         this.setValue(s);
47931         this.isConfirmed = false;
47932         Roo.form.Signature.superclass.reset.call(this);
47933     }, 
47934     test : function(){
47935 //        Roo.log(this.signPanel.dom.contentWindow.up())
47936     },
47937     //private
47938     setConfirmed : function(){
47939         
47940         
47941         
47942 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47943     },
47944     // private
47945     confirmHandler : function(){
47946         if(!this.getSignature()){
47947             return;
47948         }
47949         
47950         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47951         this.setValue(this.getSignature());
47952         this.isConfirmed = true;
47953         
47954         this.fireEvent('confirm', this);
47955     },
47956     // private
47957     // Subclasses should provide the validation implementation by overriding this
47958     validateValue : function(value){
47959         if(this.allowBlank){
47960             return true;
47961         }
47962         
47963         if(this.isConfirmed){
47964             return true;
47965         }
47966         return false;
47967     }
47968 });/*
47969  * Based on:
47970  * Ext JS Library 1.1.1
47971  * Copyright(c) 2006-2007, Ext JS, LLC.
47972  *
47973  * Originally Released Under LGPL - original licence link has changed is not relivant.
47974  *
47975  * Fork - LGPL
47976  * <script type="text/javascript">
47977  */
47978  
47979
47980 /**
47981  * @class Roo.form.ComboBox
47982  * @extends Roo.form.TriggerField
47983  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47984  * @constructor
47985  * Create a new ComboBox.
47986  * @param {Object} config Configuration options
47987  */
47988 Roo.form.Select = function(config){
47989     Roo.form.Select.superclass.constructor.call(this, config);
47990      
47991 };
47992
47993 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47994     /**
47995      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47996      */
47997     /**
47998      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47999      * rendering into an Roo.Editor, defaults to false)
48000      */
48001     /**
48002      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48003      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48004      */
48005     /**
48006      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48007      */
48008     /**
48009      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48010      * the dropdown list (defaults to undefined, with no header element)
48011      */
48012
48013      /**
48014      * @cfg {String/Roo.Template} tpl The template to use to render the output
48015      */
48016      
48017     // private
48018     defaultAutoCreate : {tag: "select"  },
48019     /**
48020      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48021      */
48022     listWidth: undefined,
48023     /**
48024      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48025      * mode = 'remote' or 'text' if mode = 'local')
48026      */
48027     displayField: undefined,
48028     /**
48029      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48030      * mode = 'remote' or 'value' if mode = 'local'). 
48031      * Note: use of a valueField requires the user make a selection
48032      * in order for a value to be mapped.
48033      */
48034     valueField: undefined,
48035     
48036     
48037     /**
48038      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48039      * field's data value (defaults to the underlying DOM element's name)
48040      */
48041     hiddenName: undefined,
48042     /**
48043      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48044      */
48045     listClass: '',
48046     /**
48047      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48048      */
48049     selectedClass: 'x-combo-selected',
48050     /**
48051      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48052      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48053      * which displays a downward arrow icon).
48054      */
48055     triggerClass : 'x-form-arrow-trigger',
48056     /**
48057      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48058      */
48059     shadow:'sides',
48060     /**
48061      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48062      * anchor positions (defaults to 'tl-bl')
48063      */
48064     listAlign: 'tl-bl?',
48065     /**
48066      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48067      */
48068     maxHeight: 300,
48069     /**
48070      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48071      * query specified by the allQuery config option (defaults to 'query')
48072      */
48073     triggerAction: 'query',
48074     /**
48075      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48076      * (defaults to 4, does not apply if editable = false)
48077      */
48078     minChars : 4,
48079     /**
48080      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48081      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48082      */
48083     typeAhead: false,
48084     /**
48085      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48086      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48087      */
48088     queryDelay: 500,
48089     /**
48090      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48091      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48092      */
48093     pageSize: 0,
48094     /**
48095      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48096      * when editable = true (defaults to false)
48097      */
48098     selectOnFocus:false,
48099     /**
48100      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48101      */
48102     queryParam: 'query',
48103     /**
48104      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48105      * when mode = 'remote' (defaults to 'Loading...')
48106      */
48107     loadingText: 'Loading...',
48108     /**
48109      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48110      */
48111     resizable: false,
48112     /**
48113      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48114      */
48115     handleHeight : 8,
48116     /**
48117      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48118      * traditional select (defaults to true)
48119      */
48120     editable: true,
48121     /**
48122      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48123      */
48124     allQuery: '',
48125     /**
48126      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48127      */
48128     mode: 'remote',
48129     /**
48130      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48131      * listWidth has a higher value)
48132      */
48133     minListWidth : 70,
48134     /**
48135      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48136      * allow the user to set arbitrary text into the field (defaults to false)
48137      */
48138     forceSelection:false,
48139     /**
48140      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48141      * if typeAhead = true (defaults to 250)
48142      */
48143     typeAheadDelay : 250,
48144     /**
48145      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48146      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48147      */
48148     valueNotFoundText : undefined,
48149     
48150     /**
48151      * @cfg {String} defaultValue The value displayed after loading the store.
48152      */
48153     defaultValue: '',
48154     
48155     /**
48156      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48157      */
48158     blockFocus : false,
48159     
48160     /**
48161      * @cfg {Boolean} disableClear Disable showing of clear button.
48162      */
48163     disableClear : false,
48164     /**
48165      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48166      */
48167     alwaysQuery : false,
48168     
48169     //private
48170     addicon : false,
48171     editicon: false,
48172     
48173     // element that contains real text value.. (when hidden is used..)
48174      
48175     // private
48176     onRender : function(ct, position){
48177         Roo.form.Field.prototype.onRender.call(this, ct, position);
48178         
48179         if(this.store){
48180             this.store.on('beforeload', this.onBeforeLoad, this);
48181             this.store.on('load', this.onLoad, this);
48182             this.store.on('loadexception', this.onLoadException, this);
48183             this.store.load({});
48184         }
48185         
48186         
48187         
48188     },
48189
48190     // private
48191     initEvents : function(){
48192         //Roo.form.ComboBox.superclass.initEvents.call(this);
48193  
48194     },
48195
48196     onDestroy : function(){
48197        
48198         if(this.store){
48199             this.store.un('beforeload', this.onBeforeLoad, this);
48200             this.store.un('load', this.onLoad, this);
48201             this.store.un('loadexception', this.onLoadException, this);
48202         }
48203         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48204     },
48205
48206     // private
48207     fireKey : function(e){
48208         if(e.isNavKeyPress() && !this.list.isVisible()){
48209             this.fireEvent("specialkey", this, e);
48210         }
48211     },
48212
48213     // private
48214     onResize: function(w, h){
48215         
48216         return; 
48217     
48218         
48219     },
48220
48221     /**
48222      * Allow or prevent the user from directly editing the field text.  If false is passed,
48223      * the user will only be able to select from the items defined in the dropdown list.  This method
48224      * is the runtime equivalent of setting the 'editable' config option at config time.
48225      * @param {Boolean} value True to allow the user to directly edit the field text
48226      */
48227     setEditable : function(value){
48228          
48229     },
48230
48231     // private
48232     onBeforeLoad : function(){
48233         
48234         Roo.log("Select before load");
48235         return;
48236     
48237         this.innerList.update(this.loadingText ?
48238                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48239         //this.restrictHeight();
48240         this.selectedIndex = -1;
48241     },
48242
48243     // private
48244     onLoad : function(){
48245
48246     
48247         var dom = this.el.dom;
48248         dom.innerHTML = '';
48249          var od = dom.ownerDocument;
48250          
48251         if (this.emptyText) {
48252             var op = od.createElement('option');
48253             op.setAttribute('value', '');
48254             op.innerHTML = String.format('{0}', this.emptyText);
48255             dom.appendChild(op);
48256         }
48257         if(this.store.getCount() > 0){
48258            
48259             var vf = this.valueField;
48260             var df = this.displayField;
48261             this.store.data.each(function(r) {
48262                 // which colmsn to use... testing - cdoe / title..
48263                 var op = od.createElement('option');
48264                 op.setAttribute('value', r.data[vf]);
48265                 op.innerHTML = String.format('{0}', r.data[df]);
48266                 dom.appendChild(op);
48267             });
48268             if (typeof(this.defaultValue != 'undefined')) {
48269                 this.setValue(this.defaultValue);
48270             }
48271             
48272              
48273         }else{
48274             //this.onEmptyResults();
48275         }
48276         //this.el.focus();
48277     },
48278     // private
48279     onLoadException : function()
48280     {
48281         dom.innerHTML = '';
48282             
48283         Roo.log("Select on load exception");
48284         return;
48285     
48286         this.collapse();
48287         Roo.log(this.store.reader.jsonData);
48288         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48289             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48290         }
48291         
48292         
48293     },
48294     // private
48295     onTypeAhead : function(){
48296          
48297     },
48298
48299     // private
48300     onSelect : function(record, index){
48301         Roo.log('on select?');
48302         return;
48303         if(this.fireEvent('beforeselect', this, record, index) !== false){
48304             this.setFromData(index > -1 ? record.data : false);
48305             this.collapse();
48306             this.fireEvent('select', this, record, index);
48307         }
48308     },
48309
48310     /**
48311      * Returns the currently selected field value or empty string if no value is set.
48312      * @return {String} value The selected value
48313      */
48314     getValue : function(){
48315         var dom = this.el.dom;
48316         this.value = dom.options[dom.selectedIndex].value;
48317         return this.value;
48318         
48319     },
48320
48321     /**
48322      * Clears any text/value currently set in the field
48323      */
48324     clearValue : function(){
48325         this.value = '';
48326         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48327         
48328     },
48329
48330     /**
48331      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48332      * will be displayed in the field.  If the value does not match the data value of an existing item,
48333      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48334      * Otherwise the field will be blank (although the value will still be set).
48335      * @param {String} value The value to match
48336      */
48337     setValue : function(v){
48338         var d = this.el.dom;
48339         for (var i =0; i < d.options.length;i++) {
48340             if (v == d.options[i].value) {
48341                 d.selectedIndex = i;
48342                 this.value = v;
48343                 return;
48344             }
48345         }
48346         this.clearValue();
48347     },
48348     /**
48349      * @property {Object} the last set data for the element
48350      */
48351     
48352     lastData : false,
48353     /**
48354      * Sets the value of the field based on a object which is related to the record format for the store.
48355      * @param {Object} value the value to set as. or false on reset?
48356      */
48357     setFromData : function(o){
48358         Roo.log('setfrom data?');
48359          
48360         
48361         
48362     },
48363     // private
48364     reset : function(){
48365         this.clearValue();
48366     },
48367     // private
48368     findRecord : function(prop, value){
48369         
48370         return false;
48371     
48372         var record;
48373         if(this.store.getCount() > 0){
48374             this.store.each(function(r){
48375                 if(r.data[prop] == value){
48376                     record = r;
48377                     return false;
48378                 }
48379                 return true;
48380             });
48381         }
48382         return record;
48383     },
48384     
48385     getName: function()
48386     {
48387         // returns hidden if it's set..
48388         if (!this.rendered) {return ''};
48389         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48390         
48391     },
48392      
48393
48394     
48395
48396     // private
48397     onEmptyResults : function(){
48398         Roo.log('empty results');
48399         //this.collapse();
48400     },
48401
48402     /**
48403      * Returns true if the dropdown list is expanded, else false.
48404      */
48405     isExpanded : function(){
48406         return false;
48407     },
48408
48409     /**
48410      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48411      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48412      * @param {String} value The data value of the item to select
48413      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48414      * selected item if it is not currently in view (defaults to true)
48415      * @return {Boolean} True if the value matched an item in the list, else false
48416      */
48417     selectByValue : function(v, scrollIntoView){
48418         Roo.log('select By Value');
48419         return false;
48420     
48421         if(v !== undefined && v !== null){
48422             var r = this.findRecord(this.valueField || this.displayField, v);
48423             if(r){
48424                 this.select(this.store.indexOf(r), scrollIntoView);
48425                 return true;
48426             }
48427         }
48428         return false;
48429     },
48430
48431     /**
48432      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48434      * @param {Number} index The zero-based index of the list item to select
48435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48436      * selected item if it is not currently in view (defaults to true)
48437      */
48438     select : function(index, scrollIntoView){
48439         Roo.log('select ');
48440         return  ;
48441         
48442         this.selectedIndex = index;
48443         this.view.select(index);
48444         if(scrollIntoView !== false){
48445             var el = this.view.getNode(index);
48446             if(el){
48447                 this.innerList.scrollChildIntoView(el, false);
48448             }
48449         }
48450     },
48451
48452       
48453
48454     // private
48455     validateBlur : function(){
48456         
48457         return;
48458         
48459     },
48460
48461     // private
48462     initQuery : function(){
48463         this.doQuery(this.getRawValue());
48464     },
48465
48466     // private
48467     doForce : function(){
48468         if(this.el.dom.value.length > 0){
48469             this.el.dom.value =
48470                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48471              
48472         }
48473     },
48474
48475     /**
48476      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48477      * query allowing the query action to be canceled if needed.
48478      * @param {String} query The SQL query to execute
48479      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48480      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48481      * saved in the current store (defaults to false)
48482      */
48483     doQuery : function(q, forceAll){
48484         
48485         Roo.log('doQuery?');
48486         if(q === undefined || q === null){
48487             q = '';
48488         }
48489         var qe = {
48490             query: q,
48491             forceAll: forceAll,
48492             combo: this,
48493             cancel:false
48494         };
48495         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48496             return false;
48497         }
48498         q = qe.query;
48499         forceAll = qe.forceAll;
48500         if(forceAll === true || (q.length >= this.minChars)){
48501             if(this.lastQuery != q || this.alwaysQuery){
48502                 this.lastQuery = q;
48503                 if(this.mode == 'local'){
48504                     this.selectedIndex = -1;
48505                     if(forceAll){
48506                         this.store.clearFilter();
48507                     }else{
48508                         this.store.filter(this.displayField, q);
48509                     }
48510                     this.onLoad();
48511                 }else{
48512                     this.store.baseParams[this.queryParam] = q;
48513                     this.store.load({
48514                         params: this.getParams(q)
48515                     });
48516                     this.expand();
48517                 }
48518             }else{
48519                 this.selectedIndex = -1;
48520                 this.onLoad();   
48521             }
48522         }
48523     },
48524
48525     // private
48526     getParams : function(q){
48527         var p = {};
48528         //p[this.queryParam] = q;
48529         if(this.pageSize){
48530             p.start = 0;
48531             p.limit = this.pageSize;
48532         }
48533         return p;
48534     },
48535
48536     /**
48537      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48538      */
48539     collapse : function(){
48540         
48541     },
48542
48543     // private
48544     collapseIf : function(e){
48545         
48546     },
48547
48548     /**
48549      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48550      */
48551     expand : function(){
48552         
48553     } ,
48554
48555     // private
48556      
48557
48558     /** 
48559     * @cfg {Boolean} grow 
48560     * @hide 
48561     */
48562     /** 
48563     * @cfg {Number} growMin 
48564     * @hide 
48565     */
48566     /** 
48567     * @cfg {Number} growMax 
48568     * @hide 
48569     */
48570     /**
48571      * @hide
48572      * @method autoSize
48573      */
48574     
48575     setWidth : function()
48576     {
48577         
48578     },
48579     getResizeEl : function(){
48580         return this.el;
48581     }
48582 });//<script type="text/javasscript">
48583  
48584
48585 /**
48586  * @class Roo.DDView
48587  * A DnD enabled version of Roo.View.
48588  * @param {Element/String} container The Element in which to create the View.
48589  * @param {String} tpl The template string used to create the markup for each element of the View
48590  * @param {Object} config The configuration properties. These include all the config options of
48591  * {@link Roo.View} plus some specific to this class.<br>
48592  * <p>
48593  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48594  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48595  * <p>
48596  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48597 .x-view-drag-insert-above {
48598         border-top:1px dotted #3366cc;
48599 }
48600 .x-view-drag-insert-below {
48601         border-bottom:1px dotted #3366cc;
48602 }
48603 </code></pre>
48604  * 
48605  */
48606  
48607 Roo.DDView = function(container, tpl, config) {
48608     Roo.DDView.superclass.constructor.apply(this, arguments);
48609     this.getEl().setStyle("outline", "0px none");
48610     this.getEl().unselectable();
48611     if (this.dragGroup) {
48612                 this.setDraggable(this.dragGroup.split(","));
48613     }
48614     if (this.dropGroup) {
48615                 this.setDroppable(this.dropGroup.split(","));
48616     }
48617     if (this.deletable) {
48618         this.setDeletable();
48619     }
48620     this.isDirtyFlag = false;
48621         this.addEvents({
48622                 "drop" : true
48623         });
48624 };
48625
48626 Roo.extend(Roo.DDView, Roo.View, {
48627 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48628 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48629 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48630 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48631
48632         isFormField: true,
48633
48634         reset: Roo.emptyFn,
48635         
48636         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48637
48638         validate: function() {
48639                 return true;
48640         },
48641         
48642         destroy: function() {
48643                 this.purgeListeners();
48644                 this.getEl.removeAllListeners();
48645                 this.getEl().remove();
48646                 if (this.dragZone) {
48647                         if (this.dragZone.destroy) {
48648                                 this.dragZone.destroy();
48649                         }
48650                 }
48651                 if (this.dropZone) {
48652                         if (this.dropZone.destroy) {
48653                                 this.dropZone.destroy();
48654                         }
48655                 }
48656         },
48657
48658 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48659         getName: function() {
48660                 return this.name;
48661         },
48662
48663 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48664         setValue: function(v) {
48665                 if (!this.store) {
48666                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48667                 }
48668                 var data = {};
48669                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48670                 this.store.proxy = new Roo.data.MemoryProxy(data);
48671                 this.store.load();
48672         },
48673
48674 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48675         getValue: function() {
48676                 var result = '(';
48677                 this.store.each(function(rec) {
48678                         result += rec.id + ',';
48679                 });
48680                 return result.substr(0, result.length - 1) + ')';
48681         },
48682         
48683         getIds: function() {
48684                 var i = 0, result = new Array(this.store.getCount());
48685                 this.store.each(function(rec) {
48686                         result[i++] = rec.id;
48687                 });
48688                 return result;
48689         },
48690         
48691         isDirty: function() {
48692                 return this.isDirtyFlag;
48693         },
48694
48695 /**
48696  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48697  *      whole Element becomes the target, and this causes the drop gesture to append.
48698  */
48699     getTargetFromEvent : function(e) {
48700                 var target = e.getTarget();
48701                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48702                 target = target.parentNode;
48703                 }
48704                 if (!target) {
48705                         target = this.el.dom.lastChild || this.el.dom;
48706                 }
48707                 return target;
48708     },
48709
48710 /**
48711  *      Create the drag data which consists of an object which has the property "ddel" as
48712  *      the drag proxy element. 
48713  */
48714     getDragData : function(e) {
48715         var target = this.findItemFromChild(e.getTarget());
48716                 if(target) {
48717                         this.handleSelection(e);
48718                         var selNodes = this.getSelectedNodes();
48719             var dragData = {
48720                 source: this,
48721                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48722                 nodes: selNodes,
48723                 records: []
48724                         };
48725                         var selectedIndices = this.getSelectedIndexes();
48726                         for (var i = 0; i < selectedIndices.length; i++) {
48727                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48728                         }
48729                         if (selNodes.length == 1) {
48730                                 dragData.ddel = target.cloneNode(true); // the div element
48731                         } else {
48732                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48733                                 div.className = 'multi-proxy';
48734                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48735                                         div.appendChild(selNodes[i].cloneNode(true));
48736                                 }
48737                                 dragData.ddel = div;
48738                         }
48739             //console.log(dragData)
48740             //console.log(dragData.ddel.innerHTML)
48741                         return dragData;
48742                 }
48743         //console.log('nodragData')
48744                 return false;
48745     },
48746     
48747 /**     Specify to which ddGroup items in this DDView may be dragged. */
48748     setDraggable: function(ddGroup) {
48749         if (ddGroup instanceof Array) {
48750                 Roo.each(ddGroup, this.setDraggable, this);
48751                 return;
48752         }
48753         if (this.dragZone) {
48754                 this.dragZone.addToGroup(ddGroup);
48755         } else {
48756                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48757                                 containerScroll: true,
48758                                 ddGroup: ddGroup 
48759
48760                         });
48761 //                      Draggability implies selection. DragZone's mousedown selects the element.
48762                         if (!this.multiSelect) { this.singleSelect = true; }
48763
48764 //                      Wire the DragZone's handlers up to methods in *this*
48765                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48766                 }
48767     },
48768
48769 /**     Specify from which ddGroup this DDView accepts drops. */
48770     setDroppable: function(ddGroup) {
48771         if (ddGroup instanceof Array) {
48772                 Roo.each(ddGroup, this.setDroppable, this);
48773                 return;
48774         }
48775         if (this.dropZone) {
48776                 this.dropZone.addToGroup(ddGroup);
48777         } else {
48778                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48779                                 containerScroll: true,
48780                                 ddGroup: ddGroup
48781                         });
48782
48783 //                      Wire the DropZone's handlers up to methods in *this*
48784                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48785                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48786                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48787                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48788                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48789                 }
48790     },
48791
48792 /**     Decide whether to drop above or below a View node. */
48793     getDropPoint : function(e, n, dd){
48794         if (n == this.el.dom) { return "above"; }
48795                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48796                 var c = t + (b - t) / 2;
48797                 var y = Roo.lib.Event.getPageY(e);
48798                 if(y <= c) {
48799                         return "above";
48800                 }else{
48801                         return "below";
48802                 }
48803     },
48804
48805     onNodeEnter : function(n, dd, e, data){
48806                 return false;
48807     },
48808     
48809     onNodeOver : function(n, dd, e, data){
48810                 var pt = this.getDropPoint(e, n, dd);
48811                 // set the insert point style on the target node
48812                 var dragElClass = this.dropNotAllowed;
48813                 if (pt) {
48814                         var targetElClass;
48815                         if (pt == "above"){
48816                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48817                                 targetElClass = "x-view-drag-insert-above";
48818                         } else {
48819                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48820                                 targetElClass = "x-view-drag-insert-below";
48821                         }
48822                         if (this.lastInsertClass != targetElClass){
48823                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48824                                 this.lastInsertClass = targetElClass;
48825                         }
48826                 }
48827                 return dragElClass;
48828         },
48829
48830     onNodeOut : function(n, dd, e, data){
48831                 this.removeDropIndicators(n);
48832     },
48833
48834     onNodeDrop : function(n, dd, e, data){
48835         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48836                 return false;
48837         }
48838         var pt = this.getDropPoint(e, n, dd);
48839                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48840                 if (pt == "below") { insertAt++; }
48841                 for (var i = 0; i < data.records.length; i++) {
48842                         var r = data.records[i];
48843                         var dup = this.store.getById(r.id);
48844                         if (dup && (dd != this.dragZone)) {
48845                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48846                         } else {
48847                                 if (data.copy) {
48848                                         this.store.insert(insertAt++, r.copy());
48849                                 } else {
48850                                         data.source.isDirtyFlag = true;
48851                                         r.store.remove(r);
48852                                         this.store.insert(insertAt++, r);
48853                                 }
48854                                 this.isDirtyFlag = true;
48855                         }
48856                 }
48857                 this.dragZone.cachedTarget = null;
48858                 return true;
48859     },
48860
48861     removeDropIndicators : function(n){
48862                 if(n){
48863                         Roo.fly(n).removeClass([
48864                                 "x-view-drag-insert-above",
48865                                 "x-view-drag-insert-below"]);
48866                         this.lastInsertClass = "_noclass";
48867                 }
48868     },
48869
48870 /**
48871  *      Utility method. Add a delete option to the DDView's context menu.
48872  *      @param {String} imageUrl The URL of the "delete" icon image.
48873  */
48874         setDeletable: function(imageUrl) {
48875                 if (!this.singleSelect && !this.multiSelect) {
48876                         this.singleSelect = true;
48877                 }
48878                 var c = this.getContextMenu();
48879                 this.contextMenu.on("itemclick", function(item) {
48880                         switch (item.id) {
48881                                 case "delete":
48882                                         this.remove(this.getSelectedIndexes());
48883                                         break;
48884                         }
48885                 }, this);
48886                 this.contextMenu.add({
48887                         icon: imageUrl,
48888                         id: "delete",
48889                         text: 'Delete'
48890                 });
48891         },
48892         
48893 /**     Return the context menu for this DDView. */
48894         getContextMenu: function() {
48895                 if (!this.contextMenu) {
48896 //                      Create the View's context menu
48897                         this.contextMenu = new Roo.menu.Menu({
48898                                 id: this.id + "-contextmenu"
48899                         });
48900                         this.el.on("contextmenu", this.showContextMenu, this);
48901                 }
48902                 return this.contextMenu;
48903         },
48904         
48905         disableContextMenu: function() {
48906                 if (this.contextMenu) {
48907                         this.el.un("contextmenu", this.showContextMenu, this);
48908                 }
48909         },
48910
48911         showContextMenu: function(e, item) {
48912         item = this.findItemFromChild(e.getTarget());
48913                 if (item) {
48914                         e.stopEvent();
48915                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48916                         this.contextMenu.showAt(e.getXY());
48917             }
48918     },
48919
48920 /**
48921  *      Remove {@link Roo.data.Record}s at the specified indices.
48922  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48923  */
48924     remove: function(selectedIndices) {
48925                 selectedIndices = [].concat(selectedIndices);
48926                 for (var i = 0; i < selectedIndices.length; i++) {
48927                         var rec = this.store.getAt(selectedIndices[i]);
48928                         this.store.remove(rec);
48929                 }
48930     },
48931
48932 /**
48933  *      Double click fires the event, but also, if this is draggable, and there is only one other
48934  *      related DropZone, it transfers the selected node.
48935  */
48936     onDblClick : function(e){
48937         var item = this.findItemFromChild(e.getTarget());
48938         if(item){
48939             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48940                 return false;
48941             }
48942             if (this.dragGroup) {
48943                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48944                     while (targets.indexOf(this.dropZone) > -1) {
48945                             targets.remove(this.dropZone);
48946                                 }
48947                     if (targets.length == 1) {
48948                                         this.dragZone.cachedTarget = null;
48949                         var el = Roo.get(targets[0].getEl());
48950                         var box = el.getBox(true);
48951                         targets[0].onNodeDrop(el.dom, {
48952                                 target: el.dom,
48953                                 xy: [box.x, box.y + box.height - 1]
48954                         }, null, this.getDragData(e));
48955                     }
48956                 }
48957         }
48958     },
48959     
48960     handleSelection: function(e) {
48961                 this.dragZone.cachedTarget = null;
48962         var item = this.findItemFromChild(e.getTarget());
48963         if (!item) {
48964                 this.clearSelections(true);
48965                 return;
48966         }
48967                 if (item && (this.multiSelect || this.singleSelect)){
48968                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48969                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48970                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48971                                 this.unselect(item);
48972                         } else {
48973                                 this.select(item, this.multiSelect && e.ctrlKey);
48974                                 this.lastSelection = item;
48975                         }
48976                 }
48977     },
48978
48979     onItemClick : function(item, index, e){
48980                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48981                         return false;
48982                 }
48983                 return true;
48984     },
48985
48986     unselect : function(nodeInfo, suppressEvent){
48987                 var node = this.getNode(nodeInfo);
48988                 if(node && this.isSelected(node)){
48989                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48990                                 Roo.fly(node).removeClass(this.selectedClass);
48991                                 this.selections.remove(node);
48992                                 if(!suppressEvent){
48993                                         this.fireEvent("selectionchange", this, this.selections);
48994                                 }
48995                         }
48996                 }
48997     }
48998 });
48999 /*
49000  * Based on:
49001  * Ext JS Library 1.1.1
49002  * Copyright(c) 2006-2007, Ext JS, LLC.
49003  *
49004  * Originally Released Under LGPL - original licence link has changed is not relivant.
49005  *
49006  * Fork - LGPL
49007  * <script type="text/javascript">
49008  */
49009  
49010 /**
49011  * @class Roo.LayoutManager
49012  * @extends Roo.util.Observable
49013  * Base class for layout managers.
49014  */
49015 Roo.LayoutManager = function(container, config){
49016     Roo.LayoutManager.superclass.constructor.call(this);
49017     this.el = Roo.get(container);
49018     // ie scrollbar fix
49019     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49020         document.body.scroll = "no";
49021     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49022         this.el.position('relative');
49023     }
49024     this.id = this.el.id;
49025     this.el.addClass("x-layout-container");
49026     /** false to disable window resize monitoring @type Boolean */
49027     this.monitorWindowResize = true;
49028     this.regions = {};
49029     this.addEvents({
49030         /**
49031          * @event layout
49032          * Fires when a layout is performed. 
49033          * @param {Roo.LayoutManager} this
49034          */
49035         "layout" : true,
49036         /**
49037          * @event regionresized
49038          * Fires when the user resizes a region. 
49039          * @param {Roo.LayoutRegion} region The resized region
49040          * @param {Number} newSize The new size (width for east/west, height for north/south)
49041          */
49042         "regionresized" : true,
49043         /**
49044          * @event regioncollapsed
49045          * Fires when a region is collapsed. 
49046          * @param {Roo.LayoutRegion} region The collapsed region
49047          */
49048         "regioncollapsed" : true,
49049         /**
49050          * @event regionexpanded
49051          * Fires when a region is expanded.  
49052          * @param {Roo.LayoutRegion} region The expanded region
49053          */
49054         "regionexpanded" : true
49055     });
49056     this.updating = false;
49057     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49058 };
49059
49060 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49061     /**
49062      * Returns true if this layout is currently being updated
49063      * @return {Boolean}
49064      */
49065     isUpdating : function(){
49066         return this.updating; 
49067     },
49068     
49069     /**
49070      * Suspend the LayoutManager from doing auto-layouts while
49071      * making multiple add or remove calls
49072      */
49073     beginUpdate : function(){
49074         this.updating = true;    
49075     },
49076     
49077     /**
49078      * Restore auto-layouts and optionally disable the manager from performing a layout
49079      * @param {Boolean} noLayout true to disable a layout update 
49080      */
49081     endUpdate : function(noLayout){
49082         this.updating = false;
49083         if(!noLayout){
49084             this.layout();
49085         }    
49086     },
49087     
49088     layout: function(){
49089         
49090     },
49091     
49092     onRegionResized : function(region, newSize){
49093         this.fireEvent("regionresized", region, newSize);
49094         this.layout();
49095     },
49096     
49097     onRegionCollapsed : function(region){
49098         this.fireEvent("regioncollapsed", region);
49099     },
49100     
49101     onRegionExpanded : function(region){
49102         this.fireEvent("regionexpanded", region);
49103     },
49104         
49105     /**
49106      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49107      * performs box-model adjustments.
49108      * @return {Object} The size as an object {width: (the width), height: (the height)}
49109      */
49110     getViewSize : function(){
49111         var size;
49112         if(this.el.dom != document.body){
49113             size = this.el.getSize();
49114         }else{
49115             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49116         }
49117         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49118         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49119         return size;
49120     },
49121     
49122     /**
49123      * Returns the Element this layout is bound to.
49124      * @return {Roo.Element}
49125      */
49126     getEl : function(){
49127         return this.el;
49128     },
49129     
49130     /**
49131      * Returns the specified region.
49132      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49133      * @return {Roo.LayoutRegion}
49134      */
49135     getRegion : function(target){
49136         return this.regions[target.toLowerCase()];
49137     },
49138     
49139     onWindowResize : function(){
49140         if(this.monitorWindowResize){
49141             this.layout();
49142         }
49143     }
49144 });/*
49145  * Based on:
49146  * Ext JS Library 1.1.1
49147  * Copyright(c) 2006-2007, Ext JS, LLC.
49148  *
49149  * Originally Released Under LGPL - original licence link has changed is not relivant.
49150  *
49151  * Fork - LGPL
49152  * <script type="text/javascript">
49153  */
49154 /**
49155  * @class Roo.BorderLayout
49156  * @extends Roo.LayoutManager
49157  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49158  * please see: <br><br>
49159  * <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>
49160  * <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>
49161  * Example:
49162  <pre><code>
49163  var layout = new Roo.BorderLayout(document.body, {
49164     north: {
49165         initialSize: 25,
49166         titlebar: false
49167     },
49168     west: {
49169         split:true,
49170         initialSize: 200,
49171         minSize: 175,
49172         maxSize: 400,
49173         titlebar: true,
49174         collapsible: true
49175     },
49176     east: {
49177         split:true,
49178         initialSize: 202,
49179         minSize: 175,
49180         maxSize: 400,
49181         titlebar: true,
49182         collapsible: true
49183     },
49184     south: {
49185         split:true,
49186         initialSize: 100,
49187         minSize: 100,
49188         maxSize: 200,
49189         titlebar: true,
49190         collapsible: true
49191     },
49192     center: {
49193         titlebar: true,
49194         autoScroll:true,
49195         resizeTabs: true,
49196         minTabWidth: 50,
49197         preferredTabWidth: 150
49198     }
49199 });
49200
49201 // shorthand
49202 var CP = Roo.ContentPanel;
49203
49204 layout.beginUpdate();
49205 layout.add("north", new CP("north", "North"));
49206 layout.add("south", new CP("south", {title: "South", closable: true}));
49207 layout.add("west", new CP("west", {title: "West"}));
49208 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49209 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49210 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49211 layout.getRegion("center").showPanel("center1");
49212 layout.endUpdate();
49213 </code></pre>
49214
49215 <b>The container the layout is rendered into can be either the body element or any other element.
49216 If it is not the body element, the container needs to either be an absolute positioned element,
49217 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49218 the container size if it is not the body element.</b>
49219
49220 * @constructor
49221 * Create a new BorderLayout
49222 * @param {String/HTMLElement/Element} container The container this layout is bound to
49223 * @param {Object} config Configuration options
49224  */
49225 Roo.BorderLayout = function(container, config){
49226     config = config || {};
49227     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49228     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49229     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49230         var target = this.factory.validRegions[i];
49231         if(config[target]){
49232             this.addRegion(target, config[target]);
49233         }
49234     }
49235 };
49236
49237 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49238     /**
49239      * Creates and adds a new region if it doesn't already exist.
49240      * @param {String} target The target region key (north, south, east, west or center).
49241      * @param {Object} config The regions config object
49242      * @return {BorderLayoutRegion} The new region
49243      */
49244     addRegion : function(target, config){
49245         if(!this.regions[target]){
49246             var r = this.factory.create(target, this, config);
49247             this.bindRegion(target, r);
49248         }
49249         return this.regions[target];
49250     },
49251
49252     // private (kinda)
49253     bindRegion : function(name, r){
49254         this.regions[name] = r;
49255         r.on("visibilitychange", this.layout, this);
49256         r.on("paneladded", this.layout, this);
49257         r.on("panelremoved", this.layout, this);
49258         r.on("invalidated", this.layout, this);
49259         r.on("resized", this.onRegionResized, this);
49260         r.on("collapsed", this.onRegionCollapsed, this);
49261         r.on("expanded", this.onRegionExpanded, this);
49262     },
49263
49264     /**
49265      * Performs a layout update.
49266      */
49267     layout : function(){
49268         if(this.updating) return;
49269         var size = this.getViewSize();
49270         var w = size.width;
49271         var h = size.height;
49272         var centerW = w;
49273         var centerH = h;
49274         var centerY = 0;
49275         var centerX = 0;
49276         //var x = 0, y = 0;
49277
49278         var rs = this.regions;
49279         var north = rs["north"];
49280         var south = rs["south"]; 
49281         var west = rs["west"];
49282         var east = rs["east"];
49283         var center = rs["center"];
49284         //if(this.hideOnLayout){ // not supported anymore
49285             //c.el.setStyle("display", "none");
49286         //}
49287         if(north && north.isVisible()){
49288             var b = north.getBox();
49289             var m = north.getMargins();
49290             b.width = w - (m.left+m.right);
49291             b.x = m.left;
49292             b.y = m.top;
49293             centerY = b.height + b.y + m.bottom;
49294             centerH -= centerY;
49295             north.updateBox(this.safeBox(b));
49296         }
49297         if(south && south.isVisible()){
49298             var b = south.getBox();
49299             var m = south.getMargins();
49300             b.width = w - (m.left+m.right);
49301             b.x = m.left;
49302             var totalHeight = (b.height + m.top + m.bottom);
49303             b.y = h - totalHeight + m.top;
49304             centerH -= totalHeight;
49305             south.updateBox(this.safeBox(b));
49306         }
49307         if(west && west.isVisible()){
49308             var b = west.getBox();
49309             var m = west.getMargins();
49310             b.height = centerH - (m.top+m.bottom);
49311             b.x = m.left;
49312             b.y = centerY + m.top;
49313             var totalWidth = (b.width + m.left + m.right);
49314             centerX += totalWidth;
49315             centerW -= totalWidth;
49316             west.updateBox(this.safeBox(b));
49317         }
49318         if(east && east.isVisible()){
49319             var b = east.getBox();
49320             var m = east.getMargins();
49321             b.height = centerH - (m.top+m.bottom);
49322             var totalWidth = (b.width + m.left + m.right);
49323             b.x = w - totalWidth + m.left;
49324             b.y = centerY + m.top;
49325             centerW -= totalWidth;
49326             east.updateBox(this.safeBox(b));
49327         }
49328         if(center){
49329             var m = center.getMargins();
49330             var centerBox = {
49331                 x: centerX + m.left,
49332                 y: centerY + m.top,
49333                 width: centerW - (m.left+m.right),
49334                 height: centerH - (m.top+m.bottom)
49335             };
49336             //if(this.hideOnLayout){
49337                 //center.el.setStyle("display", "block");
49338             //}
49339             center.updateBox(this.safeBox(centerBox));
49340         }
49341         this.el.repaint();
49342         this.fireEvent("layout", this);
49343     },
49344
49345     // private
49346     safeBox : function(box){
49347         box.width = Math.max(0, box.width);
49348         box.height = Math.max(0, box.height);
49349         return box;
49350     },
49351
49352     /**
49353      * Adds a ContentPanel (or subclass) to this layout.
49354      * @param {String} target The target region key (north, south, east, west or center).
49355      * @param {Roo.ContentPanel} panel The panel to add
49356      * @return {Roo.ContentPanel} The added panel
49357      */
49358     add : function(target, panel){
49359          
49360         target = target.toLowerCase();
49361         return this.regions[target].add(panel);
49362     },
49363
49364     /**
49365      * Remove a ContentPanel (or subclass) to this layout.
49366      * @param {String} target The target region key (north, south, east, west or center).
49367      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49368      * @return {Roo.ContentPanel} The removed panel
49369      */
49370     remove : function(target, panel){
49371         target = target.toLowerCase();
49372         return this.regions[target].remove(panel);
49373     },
49374
49375     /**
49376      * Searches all regions for a panel with the specified id
49377      * @param {String} panelId
49378      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49379      */
49380     findPanel : function(panelId){
49381         var rs = this.regions;
49382         for(var target in rs){
49383             if(typeof rs[target] != "function"){
49384                 var p = rs[target].getPanel(panelId);
49385                 if(p){
49386                     return p;
49387                 }
49388             }
49389         }
49390         return null;
49391     },
49392
49393     /**
49394      * Searches all regions for a panel with the specified id and activates (shows) it.
49395      * @param {String/ContentPanel} panelId The panels id or the panel itself
49396      * @return {Roo.ContentPanel} The shown panel or null
49397      */
49398     showPanel : function(panelId) {
49399       var rs = this.regions;
49400       for(var target in rs){
49401          var r = rs[target];
49402          if(typeof r != "function"){
49403             if(r.hasPanel(panelId)){
49404                return r.showPanel(panelId);
49405             }
49406          }
49407       }
49408       return null;
49409    },
49410
49411    /**
49412      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49413      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49414      */
49415     restoreState : function(provider){
49416         if(!provider){
49417             provider = Roo.state.Manager;
49418         }
49419         var sm = new Roo.LayoutStateManager();
49420         sm.init(this, provider);
49421     },
49422
49423     /**
49424      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49425      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49426      * a valid ContentPanel config object.  Example:
49427      * <pre><code>
49428 // Create the main layout
49429 var layout = new Roo.BorderLayout('main-ct', {
49430     west: {
49431         split:true,
49432         minSize: 175,
49433         titlebar: true
49434     },
49435     center: {
49436         title:'Components'
49437     }
49438 }, 'main-ct');
49439
49440 // Create and add multiple ContentPanels at once via configs
49441 layout.batchAdd({
49442    west: {
49443        id: 'source-files',
49444        autoCreate:true,
49445        title:'Ext Source Files',
49446        autoScroll:true,
49447        fitToFrame:true
49448    },
49449    center : {
49450        el: cview,
49451        autoScroll:true,
49452        fitToFrame:true,
49453        toolbar: tb,
49454        resizeEl:'cbody'
49455    }
49456 });
49457 </code></pre>
49458      * @param {Object} regions An object containing ContentPanel configs by region name
49459      */
49460     batchAdd : function(regions){
49461         this.beginUpdate();
49462         for(var rname in regions){
49463             var lr = this.regions[rname];
49464             if(lr){
49465                 this.addTypedPanels(lr, regions[rname]);
49466             }
49467         }
49468         this.endUpdate();
49469     },
49470
49471     // private
49472     addTypedPanels : function(lr, ps){
49473         if(typeof ps == 'string'){
49474             lr.add(new Roo.ContentPanel(ps));
49475         }
49476         else if(ps instanceof Array){
49477             for(var i =0, len = ps.length; i < len; i++){
49478                 this.addTypedPanels(lr, ps[i]);
49479             }
49480         }
49481         else if(!ps.events){ // raw config?
49482             var el = ps.el;
49483             delete ps.el; // prevent conflict
49484             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49485         }
49486         else {  // panel object assumed!
49487             lr.add(ps);
49488         }
49489     },
49490     /**
49491      * Adds a xtype elements to the layout.
49492      * <pre><code>
49493
49494 layout.addxtype({
49495        xtype : 'ContentPanel',
49496        region: 'west',
49497        items: [ .... ]
49498    }
49499 );
49500
49501 layout.addxtype({
49502         xtype : 'NestedLayoutPanel',
49503         region: 'west',
49504         layout: {
49505            center: { },
49506            west: { }   
49507         },
49508         items : [ ... list of content panels or nested layout panels.. ]
49509    }
49510 );
49511 </code></pre>
49512      * @param {Object} cfg Xtype definition of item to add.
49513      */
49514     addxtype : function(cfg)
49515     {
49516         // basically accepts a pannel...
49517         // can accept a layout region..!?!?
49518         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49519         
49520         if (!cfg.xtype.match(/Panel$/)) {
49521             return false;
49522         }
49523         var ret = false;
49524         
49525         if (typeof(cfg.region) == 'undefined') {
49526             Roo.log("Failed to add Panel, region was not set");
49527             Roo.log(cfg);
49528             return false;
49529         }
49530         var region = cfg.region;
49531         delete cfg.region;
49532         
49533           
49534         var xitems = [];
49535         if (cfg.items) {
49536             xitems = cfg.items;
49537             delete cfg.items;
49538         }
49539         var nb = false;
49540         
49541         switch(cfg.xtype) 
49542         {
49543             case 'ContentPanel':  // ContentPanel (el, cfg)
49544             case 'ScrollPanel':  // ContentPanel (el, cfg)
49545             case 'ViewPanel': 
49546                 if(cfg.autoCreate) {
49547                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49548                 } else {
49549                     var el = this.el.createChild();
49550                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49551                 }
49552                 
49553                 this.add(region, ret);
49554                 break;
49555             
49556             
49557             case 'TreePanel': // our new panel!
49558                 cfg.el = this.el.createChild();
49559                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49560                 this.add(region, ret);
49561                 break;
49562             
49563             case 'NestedLayoutPanel': 
49564                 // create a new Layout (which is  a Border Layout...
49565                 var el = this.el.createChild();
49566                 var clayout = cfg.layout;
49567                 delete cfg.layout;
49568                 clayout.items   = clayout.items  || [];
49569                 // replace this exitems with the clayout ones..
49570                 xitems = clayout.items;
49571                  
49572                 
49573                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49574                     cfg.background = false;
49575                 }
49576                 var layout = new Roo.BorderLayout(el, clayout);
49577                 
49578                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49579                 //console.log('adding nested layout panel '  + cfg.toSource());
49580                 this.add(region, ret);
49581                 nb = {}; /// find first...
49582                 break;
49583                 
49584             case 'GridPanel': 
49585             
49586                 // needs grid and region
49587                 
49588                 //var el = this.getRegion(region).el.createChild();
49589                 var el = this.el.createChild();
49590                 // create the grid first...
49591                 
49592                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49593                 delete cfg.grid;
49594                 if (region == 'center' && this.active ) {
49595                     cfg.background = false;
49596                 }
49597                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49598                 
49599                 this.add(region, ret);
49600                 if (cfg.background) {
49601                     ret.on('activate', function(gp) {
49602                         if (!gp.grid.rendered) {
49603                             gp.grid.render();
49604                         }
49605                     });
49606                 } else {
49607                     grid.render();
49608                 }
49609                 break;
49610            
49611            
49612            
49613                 
49614                 
49615                 
49616             default:
49617                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49618                     
49619                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49620                     this.add(region, ret);
49621                 } else {
49622                 
49623                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49624                     return null;
49625                 }
49626                 
49627              // GridPanel (grid, cfg)
49628             
49629         }
49630         this.beginUpdate();
49631         // add children..
49632         var region = '';
49633         var abn = {};
49634         Roo.each(xitems, function(i)  {
49635             region = nb && i.region ? i.region : false;
49636             
49637             var add = ret.addxtype(i);
49638            
49639             if (region) {
49640                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49641                 if (!i.background) {
49642                     abn[region] = nb[region] ;
49643                 }
49644             }
49645             
49646         });
49647         this.endUpdate();
49648
49649         // make the last non-background panel active..
49650         //if (nb) { Roo.log(abn); }
49651         if (nb) {
49652             
49653             for(var r in abn) {
49654                 region = this.getRegion(r);
49655                 if (region) {
49656                     // tried using nb[r], but it does not work..
49657                      
49658                     region.showPanel(abn[r]);
49659                    
49660                 }
49661             }
49662         }
49663         return ret;
49664         
49665     }
49666 });
49667
49668 /**
49669  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49670  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49671  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49672  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49673  * <pre><code>
49674 // shorthand
49675 var CP = Roo.ContentPanel;
49676
49677 var layout = Roo.BorderLayout.create({
49678     north: {
49679         initialSize: 25,
49680         titlebar: false,
49681         panels: [new CP("north", "North")]
49682     },
49683     west: {
49684         split:true,
49685         initialSize: 200,
49686         minSize: 175,
49687         maxSize: 400,
49688         titlebar: true,
49689         collapsible: true,
49690         panels: [new CP("west", {title: "West"})]
49691     },
49692     east: {
49693         split:true,
49694         initialSize: 202,
49695         minSize: 175,
49696         maxSize: 400,
49697         titlebar: true,
49698         collapsible: true,
49699         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49700     },
49701     south: {
49702         split:true,
49703         initialSize: 100,
49704         minSize: 100,
49705         maxSize: 200,
49706         titlebar: true,
49707         collapsible: true,
49708         panels: [new CP("south", {title: "South", closable: true})]
49709     },
49710     center: {
49711         titlebar: true,
49712         autoScroll:true,
49713         resizeTabs: true,
49714         minTabWidth: 50,
49715         preferredTabWidth: 150,
49716         panels: [
49717             new CP("center1", {title: "Close Me", closable: true}),
49718             new CP("center2", {title: "Center Panel", closable: false})
49719         ]
49720     }
49721 }, document.body);
49722
49723 layout.getRegion("center").showPanel("center1");
49724 </code></pre>
49725  * @param config
49726  * @param targetEl
49727  */
49728 Roo.BorderLayout.create = function(config, targetEl){
49729     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49730     layout.beginUpdate();
49731     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49732     for(var j = 0, jlen = regions.length; j < jlen; j++){
49733         var lr = regions[j];
49734         if(layout.regions[lr] && config[lr].panels){
49735             var r = layout.regions[lr];
49736             var ps = config[lr].panels;
49737             layout.addTypedPanels(r, ps);
49738         }
49739     }
49740     layout.endUpdate();
49741     return layout;
49742 };
49743
49744 // private
49745 Roo.BorderLayout.RegionFactory = {
49746     // private
49747     validRegions : ["north","south","east","west","center"],
49748
49749     // private
49750     create : function(target, mgr, config){
49751         target = target.toLowerCase();
49752         if(config.lightweight || config.basic){
49753             return new Roo.BasicLayoutRegion(mgr, config, target);
49754         }
49755         switch(target){
49756             case "north":
49757                 return new Roo.NorthLayoutRegion(mgr, config);
49758             case "south":
49759                 return new Roo.SouthLayoutRegion(mgr, config);
49760             case "east":
49761                 return new Roo.EastLayoutRegion(mgr, config);
49762             case "west":
49763                 return new Roo.WestLayoutRegion(mgr, config);
49764             case "center":
49765                 return new Roo.CenterLayoutRegion(mgr, config);
49766         }
49767         throw 'Layout region "'+target+'" not supported.';
49768     }
49769 };/*
49770  * Based on:
49771  * Ext JS Library 1.1.1
49772  * Copyright(c) 2006-2007, Ext JS, LLC.
49773  *
49774  * Originally Released Under LGPL - original licence link has changed is not relivant.
49775  *
49776  * Fork - LGPL
49777  * <script type="text/javascript">
49778  */
49779  
49780 /**
49781  * @class Roo.BasicLayoutRegion
49782  * @extends Roo.util.Observable
49783  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49784  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49785  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49786  */
49787 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49788     this.mgr = mgr;
49789     this.position  = pos;
49790     this.events = {
49791         /**
49792          * @scope Roo.BasicLayoutRegion
49793          */
49794         
49795         /**
49796          * @event beforeremove
49797          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49798          * @param {Roo.LayoutRegion} this
49799          * @param {Roo.ContentPanel} panel The panel
49800          * @param {Object} e The cancel event object
49801          */
49802         "beforeremove" : true,
49803         /**
49804          * @event invalidated
49805          * Fires when the layout for this region is changed.
49806          * @param {Roo.LayoutRegion} this
49807          */
49808         "invalidated" : true,
49809         /**
49810          * @event visibilitychange
49811          * Fires when this region is shown or hidden 
49812          * @param {Roo.LayoutRegion} this
49813          * @param {Boolean} visibility true or false
49814          */
49815         "visibilitychange" : true,
49816         /**
49817          * @event paneladded
49818          * Fires when a panel is added. 
49819          * @param {Roo.LayoutRegion} this
49820          * @param {Roo.ContentPanel} panel The panel
49821          */
49822         "paneladded" : true,
49823         /**
49824          * @event panelremoved
49825          * Fires when a panel is removed. 
49826          * @param {Roo.LayoutRegion} this
49827          * @param {Roo.ContentPanel} panel The panel
49828          */
49829         "panelremoved" : true,
49830         /**
49831          * @event collapsed
49832          * Fires when this region is collapsed.
49833          * @param {Roo.LayoutRegion} this
49834          */
49835         "collapsed" : true,
49836         /**
49837          * @event expanded
49838          * Fires when this region is expanded.
49839          * @param {Roo.LayoutRegion} this
49840          */
49841         "expanded" : true,
49842         /**
49843          * @event slideshow
49844          * Fires when this region is slid into view.
49845          * @param {Roo.LayoutRegion} this
49846          */
49847         "slideshow" : true,
49848         /**
49849          * @event slidehide
49850          * Fires when this region slides out of view. 
49851          * @param {Roo.LayoutRegion} this
49852          */
49853         "slidehide" : true,
49854         /**
49855          * @event panelactivated
49856          * Fires when a panel is activated. 
49857          * @param {Roo.LayoutRegion} this
49858          * @param {Roo.ContentPanel} panel The activated panel
49859          */
49860         "panelactivated" : true,
49861         /**
49862          * @event resized
49863          * Fires when the user resizes this region. 
49864          * @param {Roo.LayoutRegion} this
49865          * @param {Number} newSize The new size (width for east/west, height for north/south)
49866          */
49867         "resized" : true
49868     };
49869     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49870     this.panels = new Roo.util.MixedCollection();
49871     this.panels.getKey = this.getPanelId.createDelegate(this);
49872     this.box = null;
49873     this.activePanel = null;
49874     // ensure listeners are added...
49875     
49876     if (config.listeners || config.events) {
49877         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49878             listeners : config.listeners || {},
49879             events : config.events || {}
49880         });
49881     }
49882     
49883     if(skipConfig !== true){
49884         this.applyConfig(config);
49885     }
49886 };
49887
49888 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49889     getPanelId : function(p){
49890         return p.getId();
49891     },
49892     
49893     applyConfig : function(config){
49894         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49895         this.config = config;
49896         
49897     },
49898     
49899     /**
49900      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49901      * the width, for horizontal (north, south) the height.
49902      * @param {Number} newSize The new width or height
49903      */
49904     resizeTo : function(newSize){
49905         var el = this.el ? this.el :
49906                  (this.activePanel ? this.activePanel.getEl() : null);
49907         if(el){
49908             switch(this.position){
49909                 case "east":
49910                 case "west":
49911                     el.setWidth(newSize);
49912                     this.fireEvent("resized", this, newSize);
49913                 break;
49914                 case "north":
49915                 case "south":
49916                     el.setHeight(newSize);
49917                     this.fireEvent("resized", this, newSize);
49918                 break;                
49919             }
49920         }
49921     },
49922     
49923     getBox : function(){
49924         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49925     },
49926     
49927     getMargins : function(){
49928         return this.margins;
49929     },
49930     
49931     updateBox : function(box){
49932         this.box = box;
49933         var el = this.activePanel.getEl();
49934         el.dom.style.left = box.x + "px";
49935         el.dom.style.top = box.y + "px";
49936         this.activePanel.setSize(box.width, box.height);
49937     },
49938     
49939     /**
49940      * Returns the container element for this region.
49941      * @return {Roo.Element}
49942      */
49943     getEl : function(){
49944         return this.activePanel;
49945     },
49946     
49947     /**
49948      * Returns true if this region is currently visible.
49949      * @return {Boolean}
49950      */
49951     isVisible : function(){
49952         return this.activePanel ? true : false;
49953     },
49954     
49955     setActivePanel : function(panel){
49956         panel = this.getPanel(panel);
49957         if(this.activePanel && this.activePanel != panel){
49958             this.activePanel.setActiveState(false);
49959             this.activePanel.getEl().setLeftTop(-10000,-10000);
49960         }
49961         this.activePanel = panel;
49962         panel.setActiveState(true);
49963         if(this.box){
49964             panel.setSize(this.box.width, this.box.height);
49965         }
49966         this.fireEvent("panelactivated", this, panel);
49967         this.fireEvent("invalidated");
49968     },
49969     
49970     /**
49971      * Show the specified panel.
49972      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49973      * @return {Roo.ContentPanel} The shown panel or null
49974      */
49975     showPanel : function(panel){
49976         if(panel = this.getPanel(panel)){
49977             this.setActivePanel(panel);
49978         }
49979         return panel;
49980     },
49981     
49982     /**
49983      * Get the active panel for this region.
49984      * @return {Roo.ContentPanel} The active panel or null
49985      */
49986     getActivePanel : function(){
49987         return this.activePanel;
49988     },
49989     
49990     /**
49991      * Add the passed ContentPanel(s)
49992      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49993      * @return {Roo.ContentPanel} The panel added (if only one was added)
49994      */
49995     add : function(panel){
49996         if(arguments.length > 1){
49997             for(var i = 0, len = arguments.length; i < len; i++) {
49998                 this.add(arguments[i]);
49999             }
50000             return null;
50001         }
50002         if(this.hasPanel(panel)){
50003             this.showPanel(panel);
50004             return panel;
50005         }
50006         var el = panel.getEl();
50007         if(el.dom.parentNode != this.mgr.el.dom){
50008             this.mgr.el.dom.appendChild(el.dom);
50009         }
50010         if(panel.setRegion){
50011             panel.setRegion(this);
50012         }
50013         this.panels.add(panel);
50014         el.setStyle("position", "absolute");
50015         if(!panel.background){
50016             this.setActivePanel(panel);
50017             if(this.config.initialSize && this.panels.getCount()==1){
50018                 this.resizeTo(this.config.initialSize);
50019             }
50020         }
50021         this.fireEvent("paneladded", this, panel);
50022         return panel;
50023     },
50024     
50025     /**
50026      * Returns true if the panel is in this region.
50027      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50028      * @return {Boolean}
50029      */
50030     hasPanel : function(panel){
50031         if(typeof panel == "object"){ // must be panel obj
50032             panel = panel.getId();
50033         }
50034         return this.getPanel(panel) ? true : false;
50035     },
50036     
50037     /**
50038      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50039      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50040      * @param {Boolean} preservePanel Overrides the config preservePanel option
50041      * @return {Roo.ContentPanel} The panel that was removed
50042      */
50043     remove : function(panel, preservePanel){
50044         panel = this.getPanel(panel);
50045         if(!panel){
50046             return null;
50047         }
50048         var e = {};
50049         this.fireEvent("beforeremove", this, panel, e);
50050         if(e.cancel === true){
50051             return null;
50052         }
50053         var panelId = panel.getId();
50054         this.panels.removeKey(panelId);
50055         return panel;
50056     },
50057     
50058     /**
50059      * Returns the panel specified or null if it's not in this region.
50060      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50061      * @return {Roo.ContentPanel}
50062      */
50063     getPanel : function(id){
50064         if(typeof id == "object"){ // must be panel obj
50065             return id;
50066         }
50067         return this.panels.get(id);
50068     },
50069     
50070     /**
50071      * Returns this regions position (north/south/east/west/center).
50072      * @return {String} 
50073      */
50074     getPosition: function(){
50075         return this.position;    
50076     }
50077 });/*
50078  * Based on:
50079  * Ext JS Library 1.1.1
50080  * Copyright(c) 2006-2007, Ext JS, LLC.
50081  *
50082  * Originally Released Under LGPL - original licence link has changed is not relivant.
50083  *
50084  * Fork - LGPL
50085  * <script type="text/javascript">
50086  */
50087  
50088 /**
50089  * @class Roo.LayoutRegion
50090  * @extends Roo.BasicLayoutRegion
50091  * This class represents a region in a layout manager.
50092  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50093  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50094  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50095  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50096  * @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})
50097  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50098  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50099  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50100  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50101  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50102  * @cfg {String}    title           The title for the region (overrides panel titles)
50103  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50104  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50105  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50106  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50107  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50108  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50109  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50110  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50111  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50112  * @cfg {Boolean}   showPin         True to show a pin button
50113  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50114  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50115  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50116  * @cfg {Number}    width           For East/West panels
50117  * @cfg {Number}    height          For North/South panels
50118  * @cfg {Boolean}   split           To show the splitter
50119  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50120  */
50121 Roo.LayoutRegion = function(mgr, config, pos){
50122     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50123     var dh = Roo.DomHelper;
50124     /** This region's container element 
50125     * @type Roo.Element */
50126     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50127     /** This region's title element 
50128     * @type Roo.Element */
50129
50130     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50131         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50132         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50133     ]}, true);
50134     this.titleEl.enableDisplayMode();
50135     /** This region's title text element 
50136     * @type HTMLElement */
50137     this.titleTextEl = this.titleEl.dom.firstChild;
50138     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50139     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50140     this.closeBtn.enableDisplayMode();
50141     this.closeBtn.on("click", this.closeClicked, this);
50142     this.closeBtn.hide();
50143
50144     this.createBody(config);
50145     this.visible = true;
50146     this.collapsed = false;
50147
50148     if(config.hideWhenEmpty){
50149         this.hide();
50150         this.on("paneladded", this.validateVisibility, this);
50151         this.on("panelremoved", this.validateVisibility, this);
50152     }
50153     this.applyConfig(config);
50154 };
50155
50156 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50157
50158     createBody : function(){
50159         /** This region's body element 
50160         * @type Roo.Element */
50161         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50162     },
50163
50164     applyConfig : function(c){
50165         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50166             var dh = Roo.DomHelper;
50167             if(c.titlebar !== false){
50168                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50169                 this.collapseBtn.on("click", this.collapse, this);
50170                 this.collapseBtn.enableDisplayMode();
50171
50172                 if(c.showPin === true || this.showPin){
50173                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50174                     this.stickBtn.enableDisplayMode();
50175                     this.stickBtn.on("click", this.expand, this);
50176                     this.stickBtn.hide();
50177                 }
50178             }
50179             /** This region's collapsed element
50180             * @type Roo.Element */
50181             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50182                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50183             ]}, true);
50184             if(c.floatable !== false){
50185                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50186                this.collapsedEl.on("click", this.collapseClick, this);
50187             }
50188
50189             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50190                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50191                    id: "message", unselectable: "on", style:{"float":"left"}});
50192                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50193              }
50194             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50195             this.expandBtn.on("click", this.expand, this);
50196         }
50197         if(this.collapseBtn){
50198             this.collapseBtn.setVisible(c.collapsible == true);
50199         }
50200         this.cmargins = c.cmargins || this.cmargins ||
50201                          (this.position == "west" || this.position == "east" ?
50202                              {top: 0, left: 2, right:2, bottom: 0} :
50203                              {top: 2, left: 0, right:0, bottom: 2});
50204         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50205         this.bottomTabs = c.tabPosition != "top";
50206         this.autoScroll = c.autoScroll || false;
50207         if(this.autoScroll){
50208             this.bodyEl.setStyle("overflow", "auto");
50209         }else{
50210             this.bodyEl.setStyle("overflow", "hidden");
50211         }
50212         //if(c.titlebar !== false){
50213             if((!c.titlebar && !c.title) || c.titlebar === false){
50214                 this.titleEl.hide();
50215             }else{
50216                 this.titleEl.show();
50217                 if(c.title){
50218                     this.titleTextEl.innerHTML = c.title;
50219                 }
50220             }
50221         //}
50222         this.duration = c.duration || .30;
50223         this.slideDuration = c.slideDuration || .45;
50224         this.config = c;
50225         if(c.collapsed){
50226             this.collapse(true);
50227         }
50228         if(c.hidden){
50229             this.hide();
50230         }
50231     },
50232     /**
50233      * Returns true if this region is currently visible.
50234      * @return {Boolean}
50235      */
50236     isVisible : function(){
50237         return this.visible;
50238     },
50239
50240     /**
50241      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50242      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50243      */
50244     setCollapsedTitle : function(title){
50245         title = title || "&#160;";
50246         if(this.collapsedTitleTextEl){
50247             this.collapsedTitleTextEl.innerHTML = title;
50248         }
50249     },
50250
50251     getBox : function(){
50252         var b;
50253         if(!this.collapsed){
50254             b = this.el.getBox(false, true);
50255         }else{
50256             b = this.collapsedEl.getBox(false, true);
50257         }
50258         return b;
50259     },
50260
50261     getMargins : function(){
50262         return this.collapsed ? this.cmargins : this.margins;
50263     },
50264
50265     highlight : function(){
50266         this.el.addClass("x-layout-panel-dragover");
50267     },
50268
50269     unhighlight : function(){
50270         this.el.removeClass("x-layout-panel-dragover");
50271     },
50272
50273     updateBox : function(box){
50274         this.box = box;
50275         if(!this.collapsed){
50276             this.el.dom.style.left = box.x + "px";
50277             this.el.dom.style.top = box.y + "px";
50278             this.updateBody(box.width, box.height);
50279         }else{
50280             this.collapsedEl.dom.style.left = box.x + "px";
50281             this.collapsedEl.dom.style.top = box.y + "px";
50282             this.collapsedEl.setSize(box.width, box.height);
50283         }
50284         if(this.tabs){
50285             this.tabs.autoSizeTabs();
50286         }
50287     },
50288
50289     updateBody : function(w, h){
50290         if(w !== null){
50291             this.el.setWidth(w);
50292             w -= this.el.getBorderWidth("rl");
50293             if(this.config.adjustments){
50294                 w += this.config.adjustments[0];
50295             }
50296         }
50297         if(h !== null){
50298             this.el.setHeight(h);
50299             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50300             h -= this.el.getBorderWidth("tb");
50301             if(this.config.adjustments){
50302                 h += this.config.adjustments[1];
50303             }
50304             this.bodyEl.setHeight(h);
50305             if(this.tabs){
50306                 h = this.tabs.syncHeight(h);
50307             }
50308         }
50309         if(this.panelSize){
50310             w = w !== null ? w : this.panelSize.width;
50311             h = h !== null ? h : this.panelSize.height;
50312         }
50313         if(this.activePanel){
50314             var el = this.activePanel.getEl();
50315             w = w !== null ? w : el.getWidth();
50316             h = h !== null ? h : el.getHeight();
50317             this.panelSize = {width: w, height: h};
50318             this.activePanel.setSize(w, h);
50319         }
50320         if(Roo.isIE && this.tabs){
50321             this.tabs.el.repaint();
50322         }
50323     },
50324
50325     /**
50326      * Returns the container element for this region.
50327      * @return {Roo.Element}
50328      */
50329     getEl : function(){
50330         return this.el;
50331     },
50332
50333     /**
50334      * Hides this region.
50335      */
50336     hide : function(){
50337         if(!this.collapsed){
50338             this.el.dom.style.left = "-2000px";
50339             this.el.hide();
50340         }else{
50341             this.collapsedEl.dom.style.left = "-2000px";
50342             this.collapsedEl.hide();
50343         }
50344         this.visible = false;
50345         this.fireEvent("visibilitychange", this, false);
50346     },
50347
50348     /**
50349      * Shows this region if it was previously hidden.
50350      */
50351     show : function(){
50352         if(!this.collapsed){
50353             this.el.show();
50354         }else{
50355             this.collapsedEl.show();
50356         }
50357         this.visible = true;
50358         this.fireEvent("visibilitychange", this, true);
50359     },
50360
50361     closeClicked : function(){
50362         if(this.activePanel){
50363             this.remove(this.activePanel);
50364         }
50365     },
50366
50367     collapseClick : function(e){
50368         if(this.isSlid){
50369            e.stopPropagation();
50370            this.slideIn();
50371         }else{
50372            e.stopPropagation();
50373            this.slideOut();
50374         }
50375     },
50376
50377     /**
50378      * Collapses this region.
50379      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50380      */
50381     collapse : function(skipAnim){
50382         if(this.collapsed) return;
50383         this.collapsed = true;
50384         if(this.split){
50385             this.split.el.hide();
50386         }
50387         if(this.config.animate && skipAnim !== true){
50388             this.fireEvent("invalidated", this);
50389             this.animateCollapse();
50390         }else{
50391             this.el.setLocation(-20000,-20000);
50392             this.el.hide();
50393             this.collapsedEl.show();
50394             this.fireEvent("collapsed", this);
50395             this.fireEvent("invalidated", this);
50396         }
50397     },
50398
50399     animateCollapse : function(){
50400         // overridden
50401     },
50402
50403     /**
50404      * Expands this region if it was previously collapsed.
50405      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50406      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50407      */
50408     expand : function(e, skipAnim){
50409         if(e) e.stopPropagation();
50410         if(!this.collapsed || this.el.hasActiveFx()) return;
50411         if(this.isSlid){
50412             this.afterSlideIn();
50413             skipAnim = true;
50414         }
50415         this.collapsed = false;
50416         if(this.config.animate && skipAnim !== true){
50417             this.animateExpand();
50418         }else{
50419             this.el.show();
50420             if(this.split){
50421                 this.split.el.show();
50422             }
50423             this.collapsedEl.setLocation(-2000,-2000);
50424             this.collapsedEl.hide();
50425             this.fireEvent("invalidated", this);
50426             this.fireEvent("expanded", this);
50427         }
50428     },
50429
50430     animateExpand : function(){
50431         // overridden
50432     },
50433
50434     initTabs : function()
50435     {
50436         this.bodyEl.setStyle("overflow", "hidden");
50437         var ts = new Roo.TabPanel(
50438                 this.bodyEl.dom,
50439                 {
50440                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50441                     disableTooltips: this.config.disableTabTips,
50442                     toolbar : this.config.toolbar
50443                 }
50444         );
50445         if(this.config.hideTabs){
50446             ts.stripWrap.setDisplayed(false);
50447         }
50448         this.tabs = ts;
50449         ts.resizeTabs = this.config.resizeTabs === true;
50450         ts.minTabWidth = this.config.minTabWidth || 40;
50451         ts.maxTabWidth = this.config.maxTabWidth || 250;
50452         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50453         ts.monitorResize = false;
50454         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50455         ts.bodyEl.addClass('x-layout-tabs-body');
50456         this.panels.each(this.initPanelAsTab, this);
50457     },
50458
50459     initPanelAsTab : function(panel){
50460         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50461                     this.config.closeOnTab && panel.isClosable());
50462         if(panel.tabTip !== undefined){
50463             ti.setTooltip(panel.tabTip);
50464         }
50465         ti.on("activate", function(){
50466               this.setActivePanel(panel);
50467         }, this);
50468         if(this.config.closeOnTab){
50469             ti.on("beforeclose", function(t, e){
50470                 e.cancel = true;
50471                 this.remove(panel);
50472             }, this);
50473         }
50474         return ti;
50475     },
50476
50477     updatePanelTitle : function(panel, title){
50478         if(this.activePanel == panel){
50479             this.updateTitle(title);
50480         }
50481         if(this.tabs){
50482             var ti = this.tabs.getTab(panel.getEl().id);
50483             ti.setText(title);
50484             if(panel.tabTip !== undefined){
50485                 ti.setTooltip(panel.tabTip);
50486             }
50487         }
50488     },
50489
50490     updateTitle : function(title){
50491         if(this.titleTextEl && !this.config.title){
50492             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50493         }
50494     },
50495
50496     setActivePanel : function(panel){
50497         panel = this.getPanel(panel);
50498         if(this.activePanel && this.activePanel != panel){
50499             this.activePanel.setActiveState(false);
50500         }
50501         this.activePanel = panel;
50502         panel.setActiveState(true);
50503         if(this.panelSize){
50504             panel.setSize(this.panelSize.width, this.panelSize.height);
50505         }
50506         if(this.closeBtn){
50507             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50508         }
50509         this.updateTitle(panel.getTitle());
50510         if(this.tabs){
50511             this.fireEvent("invalidated", this);
50512         }
50513         this.fireEvent("panelactivated", this, panel);
50514     },
50515
50516     /**
50517      * Shows the specified panel.
50518      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50519      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50520      */
50521     showPanel : function(panel)
50522     {
50523         panel = this.getPanel(panel);
50524         if(panel){
50525             if(this.tabs){
50526                 var tab = this.tabs.getTab(panel.getEl().id);
50527                 if(tab.isHidden()){
50528                     this.tabs.unhideTab(tab.id);
50529                 }
50530                 tab.activate();
50531             }else{
50532                 this.setActivePanel(panel);
50533             }
50534         }
50535         return panel;
50536     },
50537
50538     /**
50539      * Get the active panel for this region.
50540      * @return {Roo.ContentPanel} The active panel or null
50541      */
50542     getActivePanel : function(){
50543         return this.activePanel;
50544     },
50545
50546     validateVisibility : function(){
50547         if(this.panels.getCount() < 1){
50548             this.updateTitle("&#160;");
50549             this.closeBtn.hide();
50550             this.hide();
50551         }else{
50552             if(!this.isVisible()){
50553                 this.show();
50554             }
50555         }
50556     },
50557
50558     /**
50559      * Adds the passed ContentPanel(s) to this region.
50560      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50561      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50562      */
50563     add : function(panel){
50564         if(arguments.length > 1){
50565             for(var i = 0, len = arguments.length; i < len; i++) {
50566                 this.add(arguments[i]);
50567             }
50568             return null;
50569         }
50570         if(this.hasPanel(panel)){
50571             this.showPanel(panel);
50572             return panel;
50573         }
50574         panel.setRegion(this);
50575         this.panels.add(panel);
50576         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50577             this.bodyEl.dom.appendChild(panel.getEl().dom);
50578             if(panel.background !== true){
50579                 this.setActivePanel(panel);
50580             }
50581             this.fireEvent("paneladded", this, panel);
50582             return panel;
50583         }
50584         if(!this.tabs){
50585             this.initTabs();
50586         }else{
50587             this.initPanelAsTab(panel);
50588         }
50589         if(panel.background !== true){
50590             this.tabs.activate(panel.getEl().id);
50591         }
50592         this.fireEvent("paneladded", this, panel);
50593         return panel;
50594     },
50595
50596     /**
50597      * Hides the tab for the specified panel.
50598      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50599      */
50600     hidePanel : function(panel){
50601         if(this.tabs && (panel = this.getPanel(panel))){
50602             this.tabs.hideTab(panel.getEl().id);
50603         }
50604     },
50605
50606     /**
50607      * Unhides the tab for a previously hidden panel.
50608      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50609      */
50610     unhidePanel : function(panel){
50611         if(this.tabs && (panel = this.getPanel(panel))){
50612             this.tabs.unhideTab(panel.getEl().id);
50613         }
50614     },
50615
50616     clearPanels : function(){
50617         while(this.panels.getCount() > 0){
50618              this.remove(this.panels.first());
50619         }
50620     },
50621
50622     /**
50623      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50624      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50625      * @param {Boolean} preservePanel Overrides the config preservePanel option
50626      * @return {Roo.ContentPanel} The panel that was removed
50627      */
50628     remove : function(panel, preservePanel){
50629         panel = this.getPanel(panel);
50630         if(!panel){
50631             return null;
50632         }
50633         var e = {};
50634         this.fireEvent("beforeremove", this, panel, e);
50635         if(e.cancel === true){
50636             return null;
50637         }
50638         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50639         var panelId = panel.getId();
50640         this.panels.removeKey(panelId);
50641         if(preservePanel){
50642             document.body.appendChild(panel.getEl().dom);
50643         }
50644         if(this.tabs){
50645             this.tabs.removeTab(panel.getEl().id);
50646         }else if (!preservePanel){
50647             this.bodyEl.dom.removeChild(panel.getEl().dom);
50648         }
50649         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50650             var p = this.panels.first();
50651             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50652             tempEl.appendChild(p.getEl().dom);
50653             this.bodyEl.update("");
50654             this.bodyEl.dom.appendChild(p.getEl().dom);
50655             tempEl = null;
50656             this.updateTitle(p.getTitle());
50657             this.tabs = null;
50658             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50659             this.setActivePanel(p);
50660         }
50661         panel.setRegion(null);
50662         if(this.activePanel == panel){
50663             this.activePanel = null;
50664         }
50665         if(this.config.autoDestroy !== false && preservePanel !== true){
50666             try{panel.destroy();}catch(e){}
50667         }
50668         this.fireEvent("panelremoved", this, panel);
50669         return panel;
50670     },
50671
50672     /**
50673      * Returns the TabPanel component used by this region
50674      * @return {Roo.TabPanel}
50675      */
50676     getTabs : function(){
50677         return this.tabs;
50678     },
50679
50680     createTool : function(parentEl, className){
50681         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50682             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50683         btn.addClassOnOver("x-layout-tools-button-over");
50684         return btn;
50685     }
50686 });/*
50687  * Based on:
50688  * Ext JS Library 1.1.1
50689  * Copyright(c) 2006-2007, Ext JS, LLC.
50690  *
50691  * Originally Released Under LGPL - original licence link has changed is not relivant.
50692  *
50693  * Fork - LGPL
50694  * <script type="text/javascript">
50695  */
50696  
50697
50698
50699 /**
50700  * @class Roo.SplitLayoutRegion
50701  * @extends Roo.LayoutRegion
50702  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50703  */
50704 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50705     this.cursor = cursor;
50706     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50707 };
50708
50709 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50710     splitTip : "Drag to resize.",
50711     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50712     useSplitTips : false,
50713
50714     applyConfig : function(config){
50715         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50716         if(config.split){
50717             if(!this.split){
50718                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50719                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50720                 /** The SplitBar for this region 
50721                 * @type Roo.SplitBar */
50722                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50723                 this.split.on("moved", this.onSplitMove, this);
50724                 this.split.useShim = config.useShim === true;
50725                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50726                 if(this.useSplitTips){
50727                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50728                 }
50729                 if(config.collapsible){
50730                     this.split.el.on("dblclick", this.collapse,  this);
50731                 }
50732             }
50733             if(typeof config.minSize != "undefined"){
50734                 this.split.minSize = config.minSize;
50735             }
50736             if(typeof config.maxSize != "undefined"){
50737                 this.split.maxSize = config.maxSize;
50738             }
50739             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50740                 this.hideSplitter();
50741             }
50742         }
50743     },
50744
50745     getHMaxSize : function(){
50746          var cmax = this.config.maxSize || 10000;
50747          var center = this.mgr.getRegion("center");
50748          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50749     },
50750
50751     getVMaxSize : function(){
50752          var cmax = this.config.maxSize || 10000;
50753          var center = this.mgr.getRegion("center");
50754          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50755     },
50756
50757     onSplitMove : function(split, newSize){
50758         this.fireEvent("resized", this, newSize);
50759     },
50760     
50761     /** 
50762      * Returns the {@link Roo.SplitBar} for this region.
50763      * @return {Roo.SplitBar}
50764      */
50765     getSplitBar : function(){
50766         return this.split;
50767     },
50768     
50769     hide : function(){
50770         this.hideSplitter();
50771         Roo.SplitLayoutRegion.superclass.hide.call(this);
50772     },
50773
50774     hideSplitter : function(){
50775         if(this.split){
50776             this.split.el.setLocation(-2000,-2000);
50777             this.split.el.hide();
50778         }
50779     },
50780
50781     show : function(){
50782         if(this.split){
50783             this.split.el.show();
50784         }
50785         Roo.SplitLayoutRegion.superclass.show.call(this);
50786     },
50787     
50788     beforeSlide: function(){
50789         if(Roo.isGecko){// firefox overflow auto bug workaround
50790             this.bodyEl.clip();
50791             if(this.tabs) this.tabs.bodyEl.clip();
50792             if(this.activePanel){
50793                 this.activePanel.getEl().clip();
50794                 
50795                 if(this.activePanel.beforeSlide){
50796                     this.activePanel.beforeSlide();
50797                 }
50798             }
50799         }
50800     },
50801     
50802     afterSlide : function(){
50803         if(Roo.isGecko){// firefox overflow auto bug workaround
50804             this.bodyEl.unclip();
50805             if(this.tabs) this.tabs.bodyEl.unclip();
50806             if(this.activePanel){
50807                 this.activePanel.getEl().unclip();
50808                 if(this.activePanel.afterSlide){
50809                     this.activePanel.afterSlide();
50810                 }
50811             }
50812         }
50813     },
50814
50815     initAutoHide : function(){
50816         if(this.autoHide !== false){
50817             if(!this.autoHideHd){
50818                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50819                 this.autoHideHd = {
50820                     "mouseout": function(e){
50821                         if(!e.within(this.el, true)){
50822                             st.delay(500);
50823                         }
50824                     },
50825                     "mouseover" : function(e){
50826                         st.cancel();
50827                     },
50828                     scope : this
50829                 };
50830             }
50831             this.el.on(this.autoHideHd);
50832         }
50833     },
50834
50835     clearAutoHide : function(){
50836         if(this.autoHide !== false){
50837             this.el.un("mouseout", this.autoHideHd.mouseout);
50838             this.el.un("mouseover", this.autoHideHd.mouseover);
50839         }
50840     },
50841
50842     clearMonitor : function(){
50843         Roo.get(document).un("click", this.slideInIf, this);
50844     },
50845
50846     // these names are backwards but not changed for compat
50847     slideOut : function(){
50848         if(this.isSlid || this.el.hasActiveFx()){
50849             return;
50850         }
50851         this.isSlid = true;
50852         if(this.collapseBtn){
50853             this.collapseBtn.hide();
50854         }
50855         this.closeBtnState = this.closeBtn.getStyle('display');
50856         this.closeBtn.hide();
50857         if(this.stickBtn){
50858             this.stickBtn.show();
50859         }
50860         this.el.show();
50861         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50862         this.beforeSlide();
50863         this.el.setStyle("z-index", 10001);
50864         this.el.slideIn(this.getSlideAnchor(), {
50865             callback: function(){
50866                 this.afterSlide();
50867                 this.initAutoHide();
50868                 Roo.get(document).on("click", this.slideInIf, this);
50869                 this.fireEvent("slideshow", this);
50870             },
50871             scope: this,
50872             block: true
50873         });
50874     },
50875
50876     afterSlideIn : function(){
50877         this.clearAutoHide();
50878         this.isSlid = false;
50879         this.clearMonitor();
50880         this.el.setStyle("z-index", "");
50881         if(this.collapseBtn){
50882             this.collapseBtn.show();
50883         }
50884         this.closeBtn.setStyle('display', this.closeBtnState);
50885         if(this.stickBtn){
50886             this.stickBtn.hide();
50887         }
50888         this.fireEvent("slidehide", this);
50889     },
50890
50891     slideIn : function(cb){
50892         if(!this.isSlid || this.el.hasActiveFx()){
50893             Roo.callback(cb);
50894             return;
50895         }
50896         this.isSlid = false;
50897         this.beforeSlide();
50898         this.el.slideOut(this.getSlideAnchor(), {
50899             callback: function(){
50900                 this.el.setLeftTop(-10000, -10000);
50901                 this.afterSlide();
50902                 this.afterSlideIn();
50903                 Roo.callback(cb);
50904             },
50905             scope: this,
50906             block: true
50907         });
50908     },
50909     
50910     slideInIf : function(e){
50911         if(!e.within(this.el)){
50912             this.slideIn();
50913         }
50914     },
50915
50916     animateCollapse : function(){
50917         this.beforeSlide();
50918         this.el.setStyle("z-index", 20000);
50919         var anchor = this.getSlideAnchor();
50920         this.el.slideOut(anchor, {
50921             callback : function(){
50922                 this.el.setStyle("z-index", "");
50923                 this.collapsedEl.slideIn(anchor, {duration:.3});
50924                 this.afterSlide();
50925                 this.el.setLocation(-10000,-10000);
50926                 this.el.hide();
50927                 this.fireEvent("collapsed", this);
50928             },
50929             scope: this,
50930             block: true
50931         });
50932     },
50933
50934     animateExpand : function(){
50935         this.beforeSlide();
50936         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50937         this.el.setStyle("z-index", 20000);
50938         this.collapsedEl.hide({
50939             duration:.1
50940         });
50941         this.el.slideIn(this.getSlideAnchor(), {
50942             callback : function(){
50943                 this.el.setStyle("z-index", "");
50944                 this.afterSlide();
50945                 if(this.split){
50946                     this.split.el.show();
50947                 }
50948                 this.fireEvent("invalidated", this);
50949                 this.fireEvent("expanded", this);
50950             },
50951             scope: this,
50952             block: true
50953         });
50954     },
50955
50956     anchors : {
50957         "west" : "left",
50958         "east" : "right",
50959         "north" : "top",
50960         "south" : "bottom"
50961     },
50962
50963     sanchors : {
50964         "west" : "l",
50965         "east" : "r",
50966         "north" : "t",
50967         "south" : "b"
50968     },
50969
50970     canchors : {
50971         "west" : "tl-tr",
50972         "east" : "tr-tl",
50973         "north" : "tl-bl",
50974         "south" : "bl-tl"
50975     },
50976
50977     getAnchor : function(){
50978         return this.anchors[this.position];
50979     },
50980
50981     getCollapseAnchor : function(){
50982         return this.canchors[this.position];
50983     },
50984
50985     getSlideAnchor : function(){
50986         return this.sanchors[this.position];
50987     },
50988
50989     getAlignAdj : function(){
50990         var cm = this.cmargins;
50991         switch(this.position){
50992             case "west":
50993                 return [0, 0];
50994             break;
50995             case "east":
50996                 return [0, 0];
50997             break;
50998             case "north":
50999                 return [0, 0];
51000             break;
51001             case "south":
51002                 return [0, 0];
51003             break;
51004         }
51005     },
51006
51007     getExpandAdj : function(){
51008         var c = this.collapsedEl, cm = this.cmargins;
51009         switch(this.position){
51010             case "west":
51011                 return [-(cm.right+c.getWidth()+cm.left), 0];
51012             break;
51013             case "east":
51014                 return [cm.right+c.getWidth()+cm.left, 0];
51015             break;
51016             case "north":
51017                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51018             break;
51019             case "south":
51020                 return [0, cm.top+cm.bottom+c.getHeight()];
51021             break;
51022         }
51023     }
51024 });/*
51025  * Based on:
51026  * Ext JS Library 1.1.1
51027  * Copyright(c) 2006-2007, Ext JS, LLC.
51028  *
51029  * Originally Released Under LGPL - original licence link has changed is not relivant.
51030  *
51031  * Fork - LGPL
51032  * <script type="text/javascript">
51033  */
51034 /*
51035  * These classes are private internal classes
51036  */
51037 Roo.CenterLayoutRegion = function(mgr, config){
51038     Roo.LayoutRegion.call(this, mgr, config, "center");
51039     this.visible = true;
51040     this.minWidth = config.minWidth || 20;
51041     this.minHeight = config.minHeight || 20;
51042 };
51043
51044 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51045     hide : function(){
51046         // center panel can't be hidden
51047     },
51048     
51049     show : function(){
51050         // center panel can't be hidden
51051     },
51052     
51053     getMinWidth: function(){
51054         return this.minWidth;
51055     },
51056     
51057     getMinHeight: function(){
51058         return this.minHeight;
51059     }
51060 });
51061
51062
51063 Roo.NorthLayoutRegion = function(mgr, config){
51064     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51065     if(this.split){
51066         this.split.placement = Roo.SplitBar.TOP;
51067         this.split.orientation = Roo.SplitBar.VERTICAL;
51068         this.split.el.addClass("x-layout-split-v");
51069     }
51070     var size = config.initialSize || config.height;
51071     if(typeof size != "undefined"){
51072         this.el.setHeight(size);
51073     }
51074 };
51075 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51076     orientation: Roo.SplitBar.VERTICAL,
51077     getBox : function(){
51078         if(this.collapsed){
51079             return this.collapsedEl.getBox();
51080         }
51081         var box = this.el.getBox();
51082         if(this.split){
51083             box.height += this.split.el.getHeight();
51084         }
51085         return box;
51086     },
51087     
51088     updateBox : function(box){
51089         if(this.split && !this.collapsed){
51090             box.height -= this.split.el.getHeight();
51091             this.split.el.setLeft(box.x);
51092             this.split.el.setTop(box.y+box.height);
51093             this.split.el.setWidth(box.width);
51094         }
51095         if(this.collapsed){
51096             this.updateBody(box.width, null);
51097         }
51098         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51099     }
51100 });
51101
51102 Roo.SouthLayoutRegion = function(mgr, config){
51103     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51104     if(this.split){
51105         this.split.placement = Roo.SplitBar.BOTTOM;
51106         this.split.orientation = Roo.SplitBar.VERTICAL;
51107         this.split.el.addClass("x-layout-split-v");
51108     }
51109     var size = config.initialSize || config.height;
51110     if(typeof size != "undefined"){
51111         this.el.setHeight(size);
51112     }
51113 };
51114 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51115     orientation: Roo.SplitBar.VERTICAL,
51116     getBox : function(){
51117         if(this.collapsed){
51118             return this.collapsedEl.getBox();
51119         }
51120         var box = this.el.getBox();
51121         if(this.split){
51122             var sh = this.split.el.getHeight();
51123             box.height += sh;
51124             box.y -= sh;
51125         }
51126         return box;
51127     },
51128     
51129     updateBox : function(box){
51130         if(this.split && !this.collapsed){
51131             var sh = this.split.el.getHeight();
51132             box.height -= sh;
51133             box.y += sh;
51134             this.split.el.setLeft(box.x);
51135             this.split.el.setTop(box.y-sh);
51136             this.split.el.setWidth(box.width);
51137         }
51138         if(this.collapsed){
51139             this.updateBody(box.width, null);
51140         }
51141         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51142     }
51143 });
51144
51145 Roo.EastLayoutRegion = function(mgr, config){
51146     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51147     if(this.split){
51148         this.split.placement = Roo.SplitBar.RIGHT;
51149         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51150         this.split.el.addClass("x-layout-split-h");
51151     }
51152     var size = config.initialSize || config.width;
51153     if(typeof size != "undefined"){
51154         this.el.setWidth(size);
51155     }
51156 };
51157 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51158     orientation: Roo.SplitBar.HORIZONTAL,
51159     getBox : function(){
51160         if(this.collapsed){
51161             return this.collapsedEl.getBox();
51162         }
51163         var box = this.el.getBox();
51164         if(this.split){
51165             var sw = this.split.el.getWidth();
51166             box.width += sw;
51167             box.x -= sw;
51168         }
51169         return box;
51170     },
51171
51172     updateBox : function(box){
51173         if(this.split && !this.collapsed){
51174             var sw = this.split.el.getWidth();
51175             box.width -= sw;
51176             this.split.el.setLeft(box.x);
51177             this.split.el.setTop(box.y);
51178             this.split.el.setHeight(box.height);
51179             box.x += sw;
51180         }
51181         if(this.collapsed){
51182             this.updateBody(null, box.height);
51183         }
51184         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51185     }
51186 });
51187
51188 Roo.WestLayoutRegion = function(mgr, config){
51189     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51190     if(this.split){
51191         this.split.placement = Roo.SplitBar.LEFT;
51192         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51193         this.split.el.addClass("x-layout-split-h");
51194     }
51195     var size = config.initialSize || config.width;
51196     if(typeof size != "undefined"){
51197         this.el.setWidth(size);
51198     }
51199 };
51200 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51201     orientation: Roo.SplitBar.HORIZONTAL,
51202     getBox : function(){
51203         if(this.collapsed){
51204             return this.collapsedEl.getBox();
51205         }
51206         var box = this.el.getBox();
51207         if(this.split){
51208             box.width += this.split.el.getWidth();
51209         }
51210         return box;
51211     },
51212     
51213     updateBox : function(box){
51214         if(this.split && !this.collapsed){
51215             var sw = this.split.el.getWidth();
51216             box.width -= sw;
51217             this.split.el.setLeft(box.x+box.width);
51218             this.split.el.setTop(box.y);
51219             this.split.el.setHeight(box.height);
51220         }
51221         if(this.collapsed){
51222             this.updateBody(null, box.height);
51223         }
51224         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51225     }
51226 });
51227 /*
51228  * Based on:
51229  * Ext JS Library 1.1.1
51230  * Copyright(c) 2006-2007, Ext JS, LLC.
51231  *
51232  * Originally Released Under LGPL - original licence link has changed is not relivant.
51233  *
51234  * Fork - LGPL
51235  * <script type="text/javascript">
51236  */
51237  
51238  
51239 /*
51240  * Private internal class for reading and applying state
51241  */
51242 Roo.LayoutStateManager = function(layout){
51243      // default empty state
51244      this.state = {
51245         north: {},
51246         south: {},
51247         east: {},
51248         west: {}       
51249     };
51250 };
51251
51252 Roo.LayoutStateManager.prototype = {
51253     init : function(layout, provider){
51254         this.provider = provider;
51255         var state = provider.get(layout.id+"-layout-state");
51256         if(state){
51257             var wasUpdating = layout.isUpdating();
51258             if(!wasUpdating){
51259                 layout.beginUpdate();
51260             }
51261             for(var key in state){
51262                 if(typeof state[key] != "function"){
51263                     var rstate = state[key];
51264                     var r = layout.getRegion(key);
51265                     if(r && rstate){
51266                         if(rstate.size){
51267                             r.resizeTo(rstate.size);
51268                         }
51269                         if(rstate.collapsed == true){
51270                             r.collapse(true);
51271                         }else{
51272                             r.expand(null, true);
51273                         }
51274                     }
51275                 }
51276             }
51277             if(!wasUpdating){
51278                 layout.endUpdate();
51279             }
51280             this.state = state; 
51281         }
51282         this.layout = layout;
51283         layout.on("regionresized", this.onRegionResized, this);
51284         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51285         layout.on("regionexpanded", this.onRegionExpanded, this);
51286     },
51287     
51288     storeState : function(){
51289         this.provider.set(this.layout.id+"-layout-state", this.state);
51290     },
51291     
51292     onRegionResized : function(region, newSize){
51293         this.state[region.getPosition()].size = newSize;
51294         this.storeState();
51295     },
51296     
51297     onRegionCollapsed : function(region){
51298         this.state[region.getPosition()].collapsed = true;
51299         this.storeState();
51300     },
51301     
51302     onRegionExpanded : function(region){
51303         this.state[region.getPosition()].collapsed = false;
51304         this.storeState();
51305     }
51306 };/*
51307  * Based on:
51308  * Ext JS Library 1.1.1
51309  * Copyright(c) 2006-2007, Ext JS, LLC.
51310  *
51311  * Originally Released Under LGPL - original licence link has changed is not relivant.
51312  *
51313  * Fork - LGPL
51314  * <script type="text/javascript">
51315  */
51316 /**
51317  * @class Roo.ContentPanel
51318  * @extends Roo.util.Observable
51319  * A basic ContentPanel element.
51320  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51321  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51322  * @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
51323  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51324  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51325  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51326  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51327  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51328  * @cfg {String} title          The title for this panel
51329  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51330  * @cfg {String} url            Calls {@link #setUrl} with this value
51331  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51332  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51333  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51334  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51335
51336  * @constructor
51337  * Create a new ContentPanel.
51338  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51339  * @param {String/Object} config A string to set only the title or a config object
51340  * @param {String} content (optional) Set the HTML content for this panel
51341  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51342  */
51343 Roo.ContentPanel = function(el, config, content){
51344     
51345      
51346     /*
51347     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51348         config = el;
51349         el = Roo.id();
51350     }
51351     if (config && config.parentLayout) { 
51352         el = config.parentLayout.el.createChild(); 
51353     }
51354     */
51355     if(el.autoCreate){ // xtype is available if this is called from factory
51356         config = el;
51357         el = Roo.id();
51358     }
51359     this.el = Roo.get(el);
51360     if(!this.el && config && config.autoCreate){
51361         if(typeof config.autoCreate == "object"){
51362             if(!config.autoCreate.id){
51363                 config.autoCreate.id = config.id||el;
51364             }
51365             this.el = Roo.DomHelper.append(document.body,
51366                         config.autoCreate, true);
51367         }else{
51368             this.el = Roo.DomHelper.append(document.body,
51369                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51370         }
51371     }
51372     this.closable = false;
51373     this.loaded = false;
51374     this.active = false;
51375     if(typeof config == "string"){
51376         this.title = config;
51377     }else{
51378         Roo.apply(this, config);
51379     }
51380     
51381     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51382         this.wrapEl = this.el.wrap();
51383         this.toolbar.container = this.el.insertSibling(false, 'before');
51384         this.toolbar = new Roo.Toolbar(this.toolbar);
51385     }
51386     
51387     // xtype created footer. - not sure if will work as we normally have to render first..
51388     if (this.footer && !this.footer.el && this.footer.xtype) {
51389         if (!this.wrapEl) {
51390             this.wrapEl = this.el.wrap();
51391         }
51392     
51393         this.footer.container = this.wrapEl.createChild();
51394          
51395         this.footer = Roo.factory(this.footer, Roo);
51396         
51397     }
51398     
51399     if(this.resizeEl){
51400         this.resizeEl = Roo.get(this.resizeEl, true);
51401     }else{
51402         this.resizeEl = this.el;
51403     }
51404     // handle view.xtype
51405     
51406  
51407     
51408     
51409     this.addEvents({
51410         /**
51411          * @event activate
51412          * Fires when this panel is activated. 
51413          * @param {Roo.ContentPanel} this
51414          */
51415         "activate" : true,
51416         /**
51417          * @event deactivate
51418          * Fires when this panel is activated. 
51419          * @param {Roo.ContentPanel} this
51420          */
51421         "deactivate" : true,
51422
51423         /**
51424          * @event resize
51425          * Fires when this panel is resized if fitToFrame is true.
51426          * @param {Roo.ContentPanel} this
51427          * @param {Number} width The width after any component adjustments
51428          * @param {Number} height The height after any component adjustments
51429          */
51430         "resize" : true,
51431         
51432          /**
51433          * @event render
51434          * Fires when this tab is created
51435          * @param {Roo.ContentPanel} this
51436          */
51437         "render" : true
51438         
51439         
51440         
51441     });
51442     
51443
51444     
51445     
51446     if(this.autoScroll){
51447         this.resizeEl.setStyle("overflow", "auto");
51448     } else {
51449         // fix randome scrolling
51450         this.el.on('scroll', function() {
51451             Roo.log('fix random scolling');
51452             this.scrollTo('top',0); 
51453         });
51454     }
51455     content = content || this.content;
51456     if(content){
51457         this.setContent(content);
51458     }
51459     if(config && config.url){
51460         this.setUrl(this.url, this.params, this.loadOnce);
51461     }
51462     
51463     
51464     
51465     Roo.ContentPanel.superclass.constructor.call(this);
51466     
51467     if (this.view && typeof(this.view.xtype) != 'undefined') {
51468         this.view.el = this.el.appendChild(document.createElement("div"));
51469         this.view = Roo.factory(this.view); 
51470         this.view.render  &&  this.view.render(false, '');  
51471     }
51472     
51473     
51474     this.fireEvent('render', this);
51475 };
51476
51477 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51478     tabTip:'',
51479     setRegion : function(region){
51480         this.region = region;
51481         if(region){
51482            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51483         }else{
51484            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51485         } 
51486     },
51487     
51488     /**
51489      * Returns the toolbar for this Panel if one was configured. 
51490      * @return {Roo.Toolbar} 
51491      */
51492     getToolbar : function(){
51493         return this.toolbar;
51494     },
51495     
51496     setActiveState : function(active){
51497         this.active = active;
51498         if(!active){
51499             this.fireEvent("deactivate", this);
51500         }else{
51501             this.fireEvent("activate", this);
51502         }
51503     },
51504     /**
51505      * Updates this panel's element
51506      * @param {String} content The new content
51507      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51508     */
51509     setContent : function(content, loadScripts){
51510         this.el.update(content, loadScripts);
51511     },
51512
51513     ignoreResize : function(w, h){
51514         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51515             return true;
51516         }else{
51517             this.lastSize = {width: w, height: h};
51518             return false;
51519         }
51520     },
51521     /**
51522      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51523      * @return {Roo.UpdateManager} The UpdateManager
51524      */
51525     getUpdateManager : function(){
51526         return this.el.getUpdateManager();
51527     },
51528      /**
51529      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51530      * @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:
51531 <pre><code>
51532 panel.load({
51533     url: "your-url.php",
51534     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51535     callback: yourFunction,
51536     scope: yourObject, //(optional scope)
51537     discardUrl: false,
51538     nocache: false,
51539     text: "Loading...",
51540     timeout: 30,
51541     scripts: false
51542 });
51543 </code></pre>
51544      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51545      * 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.
51546      * @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}
51547      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51548      * @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.
51549      * @return {Roo.ContentPanel} this
51550      */
51551     load : function(){
51552         var um = this.el.getUpdateManager();
51553         um.update.apply(um, arguments);
51554         return this;
51555     },
51556
51557
51558     /**
51559      * 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.
51560      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51561      * @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)
51562      * @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)
51563      * @return {Roo.UpdateManager} The UpdateManager
51564      */
51565     setUrl : function(url, params, loadOnce){
51566         if(this.refreshDelegate){
51567             this.removeListener("activate", this.refreshDelegate);
51568         }
51569         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51570         this.on("activate", this.refreshDelegate);
51571         return this.el.getUpdateManager();
51572     },
51573     
51574     _handleRefresh : function(url, params, loadOnce){
51575         if(!loadOnce || !this.loaded){
51576             var updater = this.el.getUpdateManager();
51577             updater.update(url, params, this._setLoaded.createDelegate(this));
51578         }
51579     },
51580     
51581     _setLoaded : function(){
51582         this.loaded = true;
51583     }, 
51584     
51585     /**
51586      * Returns this panel's id
51587      * @return {String} 
51588      */
51589     getId : function(){
51590         return this.el.id;
51591     },
51592     
51593     /** 
51594      * Returns this panel's element - used by regiosn to add.
51595      * @return {Roo.Element} 
51596      */
51597     getEl : function(){
51598         return this.wrapEl || this.el;
51599     },
51600     
51601     adjustForComponents : function(width, height)
51602     {
51603         //Roo.log('adjustForComponents ');
51604         if(this.resizeEl != this.el){
51605             width -= this.el.getFrameWidth('lr');
51606             height -= this.el.getFrameWidth('tb');
51607         }
51608         if(this.toolbar){
51609             var te = this.toolbar.getEl();
51610             height -= te.getHeight();
51611             te.setWidth(width);
51612         }
51613         if(this.footer){
51614             var te = this.footer.getEl();
51615             Roo.log("footer:" + te.getHeight());
51616             
51617             height -= te.getHeight();
51618             te.setWidth(width);
51619         }
51620         
51621         
51622         if(this.adjustments){
51623             width += this.adjustments[0];
51624             height += this.adjustments[1];
51625         }
51626         return {"width": width, "height": height};
51627     },
51628     
51629     setSize : function(width, height){
51630         if(this.fitToFrame && !this.ignoreResize(width, height)){
51631             if(this.fitContainer && this.resizeEl != this.el){
51632                 this.el.setSize(width, height);
51633             }
51634             var size = this.adjustForComponents(width, height);
51635             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51636             this.fireEvent('resize', this, size.width, size.height);
51637         }
51638     },
51639     
51640     /**
51641      * Returns this panel's title
51642      * @return {String} 
51643      */
51644     getTitle : function(){
51645         return this.title;
51646     },
51647     
51648     /**
51649      * Set this panel's title
51650      * @param {String} title
51651      */
51652     setTitle : function(title){
51653         this.title = title;
51654         if(this.region){
51655             this.region.updatePanelTitle(this, title);
51656         }
51657     },
51658     
51659     /**
51660      * Returns true is this panel was configured to be closable
51661      * @return {Boolean} 
51662      */
51663     isClosable : function(){
51664         return this.closable;
51665     },
51666     
51667     beforeSlide : function(){
51668         this.el.clip();
51669         this.resizeEl.clip();
51670     },
51671     
51672     afterSlide : function(){
51673         this.el.unclip();
51674         this.resizeEl.unclip();
51675     },
51676     
51677     /**
51678      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51679      *   Will fail silently if the {@link #setUrl} method has not been called.
51680      *   This does not activate the panel, just updates its content.
51681      */
51682     refresh : function(){
51683         if(this.refreshDelegate){
51684            this.loaded = false;
51685            this.refreshDelegate();
51686         }
51687     },
51688     
51689     /**
51690      * Destroys this panel
51691      */
51692     destroy : function(){
51693         this.el.removeAllListeners();
51694         var tempEl = document.createElement("span");
51695         tempEl.appendChild(this.el.dom);
51696         tempEl.innerHTML = "";
51697         this.el.remove();
51698         this.el = null;
51699     },
51700     
51701     /**
51702      * form - if the content panel contains a form - this is a reference to it.
51703      * @type {Roo.form.Form}
51704      */
51705     form : false,
51706     /**
51707      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51708      *    This contains a reference to it.
51709      * @type {Roo.View}
51710      */
51711     view : false,
51712     
51713       /**
51714      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51715      * <pre><code>
51716
51717 layout.addxtype({
51718        xtype : 'Form',
51719        items: [ .... ]
51720    }
51721 );
51722
51723 </code></pre>
51724      * @param {Object} cfg Xtype definition of item to add.
51725      */
51726     
51727     addxtype : function(cfg) {
51728         // add form..
51729         if (cfg.xtype.match(/^Form$/)) {
51730             
51731             var el;
51732             //if (this.footer) {
51733             //    el = this.footer.container.insertSibling(false, 'before');
51734             //} else {
51735                 el = this.el.createChild();
51736             //}
51737
51738             this.form = new  Roo.form.Form(cfg);
51739             
51740             
51741             if ( this.form.allItems.length) this.form.render(el.dom);
51742             return this.form;
51743         }
51744         // should only have one of theses..
51745         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51746             // views.. should not be just added - used named prop 'view''
51747             
51748             cfg.el = this.el.appendChild(document.createElement("div"));
51749             // factory?
51750             
51751             var ret = new Roo.factory(cfg);
51752              
51753              ret.render && ret.render(false, ''); // render blank..
51754             this.view = ret;
51755             return ret;
51756         }
51757         return false;
51758     }
51759 });
51760
51761 /**
51762  * @class Roo.GridPanel
51763  * @extends Roo.ContentPanel
51764  * @constructor
51765  * Create a new GridPanel.
51766  * @param {Roo.grid.Grid} grid The grid for this panel
51767  * @param {String/Object} config A string to set only the panel's title, or a config object
51768  */
51769 Roo.GridPanel = function(grid, config){
51770     
51771   
51772     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51773         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51774         
51775     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51776     
51777     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51778     
51779     if(this.toolbar){
51780         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51781     }
51782     // xtype created footer. - not sure if will work as we normally have to render first..
51783     if (this.footer && !this.footer.el && this.footer.xtype) {
51784         
51785         this.footer.container = this.grid.getView().getFooterPanel(true);
51786         this.footer.dataSource = this.grid.dataSource;
51787         this.footer = Roo.factory(this.footer, Roo);
51788         
51789     }
51790     
51791     grid.monitorWindowResize = false; // turn off autosizing
51792     grid.autoHeight = false;
51793     grid.autoWidth = false;
51794     this.grid = grid;
51795     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51796 };
51797
51798 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51799     getId : function(){
51800         return this.grid.id;
51801     },
51802     
51803     /**
51804      * Returns the grid for this panel
51805      * @return {Roo.grid.Grid} 
51806      */
51807     getGrid : function(){
51808         return this.grid;    
51809     },
51810     
51811     setSize : function(width, height){
51812         if(!this.ignoreResize(width, height)){
51813             var grid = this.grid;
51814             var size = this.adjustForComponents(width, height);
51815             grid.getGridEl().setSize(size.width, size.height);
51816             grid.autoSize();
51817         }
51818     },
51819     
51820     beforeSlide : function(){
51821         this.grid.getView().scroller.clip();
51822     },
51823     
51824     afterSlide : function(){
51825         this.grid.getView().scroller.unclip();
51826     },
51827     
51828     destroy : function(){
51829         this.grid.destroy();
51830         delete this.grid;
51831         Roo.GridPanel.superclass.destroy.call(this); 
51832     }
51833 });
51834
51835
51836 /**
51837  * @class Roo.NestedLayoutPanel
51838  * @extends Roo.ContentPanel
51839  * @constructor
51840  * Create a new NestedLayoutPanel.
51841  * 
51842  * 
51843  * @param {Roo.BorderLayout} layout The layout for this panel
51844  * @param {String/Object} config A string to set only the title or a config object
51845  */
51846 Roo.NestedLayoutPanel = function(layout, config)
51847 {
51848     // construct with only one argument..
51849     /* FIXME - implement nicer consturctors
51850     if (layout.layout) {
51851         config = layout;
51852         layout = config.layout;
51853         delete config.layout;
51854     }
51855     if (layout.xtype && !layout.getEl) {
51856         // then layout needs constructing..
51857         layout = Roo.factory(layout, Roo);
51858     }
51859     */
51860     
51861     
51862     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51863     
51864     layout.monitorWindowResize = false; // turn off autosizing
51865     this.layout = layout;
51866     this.layout.getEl().addClass("x-layout-nested-layout");
51867     
51868     
51869     
51870     
51871 };
51872
51873 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51874
51875     setSize : function(width, height){
51876         if(!this.ignoreResize(width, height)){
51877             var size = this.adjustForComponents(width, height);
51878             var el = this.layout.getEl();
51879             el.setSize(size.width, size.height);
51880             var touch = el.dom.offsetWidth;
51881             this.layout.layout();
51882             // ie requires a double layout on the first pass
51883             if(Roo.isIE && !this.initialized){
51884                 this.initialized = true;
51885                 this.layout.layout();
51886             }
51887         }
51888     },
51889     
51890     // activate all subpanels if not currently active..
51891     
51892     setActiveState : function(active){
51893         this.active = active;
51894         if(!active){
51895             this.fireEvent("deactivate", this);
51896             return;
51897         }
51898         
51899         this.fireEvent("activate", this);
51900         // not sure if this should happen before or after..
51901         if (!this.layout) {
51902             return; // should not happen..
51903         }
51904         var reg = false;
51905         for (var r in this.layout.regions) {
51906             reg = this.layout.getRegion(r);
51907             if (reg.getActivePanel()) {
51908                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51909                 reg.setActivePanel(reg.getActivePanel());
51910                 continue;
51911             }
51912             if (!reg.panels.length) {
51913                 continue;
51914             }
51915             reg.showPanel(reg.getPanel(0));
51916         }
51917         
51918         
51919         
51920         
51921     },
51922     
51923     /**
51924      * Returns the nested BorderLayout for this panel
51925      * @return {Roo.BorderLayout} 
51926      */
51927     getLayout : function(){
51928         return this.layout;
51929     },
51930     
51931      /**
51932      * Adds a xtype elements to the layout of the nested panel
51933      * <pre><code>
51934
51935 panel.addxtype({
51936        xtype : 'ContentPanel',
51937        region: 'west',
51938        items: [ .... ]
51939    }
51940 );
51941
51942 panel.addxtype({
51943         xtype : 'NestedLayoutPanel',
51944         region: 'west',
51945         layout: {
51946            center: { },
51947            west: { }   
51948         },
51949         items : [ ... list of content panels or nested layout panels.. ]
51950    }
51951 );
51952 </code></pre>
51953      * @param {Object} cfg Xtype definition of item to add.
51954      */
51955     addxtype : function(cfg) {
51956         return this.layout.addxtype(cfg);
51957     
51958     }
51959 });
51960
51961 Roo.ScrollPanel = function(el, config, content){
51962     config = config || {};
51963     config.fitToFrame = true;
51964     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51965     
51966     this.el.dom.style.overflow = "hidden";
51967     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51968     this.el.removeClass("x-layout-inactive-content");
51969     this.el.on("mousewheel", this.onWheel, this);
51970
51971     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51972     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51973     up.unselectable(); down.unselectable();
51974     up.on("click", this.scrollUp, this);
51975     down.on("click", this.scrollDown, this);
51976     up.addClassOnOver("x-scroller-btn-over");
51977     down.addClassOnOver("x-scroller-btn-over");
51978     up.addClassOnClick("x-scroller-btn-click");
51979     down.addClassOnClick("x-scroller-btn-click");
51980     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51981
51982     this.resizeEl = this.el;
51983     this.el = wrap; this.up = up; this.down = down;
51984 };
51985
51986 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51987     increment : 100,
51988     wheelIncrement : 5,
51989     scrollUp : function(){
51990         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51991     },
51992
51993     scrollDown : function(){
51994         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51995     },
51996
51997     afterScroll : function(){
51998         var el = this.resizeEl;
51999         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52000         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52001         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52002     },
52003
52004     setSize : function(){
52005         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52006         this.afterScroll();
52007     },
52008
52009     onWheel : function(e){
52010         var d = e.getWheelDelta();
52011         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52012         this.afterScroll();
52013         e.stopEvent();
52014     },
52015
52016     setContent : function(content, loadScripts){
52017         this.resizeEl.update(content, loadScripts);
52018     }
52019
52020 });
52021
52022
52023
52024
52025
52026
52027
52028
52029
52030 /**
52031  * @class Roo.TreePanel
52032  * @extends Roo.ContentPanel
52033  * @constructor
52034  * Create a new TreePanel. - defaults to fit/scoll contents.
52035  * @param {String/Object} config A string to set only the panel's title, or a config object
52036  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52037  */
52038 Roo.TreePanel = function(config){
52039     var el = config.el;
52040     var tree = config.tree;
52041     delete config.tree; 
52042     delete config.el; // hopefull!
52043     
52044     // wrapper for IE7 strict & safari scroll issue
52045     
52046     var treeEl = el.createChild();
52047     config.resizeEl = treeEl;
52048     
52049     
52050     
52051     Roo.TreePanel.superclass.constructor.call(this, el, config);
52052  
52053  
52054     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52055     //console.log(tree);
52056     this.on('activate', function()
52057     {
52058         if (this.tree.rendered) {
52059             return;
52060         }
52061         //console.log('render tree');
52062         this.tree.render();
52063     });
52064     // this should not be needed.. - it's actually the 'el' that resizes?
52065     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52066     
52067     //this.on('resize',  function (cp, w, h) {
52068     //        this.tree.innerCt.setWidth(w);
52069     //        this.tree.innerCt.setHeight(h);
52070     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52071     //});
52072
52073         
52074     
52075 };
52076
52077 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52078     fitToFrame : true,
52079     autoScroll : true
52080 });
52081
52082
52083
52084
52085
52086
52087
52088
52089
52090
52091
52092 /*
52093  * Based on:
52094  * Ext JS Library 1.1.1
52095  * Copyright(c) 2006-2007, Ext JS, LLC.
52096  *
52097  * Originally Released Under LGPL - original licence link has changed is not relivant.
52098  *
52099  * Fork - LGPL
52100  * <script type="text/javascript">
52101  */
52102  
52103
52104 /**
52105  * @class Roo.ReaderLayout
52106  * @extends Roo.BorderLayout
52107  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52108  * center region containing two nested regions (a top one for a list view and one for item preview below),
52109  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52110  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52111  * expedites the setup of the overall layout and regions for this common application style.
52112  * Example:
52113  <pre><code>
52114 var reader = new Roo.ReaderLayout();
52115 var CP = Roo.ContentPanel;  // shortcut for adding
52116
52117 reader.beginUpdate();
52118 reader.add("north", new CP("north", "North"));
52119 reader.add("west", new CP("west", {title: "West"}));
52120 reader.add("east", new CP("east", {title: "East"}));
52121
52122 reader.regions.listView.add(new CP("listView", "List"));
52123 reader.regions.preview.add(new CP("preview", "Preview"));
52124 reader.endUpdate();
52125 </code></pre>
52126 * @constructor
52127 * Create a new ReaderLayout
52128 * @param {Object} config Configuration options
52129 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52130 * document.body if omitted)
52131 */
52132 Roo.ReaderLayout = function(config, renderTo){
52133     var c = config || {size:{}};
52134     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52135         north: c.north !== false ? Roo.apply({
52136             split:false,
52137             initialSize: 32,
52138             titlebar: false
52139         }, c.north) : false,
52140         west: c.west !== false ? Roo.apply({
52141             split:true,
52142             initialSize: 200,
52143             minSize: 175,
52144             maxSize: 400,
52145             titlebar: true,
52146             collapsible: true,
52147             animate: true,
52148             margins:{left:5,right:0,bottom:5,top:5},
52149             cmargins:{left:5,right:5,bottom:5,top:5}
52150         }, c.west) : false,
52151         east: c.east !== false ? Roo.apply({
52152             split:true,
52153             initialSize: 200,
52154             minSize: 175,
52155             maxSize: 400,
52156             titlebar: true,
52157             collapsible: true,
52158             animate: true,
52159             margins:{left:0,right:5,bottom:5,top:5},
52160             cmargins:{left:5,right:5,bottom:5,top:5}
52161         }, c.east) : false,
52162         center: Roo.apply({
52163             tabPosition: 'top',
52164             autoScroll:false,
52165             closeOnTab: true,
52166             titlebar:false,
52167             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52168         }, c.center)
52169     });
52170
52171     this.el.addClass('x-reader');
52172
52173     this.beginUpdate();
52174
52175     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52176         south: c.preview !== false ? Roo.apply({
52177             split:true,
52178             initialSize: 200,
52179             minSize: 100,
52180             autoScroll:true,
52181             collapsible:true,
52182             titlebar: true,
52183             cmargins:{top:5,left:0, right:0, bottom:0}
52184         }, c.preview) : false,
52185         center: Roo.apply({
52186             autoScroll:false,
52187             titlebar:false,
52188             minHeight:200
52189         }, c.listView)
52190     });
52191     this.add('center', new Roo.NestedLayoutPanel(inner,
52192             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52193
52194     this.endUpdate();
52195
52196     this.regions.preview = inner.getRegion('south');
52197     this.regions.listView = inner.getRegion('center');
52198 };
52199
52200 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52201  * Based on:
52202  * Ext JS Library 1.1.1
52203  * Copyright(c) 2006-2007, Ext JS, LLC.
52204  *
52205  * Originally Released Under LGPL - original licence link has changed is not relivant.
52206  *
52207  * Fork - LGPL
52208  * <script type="text/javascript">
52209  */
52210  
52211 /**
52212  * @class Roo.grid.Grid
52213  * @extends Roo.util.Observable
52214  * This class represents the primary interface of a component based grid control.
52215  * <br><br>Usage:<pre><code>
52216  var grid = new Roo.grid.Grid("my-container-id", {
52217      ds: myDataStore,
52218      cm: myColModel,
52219      selModel: mySelectionModel,
52220      autoSizeColumns: true,
52221      monitorWindowResize: false,
52222      trackMouseOver: true
52223  });
52224  // set any options
52225  grid.render();
52226  * </code></pre>
52227  * <b>Common Problems:</b><br/>
52228  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52229  * element will correct this<br/>
52230  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52231  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52232  * are unpredictable.<br/>
52233  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52234  * grid to calculate dimensions/offsets.<br/>
52235   * @constructor
52236  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52237  * The container MUST have some type of size defined for the grid to fill. The container will be
52238  * automatically set to position relative if it isn't already.
52239  * @param {Object} config A config object that sets properties on this grid.
52240  */
52241 Roo.grid.Grid = function(container, config){
52242         // initialize the container
52243         this.container = Roo.get(container);
52244         this.container.update("");
52245         this.container.setStyle("overflow", "hidden");
52246     this.container.addClass('x-grid-container');
52247
52248     this.id = this.container.id;
52249
52250     Roo.apply(this, config);
52251     // check and correct shorthanded configs
52252     if(this.ds){
52253         this.dataSource = this.ds;
52254         delete this.ds;
52255     }
52256     if(this.cm){
52257         this.colModel = this.cm;
52258         delete this.cm;
52259     }
52260     if(this.sm){
52261         this.selModel = this.sm;
52262         delete this.sm;
52263     }
52264
52265     if (this.selModel) {
52266         this.selModel = Roo.factory(this.selModel, Roo.grid);
52267         this.sm = this.selModel;
52268         this.sm.xmodule = this.xmodule || false;
52269     }
52270     if (typeof(this.colModel.config) == 'undefined') {
52271         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52272         this.cm = this.colModel;
52273         this.cm.xmodule = this.xmodule || false;
52274     }
52275     if (this.dataSource) {
52276         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52277         this.ds = this.dataSource;
52278         this.ds.xmodule = this.xmodule || false;
52279          
52280     }
52281     
52282     
52283     
52284     if(this.width){
52285         this.container.setWidth(this.width);
52286     }
52287
52288     if(this.height){
52289         this.container.setHeight(this.height);
52290     }
52291     /** @private */
52292         this.addEvents({
52293         // raw events
52294         /**
52295          * @event click
52296          * The raw click event for the entire grid.
52297          * @param {Roo.EventObject} e
52298          */
52299         "click" : true,
52300         /**
52301          * @event dblclick
52302          * The raw dblclick event for the entire grid.
52303          * @param {Roo.EventObject} e
52304          */
52305         "dblclick" : true,
52306         /**
52307          * @event contextmenu
52308          * The raw contextmenu event for the entire grid.
52309          * @param {Roo.EventObject} e
52310          */
52311         "contextmenu" : true,
52312         /**
52313          * @event mousedown
52314          * The raw mousedown event for the entire grid.
52315          * @param {Roo.EventObject} e
52316          */
52317         "mousedown" : true,
52318         /**
52319          * @event mouseup
52320          * The raw mouseup event for the entire grid.
52321          * @param {Roo.EventObject} e
52322          */
52323         "mouseup" : true,
52324         /**
52325          * @event mouseover
52326          * The raw mouseover event for the entire grid.
52327          * @param {Roo.EventObject} e
52328          */
52329         "mouseover" : true,
52330         /**
52331          * @event mouseout
52332          * The raw mouseout event for the entire grid.
52333          * @param {Roo.EventObject} e
52334          */
52335         "mouseout" : true,
52336         /**
52337          * @event keypress
52338          * The raw keypress event for the entire grid.
52339          * @param {Roo.EventObject} e
52340          */
52341         "keypress" : true,
52342         /**
52343          * @event keydown
52344          * The raw keydown event for the entire grid.
52345          * @param {Roo.EventObject} e
52346          */
52347         "keydown" : true,
52348
52349         // custom events
52350
52351         /**
52352          * @event cellclick
52353          * Fires when a cell is clicked
52354          * @param {Grid} this
52355          * @param {Number} rowIndex
52356          * @param {Number} columnIndex
52357          * @param {Roo.EventObject} e
52358          */
52359         "cellclick" : true,
52360         /**
52361          * @event celldblclick
52362          * Fires when a cell is double clicked
52363          * @param {Grid} this
52364          * @param {Number} rowIndex
52365          * @param {Number} columnIndex
52366          * @param {Roo.EventObject} e
52367          */
52368         "celldblclick" : true,
52369         /**
52370          * @event rowclick
52371          * Fires when a row is clicked
52372          * @param {Grid} this
52373          * @param {Number} rowIndex
52374          * @param {Roo.EventObject} e
52375          */
52376         "rowclick" : true,
52377         /**
52378          * @event rowdblclick
52379          * Fires when a row is double clicked
52380          * @param {Grid} this
52381          * @param {Number} rowIndex
52382          * @param {Roo.EventObject} e
52383          */
52384         "rowdblclick" : true,
52385         /**
52386          * @event headerclick
52387          * Fires when a header is clicked
52388          * @param {Grid} this
52389          * @param {Number} columnIndex
52390          * @param {Roo.EventObject} e
52391          */
52392         "headerclick" : true,
52393         /**
52394          * @event headerdblclick
52395          * Fires when a header cell is double clicked
52396          * @param {Grid} this
52397          * @param {Number} columnIndex
52398          * @param {Roo.EventObject} e
52399          */
52400         "headerdblclick" : true,
52401         /**
52402          * @event rowcontextmenu
52403          * Fires when a row is right clicked
52404          * @param {Grid} this
52405          * @param {Number} rowIndex
52406          * @param {Roo.EventObject} e
52407          */
52408         "rowcontextmenu" : true,
52409         /**
52410          * @event cellcontextmenu
52411          * Fires when a cell is right clicked
52412          * @param {Grid} this
52413          * @param {Number} rowIndex
52414          * @param {Number} cellIndex
52415          * @param {Roo.EventObject} e
52416          */
52417          "cellcontextmenu" : true,
52418         /**
52419          * @event headercontextmenu
52420          * Fires when a header is right clicked
52421          * @param {Grid} this
52422          * @param {Number} columnIndex
52423          * @param {Roo.EventObject} e
52424          */
52425         "headercontextmenu" : true,
52426         /**
52427          * @event bodyscroll
52428          * Fires when the body element is scrolled
52429          * @param {Number} scrollLeft
52430          * @param {Number} scrollTop
52431          */
52432         "bodyscroll" : true,
52433         /**
52434          * @event columnresize
52435          * Fires when the user resizes a column
52436          * @param {Number} columnIndex
52437          * @param {Number} newSize
52438          */
52439         "columnresize" : true,
52440         /**
52441          * @event columnmove
52442          * Fires when the user moves a column
52443          * @param {Number} oldIndex
52444          * @param {Number} newIndex
52445          */
52446         "columnmove" : true,
52447         /**
52448          * @event startdrag
52449          * Fires when row(s) start being dragged
52450          * @param {Grid} this
52451          * @param {Roo.GridDD} dd The drag drop object
52452          * @param {event} e The raw browser event
52453          */
52454         "startdrag" : true,
52455         /**
52456          * @event enddrag
52457          * Fires when a drag operation is complete
52458          * @param {Grid} this
52459          * @param {Roo.GridDD} dd The drag drop object
52460          * @param {event} e The raw browser event
52461          */
52462         "enddrag" : true,
52463         /**
52464          * @event dragdrop
52465          * Fires when dragged row(s) are dropped on a valid DD target
52466          * @param {Grid} this
52467          * @param {Roo.GridDD} dd The drag drop object
52468          * @param {String} targetId The target drag drop object
52469          * @param {event} e The raw browser event
52470          */
52471         "dragdrop" : true,
52472         /**
52473          * @event dragover
52474          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52475          * @param {Grid} this
52476          * @param {Roo.GridDD} dd The drag drop object
52477          * @param {String} targetId The target drag drop object
52478          * @param {event} e The raw browser event
52479          */
52480         "dragover" : true,
52481         /**
52482          * @event dragenter
52483          *  Fires when the dragged row(s) first cross another DD target while being dragged
52484          * @param {Grid} this
52485          * @param {Roo.GridDD} dd The drag drop object
52486          * @param {String} targetId The target drag drop object
52487          * @param {event} e The raw browser event
52488          */
52489         "dragenter" : true,
52490         /**
52491          * @event dragout
52492          * Fires when the dragged row(s) leave another DD target while being dragged
52493          * @param {Grid} this
52494          * @param {Roo.GridDD} dd The drag drop object
52495          * @param {String} targetId The target drag drop object
52496          * @param {event} e The raw browser event
52497          */
52498         "dragout" : true,
52499         /**
52500          * @event rowclass
52501          * Fires when a row is rendered, so you can change add a style to it.
52502          * @param {GridView} gridview   The grid view
52503          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52504          */
52505         'rowclass' : true,
52506
52507         /**
52508          * @event render
52509          * Fires when the grid is rendered
52510          * @param {Grid} grid
52511          */
52512         'render' : true
52513     });
52514
52515     Roo.grid.Grid.superclass.constructor.call(this);
52516 };
52517 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52518     
52519     /**
52520      * @cfg {String} ddGroup - drag drop group.
52521      */
52522
52523     /**
52524      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52525      */
52526     minColumnWidth : 25,
52527
52528     /**
52529      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52530      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52531      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52532      */
52533     autoSizeColumns : false,
52534
52535     /**
52536      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52537      */
52538     autoSizeHeaders : true,
52539
52540     /**
52541      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52542      */
52543     monitorWindowResize : true,
52544
52545     /**
52546      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52547      * rows measured to get a columns size. Default is 0 (all rows).
52548      */
52549     maxRowsToMeasure : 0,
52550
52551     /**
52552      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52553      */
52554     trackMouseOver : true,
52555
52556     /**
52557     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52558     */
52559     
52560     /**
52561     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52562     */
52563     enableDragDrop : false,
52564     
52565     /**
52566     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52567     */
52568     enableColumnMove : true,
52569     
52570     /**
52571     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52572     */
52573     enableColumnHide : true,
52574     
52575     /**
52576     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52577     */
52578     enableRowHeightSync : false,
52579     
52580     /**
52581     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52582     */
52583     stripeRows : true,
52584     
52585     /**
52586     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52587     */
52588     autoHeight : false,
52589
52590     /**
52591      * @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.
52592      */
52593     autoExpandColumn : false,
52594
52595     /**
52596     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52597     * Default is 50.
52598     */
52599     autoExpandMin : 50,
52600
52601     /**
52602     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52603     */
52604     autoExpandMax : 1000,
52605
52606     /**
52607     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52608     */
52609     view : null,
52610
52611     /**
52612     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52613     */
52614     loadMask : false,
52615     /**
52616     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52617     */
52618     dropTarget: false,
52619     
52620    
52621     
52622     // private
52623     rendered : false,
52624
52625     /**
52626     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52627     * of a fixed width. Default is false.
52628     */
52629     /**
52630     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52631     */
52632     /**
52633      * Called once after all setup has been completed and the grid is ready to be rendered.
52634      * @return {Roo.grid.Grid} this
52635      */
52636     render : function()
52637     {
52638         var c = this.container;
52639         // try to detect autoHeight/width mode
52640         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52641             this.autoHeight = true;
52642         }
52643         var view = this.getView();
52644         view.init(this);
52645
52646         c.on("click", this.onClick, this);
52647         c.on("dblclick", this.onDblClick, this);
52648         c.on("contextmenu", this.onContextMenu, this);
52649         c.on("keydown", this.onKeyDown, this);
52650         if (Roo.isTouch) {
52651             c.on("touchstart", this.onTouchStart, this);
52652         }
52653
52654         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52655
52656         this.getSelectionModel().init(this);
52657
52658         view.render();
52659
52660         if(this.loadMask){
52661             this.loadMask = new Roo.LoadMask(this.container,
52662                     Roo.apply({store:this.dataSource}, this.loadMask));
52663         }
52664         
52665         
52666         if (this.toolbar && this.toolbar.xtype) {
52667             this.toolbar.container = this.getView().getHeaderPanel(true);
52668             this.toolbar = new Roo.Toolbar(this.toolbar);
52669         }
52670         if (this.footer && this.footer.xtype) {
52671             this.footer.dataSource = this.getDataSource();
52672             this.footer.container = this.getView().getFooterPanel(true);
52673             this.footer = Roo.factory(this.footer, Roo);
52674         }
52675         if (this.dropTarget && this.dropTarget.xtype) {
52676             delete this.dropTarget.xtype;
52677             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52678         }
52679         
52680         
52681         this.rendered = true;
52682         this.fireEvent('render', this);
52683         return this;
52684     },
52685
52686         /**
52687          * Reconfigures the grid to use a different Store and Column Model.
52688          * The View will be bound to the new objects and refreshed.
52689          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52690          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52691          */
52692     reconfigure : function(dataSource, colModel){
52693         if(this.loadMask){
52694             this.loadMask.destroy();
52695             this.loadMask = new Roo.LoadMask(this.container,
52696                     Roo.apply({store:dataSource}, this.loadMask));
52697         }
52698         this.view.bind(dataSource, colModel);
52699         this.dataSource = dataSource;
52700         this.colModel = colModel;
52701         this.view.refresh(true);
52702     },
52703
52704     // private
52705     onKeyDown : function(e){
52706         this.fireEvent("keydown", e);
52707     },
52708
52709     /**
52710      * Destroy this grid.
52711      * @param {Boolean} removeEl True to remove the element
52712      */
52713     destroy : function(removeEl, keepListeners){
52714         if(this.loadMask){
52715             this.loadMask.destroy();
52716         }
52717         var c = this.container;
52718         c.removeAllListeners();
52719         this.view.destroy();
52720         this.colModel.purgeListeners();
52721         if(!keepListeners){
52722             this.purgeListeners();
52723         }
52724         c.update("");
52725         if(removeEl === true){
52726             c.remove();
52727         }
52728     },
52729
52730     // private
52731     processEvent : function(name, e){
52732         // does this fire select???
52733         //Roo.log('grid:processEvent '  + name);
52734         
52735         if (name != 'touchstart' ) {
52736             this.fireEvent(name, e);    
52737         }
52738         
52739         var t = e.getTarget();
52740         var v = this.view;
52741         var header = v.findHeaderIndex(t);
52742         if(header !== false){
52743             var ename = name == 'touchstart' ? 'click' : name;
52744              
52745             this.fireEvent("header" + ename, this, header, e);
52746         }else{
52747             var row = v.findRowIndex(t);
52748             var cell = v.findCellIndex(t);
52749             if (name == 'touchstart') {
52750                 // first touch is always a click.
52751                 // hopefull this happens after selection is updated.?
52752                 name = false;
52753                 
52754                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52755                     var cs = this.selModel.getSelectedCell();
52756                     if (row == cs[0] && cell == cs[1]){
52757                         name = 'dblclick';
52758                     }
52759                 }
52760                 if (typeof(this.selModel.getSelections) != 'undefined') {
52761                     var cs = this.selModel.getSelections();
52762                     var ds = this.dataSource;
52763                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52764                         name = 'dblclick';
52765                     }
52766                 }
52767                 if (!name) {
52768                     return;
52769                 }
52770             }
52771             
52772             
52773             if(row !== false){
52774                 this.fireEvent("row" + name, this, row, e);
52775                 if(cell !== false){
52776                     this.fireEvent("cell" + name, this, row, cell, e);
52777                 }
52778             }
52779         }
52780     },
52781
52782     // private
52783     onClick : function(e){
52784         this.processEvent("click", e);
52785     },
52786    // private
52787     onTouchStart : function(e){
52788         this.processEvent("touchstart", e);
52789     },
52790
52791     // private
52792     onContextMenu : function(e, t){
52793         this.processEvent("contextmenu", e);
52794     },
52795
52796     // private
52797     onDblClick : function(e){
52798         this.processEvent("dblclick", e);
52799     },
52800
52801     // private
52802     walkCells : function(row, col, step, fn, scope){
52803         var cm = this.colModel, clen = cm.getColumnCount();
52804         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52805         if(step < 0){
52806             if(col < 0){
52807                 row--;
52808                 first = false;
52809             }
52810             while(row >= 0){
52811                 if(!first){
52812                     col = clen-1;
52813                 }
52814                 first = false;
52815                 while(col >= 0){
52816                     if(fn.call(scope || this, row, col, cm) === true){
52817                         return [row, col];
52818                     }
52819                     col--;
52820                 }
52821                 row--;
52822             }
52823         } else {
52824             if(col >= clen){
52825                 row++;
52826                 first = false;
52827             }
52828             while(row < rlen){
52829                 if(!first){
52830                     col = 0;
52831                 }
52832                 first = false;
52833                 while(col < clen){
52834                     if(fn.call(scope || this, row, col, cm) === true){
52835                         return [row, col];
52836                     }
52837                     col++;
52838                 }
52839                 row++;
52840             }
52841         }
52842         return null;
52843     },
52844
52845     // private
52846     getSelections : function(){
52847         return this.selModel.getSelections();
52848     },
52849
52850     /**
52851      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52852      * but if manual update is required this method will initiate it.
52853      */
52854     autoSize : function(){
52855         if(this.rendered){
52856             this.view.layout();
52857             if(this.view.adjustForScroll){
52858                 this.view.adjustForScroll();
52859             }
52860         }
52861     },
52862
52863     /**
52864      * Returns the grid's underlying element.
52865      * @return {Element} The element
52866      */
52867     getGridEl : function(){
52868         return this.container;
52869     },
52870
52871     // private for compatibility, overridden by editor grid
52872     stopEditing : function(){},
52873
52874     /**
52875      * Returns the grid's SelectionModel.
52876      * @return {SelectionModel}
52877      */
52878     getSelectionModel : function(){
52879         if(!this.selModel){
52880             this.selModel = new Roo.grid.RowSelectionModel();
52881         }
52882         return this.selModel;
52883     },
52884
52885     /**
52886      * Returns the grid's DataSource.
52887      * @return {DataSource}
52888      */
52889     getDataSource : function(){
52890         return this.dataSource;
52891     },
52892
52893     /**
52894      * Returns the grid's ColumnModel.
52895      * @return {ColumnModel}
52896      */
52897     getColumnModel : function(){
52898         return this.colModel;
52899     },
52900
52901     /**
52902      * Returns the grid's GridView object.
52903      * @return {GridView}
52904      */
52905     getView : function(){
52906         if(!this.view){
52907             this.view = new Roo.grid.GridView(this.viewConfig);
52908         }
52909         return this.view;
52910     },
52911     /**
52912      * Called to get grid's drag proxy text, by default returns this.ddText.
52913      * @return {String}
52914      */
52915     getDragDropText : function(){
52916         var count = this.selModel.getCount();
52917         return String.format(this.ddText, count, count == 1 ? '' : 's');
52918     }
52919 });
52920 /**
52921  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52922  * %0 is replaced with the number of selected rows.
52923  * @type String
52924  */
52925 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52926  * Based on:
52927  * Ext JS Library 1.1.1
52928  * Copyright(c) 2006-2007, Ext JS, LLC.
52929  *
52930  * Originally Released Under LGPL - original licence link has changed is not relivant.
52931  *
52932  * Fork - LGPL
52933  * <script type="text/javascript">
52934  */
52935  
52936 Roo.grid.AbstractGridView = function(){
52937         this.grid = null;
52938         
52939         this.events = {
52940             "beforerowremoved" : true,
52941             "beforerowsinserted" : true,
52942             "beforerefresh" : true,
52943             "rowremoved" : true,
52944             "rowsinserted" : true,
52945             "rowupdated" : true,
52946             "refresh" : true
52947         };
52948     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52949 };
52950
52951 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52952     rowClass : "x-grid-row",
52953     cellClass : "x-grid-cell",
52954     tdClass : "x-grid-td",
52955     hdClass : "x-grid-hd",
52956     splitClass : "x-grid-hd-split",
52957     
52958     init: function(grid){
52959         this.grid = grid;
52960                 var cid = this.grid.getGridEl().id;
52961         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52962         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52963         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52964         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52965         },
52966         
52967     getColumnRenderers : function(){
52968         var renderers = [];
52969         var cm = this.grid.colModel;
52970         var colCount = cm.getColumnCount();
52971         for(var i = 0; i < colCount; i++){
52972             renderers[i] = cm.getRenderer(i);
52973         }
52974         return renderers;
52975     },
52976     
52977     getColumnIds : function(){
52978         var ids = [];
52979         var cm = this.grid.colModel;
52980         var colCount = cm.getColumnCount();
52981         for(var i = 0; i < colCount; i++){
52982             ids[i] = cm.getColumnId(i);
52983         }
52984         return ids;
52985     },
52986     
52987     getDataIndexes : function(){
52988         if(!this.indexMap){
52989             this.indexMap = this.buildIndexMap();
52990         }
52991         return this.indexMap.colToData;
52992     },
52993     
52994     getColumnIndexByDataIndex : function(dataIndex){
52995         if(!this.indexMap){
52996             this.indexMap = this.buildIndexMap();
52997         }
52998         return this.indexMap.dataToCol[dataIndex];
52999     },
53000     
53001     /**
53002      * Set a css style for a column dynamically. 
53003      * @param {Number} colIndex The index of the column
53004      * @param {String} name The css property name
53005      * @param {String} value The css value
53006      */
53007     setCSSStyle : function(colIndex, name, value){
53008         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53009         Roo.util.CSS.updateRule(selector, name, value);
53010     },
53011     
53012     generateRules : function(cm){
53013         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53014         Roo.util.CSS.removeStyleSheet(rulesId);
53015         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53016             var cid = cm.getColumnId(i);
53017             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53018                          this.tdSelector, cid, " {\n}\n",
53019                          this.hdSelector, cid, " {\n}\n",
53020                          this.splitSelector, cid, " {\n}\n");
53021         }
53022         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53023     }
53024 });/*
53025  * Based on:
53026  * Ext JS Library 1.1.1
53027  * Copyright(c) 2006-2007, Ext JS, LLC.
53028  *
53029  * Originally Released Under LGPL - original licence link has changed is not relivant.
53030  *
53031  * Fork - LGPL
53032  * <script type="text/javascript">
53033  */
53034
53035 // private
53036 // This is a support class used internally by the Grid components
53037 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53038     this.grid = grid;
53039     this.view = grid.getView();
53040     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53041     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53042     if(hd2){
53043         this.setHandleElId(Roo.id(hd));
53044         this.setOuterHandleElId(Roo.id(hd2));
53045     }
53046     this.scroll = false;
53047 };
53048 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53049     maxDragWidth: 120,
53050     getDragData : function(e){
53051         var t = Roo.lib.Event.getTarget(e);
53052         var h = this.view.findHeaderCell(t);
53053         if(h){
53054             return {ddel: h.firstChild, header:h};
53055         }
53056         return false;
53057     },
53058
53059     onInitDrag : function(e){
53060         this.view.headersDisabled = true;
53061         var clone = this.dragData.ddel.cloneNode(true);
53062         clone.id = Roo.id();
53063         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53064         this.proxy.update(clone);
53065         return true;
53066     },
53067
53068     afterValidDrop : function(){
53069         var v = this.view;
53070         setTimeout(function(){
53071             v.headersDisabled = false;
53072         }, 50);
53073     },
53074
53075     afterInvalidDrop : function(){
53076         var v = this.view;
53077         setTimeout(function(){
53078             v.headersDisabled = false;
53079         }, 50);
53080     }
53081 });
53082 /*
53083  * Based on:
53084  * Ext JS Library 1.1.1
53085  * Copyright(c) 2006-2007, Ext JS, LLC.
53086  *
53087  * Originally Released Under LGPL - original licence link has changed is not relivant.
53088  *
53089  * Fork - LGPL
53090  * <script type="text/javascript">
53091  */
53092 // private
53093 // This is a support class used internally by the Grid components
53094 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53095     this.grid = grid;
53096     this.view = grid.getView();
53097     // split the proxies so they don't interfere with mouse events
53098     this.proxyTop = Roo.DomHelper.append(document.body, {
53099         cls:"col-move-top", html:"&#160;"
53100     }, true);
53101     this.proxyBottom = Roo.DomHelper.append(document.body, {
53102         cls:"col-move-bottom", html:"&#160;"
53103     }, true);
53104     this.proxyTop.hide = this.proxyBottom.hide = function(){
53105         this.setLeftTop(-100,-100);
53106         this.setStyle("visibility", "hidden");
53107     };
53108     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53109     // temporarily disabled
53110     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53111     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53112 };
53113 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53114     proxyOffsets : [-4, -9],
53115     fly: Roo.Element.fly,
53116
53117     getTargetFromEvent : function(e){
53118         var t = Roo.lib.Event.getTarget(e);
53119         var cindex = this.view.findCellIndex(t);
53120         if(cindex !== false){
53121             return this.view.getHeaderCell(cindex);
53122         }
53123         return null;
53124     },
53125
53126     nextVisible : function(h){
53127         var v = this.view, cm = this.grid.colModel;
53128         h = h.nextSibling;
53129         while(h){
53130             if(!cm.isHidden(v.getCellIndex(h))){
53131                 return h;
53132             }
53133             h = h.nextSibling;
53134         }
53135         return null;
53136     },
53137
53138     prevVisible : function(h){
53139         var v = this.view, cm = this.grid.colModel;
53140         h = h.prevSibling;
53141         while(h){
53142             if(!cm.isHidden(v.getCellIndex(h))){
53143                 return h;
53144             }
53145             h = h.prevSibling;
53146         }
53147         return null;
53148     },
53149
53150     positionIndicator : function(h, n, e){
53151         var x = Roo.lib.Event.getPageX(e);
53152         var r = Roo.lib.Dom.getRegion(n.firstChild);
53153         var px, pt, py = r.top + this.proxyOffsets[1];
53154         if((r.right - x) <= (r.right-r.left)/2){
53155             px = r.right+this.view.borderWidth;
53156             pt = "after";
53157         }else{
53158             px = r.left;
53159             pt = "before";
53160         }
53161         var oldIndex = this.view.getCellIndex(h);
53162         var newIndex = this.view.getCellIndex(n);
53163
53164         if(this.grid.colModel.isFixed(newIndex)){
53165             return false;
53166         }
53167
53168         var locked = this.grid.colModel.isLocked(newIndex);
53169
53170         if(pt == "after"){
53171             newIndex++;
53172         }
53173         if(oldIndex < newIndex){
53174             newIndex--;
53175         }
53176         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53177             return false;
53178         }
53179         px +=  this.proxyOffsets[0];
53180         this.proxyTop.setLeftTop(px, py);
53181         this.proxyTop.show();
53182         if(!this.bottomOffset){
53183             this.bottomOffset = this.view.mainHd.getHeight();
53184         }
53185         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53186         this.proxyBottom.show();
53187         return pt;
53188     },
53189
53190     onNodeEnter : function(n, dd, e, data){
53191         if(data.header != n){
53192             this.positionIndicator(data.header, n, e);
53193         }
53194     },
53195
53196     onNodeOver : function(n, dd, e, data){
53197         var result = false;
53198         if(data.header != n){
53199             result = this.positionIndicator(data.header, n, e);
53200         }
53201         if(!result){
53202             this.proxyTop.hide();
53203             this.proxyBottom.hide();
53204         }
53205         return result ? this.dropAllowed : this.dropNotAllowed;
53206     },
53207
53208     onNodeOut : function(n, dd, e, data){
53209         this.proxyTop.hide();
53210         this.proxyBottom.hide();
53211     },
53212
53213     onNodeDrop : function(n, dd, e, data){
53214         var h = data.header;
53215         if(h != n){
53216             var cm = this.grid.colModel;
53217             var x = Roo.lib.Event.getPageX(e);
53218             var r = Roo.lib.Dom.getRegion(n.firstChild);
53219             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53220             var oldIndex = this.view.getCellIndex(h);
53221             var newIndex = this.view.getCellIndex(n);
53222             var locked = cm.isLocked(newIndex);
53223             if(pt == "after"){
53224                 newIndex++;
53225             }
53226             if(oldIndex < newIndex){
53227                 newIndex--;
53228             }
53229             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53230                 return false;
53231             }
53232             cm.setLocked(oldIndex, locked, true);
53233             cm.moveColumn(oldIndex, newIndex);
53234             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53235             return true;
53236         }
53237         return false;
53238     }
53239 });
53240 /*
53241  * Based on:
53242  * Ext JS Library 1.1.1
53243  * Copyright(c) 2006-2007, Ext JS, LLC.
53244  *
53245  * Originally Released Under LGPL - original licence link has changed is not relivant.
53246  *
53247  * Fork - LGPL
53248  * <script type="text/javascript">
53249  */
53250   
53251 /**
53252  * @class Roo.grid.GridView
53253  * @extends Roo.util.Observable
53254  *
53255  * @constructor
53256  * @param {Object} config
53257  */
53258 Roo.grid.GridView = function(config){
53259     Roo.grid.GridView.superclass.constructor.call(this);
53260     this.el = null;
53261
53262     Roo.apply(this, config);
53263 };
53264
53265 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53266
53267     unselectable :  'unselectable="on"',
53268     unselectableCls :  'x-unselectable',
53269     
53270     
53271     rowClass : "x-grid-row",
53272
53273     cellClass : "x-grid-col",
53274
53275     tdClass : "x-grid-td",
53276
53277     hdClass : "x-grid-hd",
53278
53279     splitClass : "x-grid-split",
53280
53281     sortClasses : ["sort-asc", "sort-desc"],
53282
53283     enableMoveAnim : false,
53284
53285     hlColor: "C3DAF9",
53286
53287     dh : Roo.DomHelper,
53288
53289     fly : Roo.Element.fly,
53290
53291     css : Roo.util.CSS,
53292
53293     borderWidth: 1,
53294
53295     splitOffset: 3,
53296
53297     scrollIncrement : 22,
53298
53299     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53300
53301     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53302
53303     bind : function(ds, cm){
53304         if(this.ds){
53305             this.ds.un("load", this.onLoad, this);
53306             this.ds.un("datachanged", this.onDataChange, this);
53307             this.ds.un("add", this.onAdd, this);
53308             this.ds.un("remove", this.onRemove, this);
53309             this.ds.un("update", this.onUpdate, this);
53310             this.ds.un("clear", this.onClear, this);
53311         }
53312         if(ds){
53313             ds.on("load", this.onLoad, this);
53314             ds.on("datachanged", this.onDataChange, this);
53315             ds.on("add", this.onAdd, this);
53316             ds.on("remove", this.onRemove, this);
53317             ds.on("update", this.onUpdate, this);
53318             ds.on("clear", this.onClear, this);
53319         }
53320         this.ds = ds;
53321
53322         if(this.cm){
53323             this.cm.un("widthchange", this.onColWidthChange, this);
53324             this.cm.un("headerchange", this.onHeaderChange, this);
53325             this.cm.un("hiddenchange", this.onHiddenChange, this);
53326             this.cm.un("columnmoved", this.onColumnMove, this);
53327             this.cm.un("columnlockchange", this.onColumnLock, this);
53328         }
53329         if(cm){
53330             this.generateRules(cm);
53331             cm.on("widthchange", this.onColWidthChange, this);
53332             cm.on("headerchange", this.onHeaderChange, this);
53333             cm.on("hiddenchange", this.onHiddenChange, this);
53334             cm.on("columnmoved", this.onColumnMove, this);
53335             cm.on("columnlockchange", this.onColumnLock, this);
53336         }
53337         this.cm = cm;
53338     },
53339
53340     init: function(grid){
53341         Roo.grid.GridView.superclass.init.call(this, grid);
53342
53343         this.bind(grid.dataSource, grid.colModel);
53344
53345         grid.on("headerclick", this.handleHeaderClick, this);
53346
53347         if(grid.trackMouseOver){
53348             grid.on("mouseover", this.onRowOver, this);
53349             grid.on("mouseout", this.onRowOut, this);
53350         }
53351         grid.cancelTextSelection = function(){};
53352         this.gridId = grid.id;
53353
53354         var tpls = this.templates || {};
53355
53356         if(!tpls.master){
53357             tpls.master = new Roo.Template(
53358                '<div class="x-grid" hidefocus="true">',
53359                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53360                   '<div class="x-grid-topbar"></div>',
53361                   '<div class="x-grid-scroller"><div></div></div>',
53362                   '<div class="x-grid-locked">',
53363                       '<div class="x-grid-header">{lockedHeader}</div>',
53364                       '<div class="x-grid-body">{lockedBody}</div>',
53365                   "</div>",
53366                   '<div class="x-grid-viewport">',
53367                       '<div class="x-grid-header">{header}</div>',
53368                       '<div class="x-grid-body">{body}</div>',
53369                   "</div>",
53370                   '<div class="x-grid-bottombar"></div>',
53371                  
53372                   '<div class="x-grid-resize-proxy">&#160;</div>',
53373                "</div>"
53374             );
53375             tpls.master.disableformats = true;
53376         }
53377
53378         if(!tpls.header){
53379             tpls.header = new Roo.Template(
53380                '<table border="0" cellspacing="0" cellpadding="0">',
53381                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53382                "</table>{splits}"
53383             );
53384             tpls.header.disableformats = true;
53385         }
53386         tpls.header.compile();
53387
53388         if(!tpls.hcell){
53389             tpls.hcell = new Roo.Template(
53390                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53391                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53392                 "</div></td>"
53393              );
53394              tpls.hcell.disableFormats = true;
53395         }
53396         tpls.hcell.compile();
53397
53398         if(!tpls.hsplit){
53399             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53400                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53401             tpls.hsplit.disableFormats = true;
53402         }
53403         tpls.hsplit.compile();
53404
53405         if(!tpls.body){
53406             tpls.body = new Roo.Template(
53407                '<table border="0" cellspacing="0" cellpadding="0">',
53408                "<tbody>{rows}</tbody>",
53409                "</table>"
53410             );
53411             tpls.body.disableFormats = true;
53412         }
53413         tpls.body.compile();
53414
53415         if(!tpls.row){
53416             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53417             tpls.row.disableFormats = true;
53418         }
53419         tpls.row.compile();
53420
53421         if(!tpls.cell){
53422             tpls.cell = new Roo.Template(
53423                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53424                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53425                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53426                 "</td>"
53427             );
53428             tpls.cell.disableFormats = true;
53429         }
53430         tpls.cell.compile();
53431
53432         this.templates = tpls;
53433     },
53434
53435     // remap these for backwards compat
53436     onColWidthChange : function(){
53437         this.updateColumns.apply(this, arguments);
53438     },
53439     onHeaderChange : function(){
53440         this.updateHeaders.apply(this, arguments);
53441     }, 
53442     onHiddenChange : function(){
53443         this.handleHiddenChange.apply(this, arguments);
53444     },
53445     onColumnMove : function(){
53446         this.handleColumnMove.apply(this, arguments);
53447     },
53448     onColumnLock : function(){
53449         this.handleLockChange.apply(this, arguments);
53450     },
53451
53452     onDataChange : function(){
53453         this.refresh();
53454         this.updateHeaderSortState();
53455     },
53456
53457     onClear : function(){
53458         this.refresh();
53459     },
53460
53461     onUpdate : function(ds, record){
53462         this.refreshRow(record);
53463     },
53464
53465     refreshRow : function(record){
53466         var ds = this.ds, index;
53467         if(typeof record == 'number'){
53468             index = record;
53469             record = ds.getAt(index);
53470         }else{
53471             index = ds.indexOf(record);
53472         }
53473         this.insertRows(ds, index, index, true);
53474         this.onRemove(ds, record, index+1, true);
53475         this.syncRowHeights(index, index);
53476         this.layout();
53477         this.fireEvent("rowupdated", this, index, record);
53478     },
53479
53480     onAdd : function(ds, records, index){
53481         this.insertRows(ds, index, index + (records.length-1));
53482     },
53483
53484     onRemove : function(ds, record, index, isUpdate){
53485         if(isUpdate !== true){
53486             this.fireEvent("beforerowremoved", this, index, record);
53487         }
53488         var bt = this.getBodyTable(), lt = this.getLockedTable();
53489         if(bt.rows[index]){
53490             bt.firstChild.removeChild(bt.rows[index]);
53491         }
53492         if(lt.rows[index]){
53493             lt.firstChild.removeChild(lt.rows[index]);
53494         }
53495         if(isUpdate !== true){
53496             this.stripeRows(index);
53497             this.syncRowHeights(index, index);
53498             this.layout();
53499             this.fireEvent("rowremoved", this, index, record);
53500         }
53501     },
53502
53503     onLoad : function(){
53504         this.scrollToTop();
53505     },
53506
53507     /**
53508      * Scrolls the grid to the top
53509      */
53510     scrollToTop : function(){
53511         if(this.scroller){
53512             this.scroller.dom.scrollTop = 0;
53513             this.syncScroll();
53514         }
53515     },
53516
53517     /**
53518      * Gets a panel in the header of the grid that can be used for toolbars etc.
53519      * After modifying the contents of this panel a call to grid.autoSize() may be
53520      * required to register any changes in size.
53521      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53522      * @return Roo.Element
53523      */
53524     getHeaderPanel : function(doShow){
53525         if(doShow){
53526             this.headerPanel.show();
53527         }
53528         return this.headerPanel;
53529     },
53530
53531     /**
53532      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53533      * After modifying the contents of this panel a call to grid.autoSize() may be
53534      * required to register any changes in size.
53535      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53536      * @return Roo.Element
53537      */
53538     getFooterPanel : function(doShow){
53539         if(doShow){
53540             this.footerPanel.show();
53541         }
53542         return this.footerPanel;
53543     },
53544
53545     initElements : function(){
53546         var E = Roo.Element;
53547         var el = this.grid.getGridEl().dom.firstChild;
53548         var cs = el.childNodes;
53549
53550         this.el = new E(el);
53551         
53552          this.focusEl = new E(el.firstChild);
53553         this.focusEl.swallowEvent("click", true);
53554         
53555         this.headerPanel = new E(cs[1]);
53556         this.headerPanel.enableDisplayMode("block");
53557
53558         this.scroller = new E(cs[2]);
53559         this.scrollSizer = new E(this.scroller.dom.firstChild);
53560
53561         this.lockedWrap = new E(cs[3]);
53562         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53563         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53564
53565         this.mainWrap = new E(cs[4]);
53566         this.mainHd = new E(this.mainWrap.dom.firstChild);
53567         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53568
53569         this.footerPanel = new E(cs[5]);
53570         this.footerPanel.enableDisplayMode("block");
53571
53572         this.resizeProxy = new E(cs[6]);
53573
53574         this.headerSelector = String.format(
53575            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53576            this.lockedHd.id, this.mainHd.id
53577         );
53578
53579         this.splitterSelector = String.format(
53580            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53581            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53582         );
53583     },
53584     idToCssName : function(s)
53585     {
53586         return s.replace(/[^a-z0-9]+/ig, '-');
53587     },
53588
53589     getHeaderCell : function(index){
53590         return Roo.DomQuery.select(this.headerSelector)[index];
53591     },
53592
53593     getHeaderCellMeasure : function(index){
53594         return this.getHeaderCell(index).firstChild;
53595     },
53596
53597     getHeaderCellText : function(index){
53598         return this.getHeaderCell(index).firstChild.firstChild;
53599     },
53600
53601     getLockedTable : function(){
53602         return this.lockedBody.dom.firstChild;
53603     },
53604
53605     getBodyTable : function(){
53606         return this.mainBody.dom.firstChild;
53607     },
53608
53609     getLockedRow : function(index){
53610         return this.getLockedTable().rows[index];
53611     },
53612
53613     getRow : function(index){
53614         return this.getBodyTable().rows[index];
53615     },
53616
53617     getRowComposite : function(index){
53618         if(!this.rowEl){
53619             this.rowEl = new Roo.CompositeElementLite();
53620         }
53621         var els = [], lrow, mrow;
53622         if(lrow = this.getLockedRow(index)){
53623             els.push(lrow);
53624         }
53625         if(mrow = this.getRow(index)){
53626             els.push(mrow);
53627         }
53628         this.rowEl.elements = els;
53629         return this.rowEl;
53630     },
53631     /**
53632      * Gets the 'td' of the cell
53633      * 
53634      * @param {Integer} rowIndex row to select
53635      * @param {Integer} colIndex column to select
53636      * 
53637      * @return {Object} 
53638      */
53639     getCell : function(rowIndex, colIndex){
53640         var locked = this.cm.getLockedCount();
53641         var source;
53642         if(colIndex < locked){
53643             source = this.lockedBody.dom.firstChild;
53644         }else{
53645             source = this.mainBody.dom.firstChild;
53646             colIndex -= locked;
53647         }
53648         return source.rows[rowIndex].childNodes[colIndex];
53649     },
53650
53651     getCellText : function(rowIndex, colIndex){
53652         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53653     },
53654
53655     getCellBox : function(cell){
53656         var b = this.fly(cell).getBox();
53657         if(Roo.isOpera){ // opera fails to report the Y
53658             b.y = cell.offsetTop + this.mainBody.getY();
53659         }
53660         return b;
53661     },
53662
53663     getCellIndex : function(cell){
53664         var id = String(cell.className).match(this.cellRE);
53665         if(id){
53666             return parseInt(id[1], 10);
53667         }
53668         return 0;
53669     },
53670
53671     findHeaderIndex : function(n){
53672         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53673         return r ? this.getCellIndex(r) : false;
53674     },
53675
53676     findHeaderCell : function(n){
53677         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53678         return r ? r : false;
53679     },
53680
53681     findRowIndex : function(n){
53682         if(!n){
53683             return false;
53684         }
53685         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53686         return r ? r.rowIndex : false;
53687     },
53688
53689     findCellIndex : function(node){
53690         var stop = this.el.dom;
53691         while(node && node != stop){
53692             if(this.findRE.test(node.className)){
53693                 return this.getCellIndex(node);
53694             }
53695             node = node.parentNode;
53696         }
53697         return false;
53698     },
53699
53700     getColumnId : function(index){
53701         return this.cm.getColumnId(index);
53702     },
53703
53704     getSplitters : function()
53705     {
53706         if(this.splitterSelector){
53707            return Roo.DomQuery.select(this.splitterSelector);
53708         }else{
53709             return null;
53710       }
53711     },
53712
53713     getSplitter : function(index){
53714         return this.getSplitters()[index];
53715     },
53716
53717     onRowOver : function(e, t){
53718         var row;
53719         if((row = this.findRowIndex(t)) !== false){
53720             this.getRowComposite(row).addClass("x-grid-row-over");
53721         }
53722     },
53723
53724     onRowOut : function(e, t){
53725         var row;
53726         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53727             this.getRowComposite(row).removeClass("x-grid-row-over");
53728         }
53729     },
53730
53731     renderHeaders : function(){
53732         var cm = this.cm;
53733         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53734         var cb = [], lb = [], sb = [], lsb = [], p = {};
53735         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53736             p.cellId = "x-grid-hd-0-" + i;
53737             p.splitId = "x-grid-csplit-0-" + i;
53738             p.id = cm.getColumnId(i);
53739             p.title = cm.getColumnTooltip(i) || "";
53740             p.value = cm.getColumnHeader(i) || "";
53741             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53742             if(!cm.isLocked(i)){
53743                 cb[cb.length] = ct.apply(p);
53744                 sb[sb.length] = st.apply(p);
53745             }else{
53746                 lb[lb.length] = ct.apply(p);
53747                 lsb[lsb.length] = st.apply(p);
53748             }
53749         }
53750         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53751                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53752     },
53753
53754     updateHeaders : function(){
53755         var html = this.renderHeaders();
53756         this.lockedHd.update(html[0]);
53757         this.mainHd.update(html[1]);
53758     },
53759
53760     /**
53761      * Focuses the specified row.
53762      * @param {Number} row The row index
53763      */
53764     focusRow : function(row)
53765     {
53766         //Roo.log('GridView.focusRow');
53767         var x = this.scroller.dom.scrollLeft;
53768         this.focusCell(row, 0, false);
53769         this.scroller.dom.scrollLeft = x;
53770     },
53771
53772     /**
53773      * Focuses the specified cell.
53774      * @param {Number} row The row index
53775      * @param {Number} col The column index
53776      * @param {Boolean} hscroll false to disable horizontal scrolling
53777      */
53778     focusCell : function(row, col, hscroll)
53779     {
53780         //Roo.log('GridView.focusCell');
53781         var el = this.ensureVisible(row, col, hscroll);
53782         this.focusEl.alignTo(el, "tl-tl");
53783         if(Roo.isGecko){
53784             this.focusEl.focus();
53785         }else{
53786             this.focusEl.focus.defer(1, this.focusEl);
53787         }
53788     },
53789
53790     /**
53791      * Scrolls the specified cell into view
53792      * @param {Number} row The row index
53793      * @param {Number} col The column index
53794      * @param {Boolean} hscroll false to disable horizontal scrolling
53795      */
53796     ensureVisible : function(row, col, hscroll)
53797     {
53798         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53799         //return null; //disable for testing.
53800         if(typeof row != "number"){
53801             row = row.rowIndex;
53802         }
53803         if(row < 0 && row >= this.ds.getCount()){
53804             return  null;
53805         }
53806         col = (col !== undefined ? col : 0);
53807         var cm = this.grid.colModel;
53808         while(cm.isHidden(col)){
53809             col++;
53810         }
53811
53812         var el = this.getCell(row, col);
53813         if(!el){
53814             return null;
53815         }
53816         var c = this.scroller.dom;
53817
53818         var ctop = parseInt(el.offsetTop, 10);
53819         var cleft = parseInt(el.offsetLeft, 10);
53820         var cbot = ctop + el.offsetHeight;
53821         var cright = cleft + el.offsetWidth;
53822         
53823         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53824         var stop = parseInt(c.scrollTop, 10);
53825         var sleft = parseInt(c.scrollLeft, 10);
53826         var sbot = stop + ch;
53827         var sright = sleft + c.clientWidth;
53828         /*
53829         Roo.log('GridView.ensureVisible:' +
53830                 ' ctop:' + ctop +
53831                 ' c.clientHeight:' + c.clientHeight +
53832                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53833                 ' stop:' + stop +
53834                 ' cbot:' + cbot +
53835                 ' sbot:' + sbot +
53836                 ' ch:' + ch  
53837                 );
53838         */
53839         if(ctop < stop){
53840              c.scrollTop = ctop;
53841             //Roo.log("set scrolltop to ctop DISABLE?");
53842         }else if(cbot > sbot){
53843             //Roo.log("set scrolltop to cbot-ch");
53844             c.scrollTop = cbot-ch;
53845         }
53846         
53847         if(hscroll !== false){
53848             if(cleft < sleft){
53849                 c.scrollLeft = cleft;
53850             }else if(cright > sright){
53851                 c.scrollLeft = cright-c.clientWidth;
53852             }
53853         }
53854          
53855         return el;
53856     },
53857
53858     updateColumns : function(){
53859         this.grid.stopEditing();
53860         var cm = this.grid.colModel, colIds = this.getColumnIds();
53861         //var totalWidth = cm.getTotalWidth();
53862         var pos = 0;
53863         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53864             //if(cm.isHidden(i)) continue;
53865             var w = cm.getColumnWidth(i);
53866             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53867             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53868         }
53869         this.updateSplitters();
53870     },
53871
53872     generateRules : function(cm){
53873         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53874         Roo.util.CSS.removeStyleSheet(rulesId);
53875         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53876             var cid = cm.getColumnId(i);
53877             var align = '';
53878             if(cm.config[i].align){
53879                 align = 'text-align:'+cm.config[i].align+';';
53880             }
53881             var hidden = '';
53882             if(cm.isHidden(i)){
53883                 hidden = 'display:none;';
53884             }
53885             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53886             ruleBuf.push(
53887                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53888                     this.hdSelector, cid, " {\n", align, width, "}\n",
53889                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53890                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53891         }
53892         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53893     },
53894
53895     updateSplitters : function(){
53896         var cm = this.cm, s = this.getSplitters();
53897         if(s){ // splitters not created yet
53898             var pos = 0, locked = true;
53899             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53900                 if(cm.isHidden(i)) continue;
53901                 var w = cm.getColumnWidth(i); // make sure it's a number
53902                 if(!cm.isLocked(i) && locked){
53903                     pos = 0;
53904                     locked = false;
53905                 }
53906                 pos += w;
53907                 s[i].style.left = (pos-this.splitOffset) + "px";
53908             }
53909         }
53910     },
53911
53912     handleHiddenChange : function(colModel, colIndex, hidden){
53913         if(hidden){
53914             this.hideColumn(colIndex);
53915         }else{
53916             this.unhideColumn(colIndex);
53917         }
53918     },
53919
53920     hideColumn : function(colIndex){
53921         var cid = this.getColumnId(colIndex);
53922         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53923         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53924         if(Roo.isSafari){
53925             this.updateHeaders();
53926         }
53927         this.updateSplitters();
53928         this.layout();
53929     },
53930
53931     unhideColumn : function(colIndex){
53932         var cid = this.getColumnId(colIndex);
53933         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53934         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53935
53936         if(Roo.isSafari){
53937             this.updateHeaders();
53938         }
53939         this.updateSplitters();
53940         this.layout();
53941     },
53942
53943     insertRows : function(dm, firstRow, lastRow, isUpdate){
53944         if(firstRow == 0 && lastRow == dm.getCount()-1){
53945             this.refresh();
53946         }else{
53947             if(!isUpdate){
53948                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53949             }
53950             var s = this.getScrollState();
53951             var markup = this.renderRows(firstRow, lastRow);
53952             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53953             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53954             this.restoreScroll(s);
53955             if(!isUpdate){
53956                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53957                 this.syncRowHeights(firstRow, lastRow);
53958                 this.stripeRows(firstRow);
53959                 this.layout();
53960             }
53961         }
53962     },
53963
53964     bufferRows : function(markup, target, index){
53965         var before = null, trows = target.rows, tbody = target.tBodies[0];
53966         if(index < trows.length){
53967             before = trows[index];
53968         }
53969         var b = document.createElement("div");
53970         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53971         var rows = b.firstChild.rows;
53972         for(var i = 0, len = rows.length; i < len; i++){
53973             if(before){
53974                 tbody.insertBefore(rows[0], before);
53975             }else{
53976                 tbody.appendChild(rows[0]);
53977             }
53978         }
53979         b.innerHTML = "";
53980         b = null;
53981     },
53982
53983     deleteRows : function(dm, firstRow, lastRow){
53984         if(dm.getRowCount()<1){
53985             this.fireEvent("beforerefresh", this);
53986             this.mainBody.update("");
53987             this.lockedBody.update("");
53988             this.fireEvent("refresh", this);
53989         }else{
53990             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53991             var bt = this.getBodyTable();
53992             var tbody = bt.firstChild;
53993             var rows = bt.rows;
53994             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53995                 tbody.removeChild(rows[firstRow]);
53996             }
53997             this.stripeRows(firstRow);
53998             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53999         }
54000     },
54001
54002     updateRows : function(dataSource, firstRow, lastRow){
54003         var s = this.getScrollState();
54004         this.refresh();
54005         this.restoreScroll(s);
54006     },
54007
54008     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54009         if(!noRefresh){
54010            this.refresh();
54011         }
54012         this.updateHeaderSortState();
54013     },
54014
54015     getScrollState : function(){
54016         
54017         var sb = this.scroller.dom;
54018         return {left: sb.scrollLeft, top: sb.scrollTop};
54019     },
54020
54021     stripeRows : function(startRow){
54022         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54023             return;
54024         }
54025         startRow = startRow || 0;
54026         var rows = this.getBodyTable().rows;
54027         var lrows = this.getLockedTable().rows;
54028         var cls = ' x-grid-row-alt ';
54029         for(var i = startRow, len = rows.length; i < len; i++){
54030             var row = rows[i], lrow = lrows[i];
54031             var isAlt = ((i+1) % 2 == 0);
54032             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54033             if(isAlt == hasAlt){
54034                 continue;
54035             }
54036             if(isAlt){
54037                 row.className += " x-grid-row-alt";
54038             }else{
54039                 row.className = row.className.replace("x-grid-row-alt", "");
54040             }
54041             if(lrow){
54042                 lrow.className = row.className;
54043             }
54044         }
54045     },
54046
54047     restoreScroll : function(state){
54048         //Roo.log('GridView.restoreScroll');
54049         var sb = this.scroller.dom;
54050         sb.scrollLeft = state.left;
54051         sb.scrollTop = state.top;
54052         this.syncScroll();
54053     },
54054
54055     syncScroll : function(){
54056         //Roo.log('GridView.syncScroll');
54057         var sb = this.scroller.dom;
54058         var sh = this.mainHd.dom;
54059         var bs = this.mainBody.dom;
54060         var lv = this.lockedBody.dom;
54061         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54062         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54063     },
54064
54065     handleScroll : function(e){
54066         this.syncScroll();
54067         var sb = this.scroller.dom;
54068         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54069         e.stopEvent();
54070     },
54071
54072     handleWheel : function(e){
54073         var d = e.getWheelDelta();
54074         this.scroller.dom.scrollTop -= d*22;
54075         // set this here to prevent jumpy scrolling on large tables
54076         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54077         e.stopEvent();
54078     },
54079
54080     renderRows : function(startRow, endRow){
54081         // pull in all the crap needed to render rows
54082         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54083         var colCount = cm.getColumnCount();
54084
54085         if(ds.getCount() < 1){
54086             return ["", ""];
54087         }
54088
54089         // build a map for all the columns
54090         var cs = [];
54091         for(var i = 0; i < colCount; i++){
54092             var name = cm.getDataIndex(i);
54093             cs[i] = {
54094                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54095                 renderer : cm.getRenderer(i),
54096                 id : cm.getColumnId(i),
54097                 locked : cm.isLocked(i)
54098             };
54099         }
54100
54101         startRow = startRow || 0;
54102         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54103
54104         // records to render
54105         var rs = ds.getRange(startRow, endRow);
54106
54107         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54108     },
54109
54110     // As much as I hate to duplicate code, this was branched because FireFox really hates
54111     // [].join("") on strings. The performance difference was substantial enough to
54112     // branch this function
54113     doRender : Roo.isGecko ?
54114             function(cs, rs, ds, startRow, colCount, stripe){
54115                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54116                 // buffers
54117                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54118                 
54119                 var hasListener = this.grid.hasListener('rowclass');
54120                 var rowcfg = {};
54121                 for(var j = 0, len = rs.length; j < len; j++){
54122                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54123                     for(var i = 0; i < colCount; i++){
54124                         c = cs[i];
54125                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54126                         p.id = c.id;
54127                         p.css = p.attr = "";
54128                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54129                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54130                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54131                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54132                         }
54133                         var markup = ct.apply(p);
54134                         if(!c.locked){
54135                             cb+= markup;
54136                         }else{
54137                             lcb+= markup;
54138                         }
54139                     }
54140                     var alt = [];
54141                     if(stripe && ((rowIndex+1) % 2 == 0)){
54142                         alt.push("x-grid-row-alt")
54143                     }
54144                     if(r.dirty){
54145                         alt.push(  " x-grid-dirty-row");
54146                     }
54147                     rp.cells = lcb;
54148                     if(this.getRowClass){
54149                         alt.push(this.getRowClass(r, rowIndex));
54150                     }
54151                     if (hasListener) {
54152                         rowcfg = {
54153                              
54154                             record: r,
54155                             rowIndex : rowIndex,
54156                             rowClass : ''
54157                         }
54158                         this.grid.fireEvent('rowclass', this, rowcfg);
54159                         alt.push(rowcfg.rowClass);
54160                     }
54161                     rp.alt = alt.join(" ");
54162                     lbuf+= rt.apply(rp);
54163                     rp.cells = cb;
54164                     buf+=  rt.apply(rp);
54165                 }
54166                 return [lbuf, buf];
54167             } :
54168             function(cs, rs, ds, startRow, colCount, stripe){
54169                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54170                 // buffers
54171                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54172                 var hasListener = this.grid.hasListener('rowclass');
54173  
54174                 var rowcfg = {};
54175                 for(var j = 0, len = rs.length; j < len; j++){
54176                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54177                     for(var i = 0; i < colCount; i++){
54178                         c = cs[i];
54179                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54180                         p.id = c.id;
54181                         p.css = p.attr = "";
54182                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54183                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54184                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54185                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54186                         }
54187                         
54188                         var markup = ct.apply(p);
54189                         if(!c.locked){
54190                             cb[cb.length] = markup;
54191                         }else{
54192                             lcb[lcb.length] = markup;
54193                         }
54194                     }
54195                     var alt = [];
54196                     if(stripe && ((rowIndex+1) % 2 == 0)){
54197                         alt.push( "x-grid-row-alt");
54198                     }
54199                     if(r.dirty){
54200                         alt.push(" x-grid-dirty-row");
54201                     }
54202                     rp.cells = lcb;
54203                     if(this.getRowClass){
54204                         alt.push( this.getRowClass(r, rowIndex));
54205                     }
54206                     if (hasListener) {
54207                         rowcfg = {
54208                              
54209                             record: r,
54210                             rowIndex : rowIndex,
54211                             rowClass : ''
54212                         }
54213                         this.grid.fireEvent('rowclass', this, rowcfg);
54214                         alt.push(rowcfg.rowClass);
54215                     }
54216                     rp.alt = alt.join(" ");
54217                     rp.cells = lcb.join("");
54218                     lbuf[lbuf.length] = rt.apply(rp);
54219                     rp.cells = cb.join("");
54220                     buf[buf.length] =  rt.apply(rp);
54221                 }
54222                 return [lbuf.join(""), buf.join("")];
54223             },
54224
54225     renderBody : function(){
54226         var markup = this.renderRows();
54227         var bt = this.templates.body;
54228         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54229     },
54230
54231     /**
54232      * Refreshes the grid
54233      * @param {Boolean} headersToo
54234      */
54235     refresh : function(headersToo){
54236         this.fireEvent("beforerefresh", this);
54237         this.grid.stopEditing();
54238         var result = this.renderBody();
54239         this.lockedBody.update(result[0]);
54240         this.mainBody.update(result[1]);
54241         if(headersToo === true){
54242             this.updateHeaders();
54243             this.updateColumns();
54244             this.updateSplitters();
54245             this.updateHeaderSortState();
54246         }
54247         this.syncRowHeights();
54248         this.layout();
54249         this.fireEvent("refresh", this);
54250     },
54251
54252     handleColumnMove : function(cm, oldIndex, newIndex){
54253         this.indexMap = null;
54254         var s = this.getScrollState();
54255         this.refresh(true);
54256         this.restoreScroll(s);
54257         this.afterMove(newIndex);
54258     },
54259
54260     afterMove : function(colIndex){
54261         if(this.enableMoveAnim && Roo.enableFx){
54262             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54263         }
54264         // if multisort - fix sortOrder, and reload..
54265         if (this.grid.dataSource.multiSort) {
54266             // the we can call sort again..
54267             var dm = this.grid.dataSource;
54268             var cm = this.grid.colModel;
54269             var so = [];
54270             for(var i = 0; i < cm.config.length; i++ ) {
54271                 
54272                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54273                     continue; // dont' bother, it's not in sort list or being set.
54274                 }
54275                 
54276                 so.push(cm.config[i].dataIndex);
54277             };
54278             dm.sortOrder = so;
54279             dm.load(dm.lastOptions);
54280             
54281             
54282         }
54283         
54284     },
54285
54286     updateCell : function(dm, rowIndex, dataIndex){
54287         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54288         if(typeof colIndex == "undefined"){ // not present in grid
54289             return;
54290         }
54291         var cm = this.grid.colModel;
54292         var cell = this.getCell(rowIndex, colIndex);
54293         var cellText = this.getCellText(rowIndex, colIndex);
54294
54295         var p = {
54296             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54297             id : cm.getColumnId(colIndex),
54298             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54299         };
54300         var renderer = cm.getRenderer(colIndex);
54301         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54302         if(typeof val == "undefined" || val === "") val = "&#160;";
54303         cellText.innerHTML = val;
54304         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54305         this.syncRowHeights(rowIndex, rowIndex);
54306     },
54307
54308     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54309         var maxWidth = 0;
54310         if(this.grid.autoSizeHeaders){
54311             var h = this.getHeaderCellMeasure(colIndex);
54312             maxWidth = Math.max(maxWidth, h.scrollWidth);
54313         }
54314         var tb, index;
54315         if(this.cm.isLocked(colIndex)){
54316             tb = this.getLockedTable();
54317             index = colIndex;
54318         }else{
54319             tb = this.getBodyTable();
54320             index = colIndex - this.cm.getLockedCount();
54321         }
54322         if(tb && tb.rows){
54323             var rows = tb.rows;
54324             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54325             for(var i = 0; i < stopIndex; i++){
54326                 var cell = rows[i].childNodes[index].firstChild;
54327                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54328             }
54329         }
54330         return maxWidth + /*margin for error in IE*/ 5;
54331     },
54332     /**
54333      * Autofit a column to its content.
54334      * @param {Number} colIndex
54335      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54336      */
54337      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54338          if(this.cm.isHidden(colIndex)){
54339              return; // can't calc a hidden column
54340          }
54341         if(forceMinSize){
54342             var cid = this.cm.getColumnId(colIndex);
54343             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54344            if(this.grid.autoSizeHeaders){
54345                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54346            }
54347         }
54348         var newWidth = this.calcColumnWidth(colIndex);
54349         this.cm.setColumnWidth(colIndex,
54350             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54351         if(!suppressEvent){
54352             this.grid.fireEvent("columnresize", colIndex, newWidth);
54353         }
54354     },
54355
54356     /**
54357      * Autofits all columns to their content and then expands to fit any extra space in the grid
54358      */
54359      autoSizeColumns : function(){
54360         var cm = this.grid.colModel;
54361         var colCount = cm.getColumnCount();
54362         for(var i = 0; i < colCount; i++){
54363             this.autoSizeColumn(i, true, true);
54364         }
54365         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54366             this.fitColumns();
54367         }else{
54368             this.updateColumns();
54369             this.layout();
54370         }
54371     },
54372
54373     /**
54374      * Autofits all columns to the grid's width proportionate with their current size
54375      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54376      */
54377     fitColumns : function(reserveScrollSpace){
54378         var cm = this.grid.colModel;
54379         var colCount = cm.getColumnCount();
54380         var cols = [];
54381         var width = 0;
54382         var i, w;
54383         for (i = 0; i < colCount; i++){
54384             if(!cm.isHidden(i) && !cm.isFixed(i)){
54385                 w = cm.getColumnWidth(i);
54386                 cols.push(i);
54387                 cols.push(w);
54388                 width += w;
54389             }
54390         }
54391         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54392         if(reserveScrollSpace){
54393             avail -= 17;
54394         }
54395         var frac = (avail - cm.getTotalWidth())/width;
54396         while (cols.length){
54397             w = cols.pop();
54398             i = cols.pop();
54399             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54400         }
54401         this.updateColumns();
54402         this.layout();
54403     },
54404
54405     onRowSelect : function(rowIndex){
54406         var row = this.getRowComposite(rowIndex);
54407         row.addClass("x-grid-row-selected");
54408     },
54409
54410     onRowDeselect : function(rowIndex){
54411         var row = this.getRowComposite(rowIndex);
54412         row.removeClass("x-grid-row-selected");
54413     },
54414
54415     onCellSelect : function(row, col){
54416         var cell = this.getCell(row, col);
54417         if(cell){
54418             Roo.fly(cell).addClass("x-grid-cell-selected");
54419         }
54420     },
54421
54422     onCellDeselect : function(row, col){
54423         var cell = this.getCell(row, col);
54424         if(cell){
54425             Roo.fly(cell).removeClass("x-grid-cell-selected");
54426         }
54427     },
54428
54429     updateHeaderSortState : function(){
54430         
54431         // sort state can be single { field: xxx, direction : yyy}
54432         // or   { xxx=>ASC , yyy : DESC ..... }
54433         
54434         var mstate = {};
54435         if (!this.ds.multiSort) { 
54436             var state = this.ds.getSortState();
54437             if(!state){
54438                 return;
54439             }
54440             mstate[state.field] = state.direction;
54441             // FIXME... - this is not used here.. but might be elsewhere..
54442             this.sortState = state;
54443             
54444         } else {
54445             mstate = this.ds.sortToggle;
54446         }
54447         //remove existing sort classes..
54448         
54449         var sc = this.sortClasses;
54450         var hds = this.el.select(this.headerSelector).removeClass(sc);
54451         
54452         for(var f in mstate) {
54453         
54454             var sortColumn = this.cm.findColumnIndex(f);
54455             
54456             if(sortColumn != -1){
54457                 var sortDir = mstate[f];        
54458                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54459             }
54460         }
54461         
54462          
54463         
54464     },
54465
54466
54467     handleHeaderClick : function(g, index,e){
54468         
54469         Roo.log("header click");
54470         
54471         if (Roo.isTouch) {
54472             // touch events on header are handled by context
54473             this.handleHdCtx(g,index,e);
54474             return;
54475         }
54476         
54477         
54478         if(this.headersDisabled){
54479             return;
54480         }
54481         var dm = g.dataSource, cm = g.colModel;
54482         if(!cm.isSortable(index)){
54483             return;
54484         }
54485         g.stopEditing();
54486         
54487         if (dm.multiSort) {
54488             // update the sortOrder
54489             var so = [];
54490             for(var i = 0; i < cm.config.length; i++ ) {
54491                 
54492                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54493                     continue; // dont' bother, it's not in sort list or being set.
54494                 }
54495                 
54496                 so.push(cm.config[i].dataIndex);
54497             };
54498             dm.sortOrder = so;
54499         }
54500         
54501         
54502         dm.sort(cm.getDataIndex(index));
54503     },
54504
54505
54506     destroy : function(){
54507         if(this.colMenu){
54508             this.colMenu.removeAll();
54509             Roo.menu.MenuMgr.unregister(this.colMenu);
54510             this.colMenu.getEl().remove();
54511             delete this.colMenu;
54512         }
54513         if(this.hmenu){
54514             this.hmenu.removeAll();
54515             Roo.menu.MenuMgr.unregister(this.hmenu);
54516             this.hmenu.getEl().remove();
54517             delete this.hmenu;
54518         }
54519         if(this.grid.enableColumnMove){
54520             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54521             if(dds){
54522                 for(var dd in dds){
54523                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54524                         var elid = dds[dd].dragElId;
54525                         dds[dd].unreg();
54526                         Roo.get(elid).remove();
54527                     } else if(dds[dd].config.isTarget){
54528                         dds[dd].proxyTop.remove();
54529                         dds[dd].proxyBottom.remove();
54530                         dds[dd].unreg();
54531                     }
54532                     if(Roo.dd.DDM.locationCache[dd]){
54533                         delete Roo.dd.DDM.locationCache[dd];
54534                     }
54535                 }
54536                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54537             }
54538         }
54539         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54540         this.bind(null, null);
54541         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54542     },
54543
54544     handleLockChange : function(){
54545         this.refresh(true);
54546     },
54547
54548     onDenyColumnLock : function(){
54549
54550     },
54551
54552     onDenyColumnHide : function(){
54553
54554     },
54555
54556     handleHdMenuClick : function(item){
54557         var index = this.hdCtxIndex;
54558         var cm = this.cm, ds = this.ds;
54559         switch(item.id){
54560             case "asc":
54561                 ds.sort(cm.getDataIndex(index), "ASC");
54562                 break;
54563             case "desc":
54564                 ds.sort(cm.getDataIndex(index), "DESC");
54565                 break;
54566             case "lock":
54567                 var lc = cm.getLockedCount();
54568                 if(cm.getColumnCount(true) <= lc+1){
54569                     this.onDenyColumnLock();
54570                     return;
54571                 }
54572                 if(lc != index){
54573                     cm.setLocked(index, true, true);
54574                     cm.moveColumn(index, lc);
54575                     this.grid.fireEvent("columnmove", index, lc);
54576                 }else{
54577                     cm.setLocked(index, true);
54578                 }
54579             break;
54580             case "unlock":
54581                 var lc = cm.getLockedCount();
54582                 if((lc-1) != index){
54583                     cm.setLocked(index, false, true);
54584                     cm.moveColumn(index, lc-1);
54585                     this.grid.fireEvent("columnmove", index, lc-1);
54586                 }else{
54587                     cm.setLocked(index, false);
54588                 }
54589             break;
54590             case 'wider': // used to expand cols on touch..
54591             case 'narrow':
54592                 var cw = cm.getColumnWidth(index);
54593                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54594                 cw = Math.max(0, cw);
54595                 cw = Math.min(cw,4000);
54596                 cm.setColumnWidth(index, cw);
54597                 break;
54598                 
54599             default:
54600                 index = cm.getIndexById(item.id.substr(4));
54601                 if(index != -1){
54602                     if(item.checked && cm.getColumnCount(true) <= 1){
54603                         this.onDenyColumnHide();
54604                         return false;
54605                     }
54606                     cm.setHidden(index, item.checked);
54607                 }
54608         }
54609         return true;
54610     },
54611
54612     beforeColMenuShow : function(){
54613         var cm = this.cm,  colCount = cm.getColumnCount();
54614         this.colMenu.removeAll();
54615         for(var i = 0; i < colCount; i++){
54616             this.colMenu.add(new Roo.menu.CheckItem({
54617                 id: "col-"+cm.getColumnId(i),
54618                 text: cm.getColumnHeader(i),
54619                 checked: !cm.isHidden(i),
54620                 hideOnClick:false
54621             }));
54622         }
54623     },
54624
54625     handleHdCtx : function(g, index, e){
54626         e.stopEvent();
54627         var hd = this.getHeaderCell(index);
54628         this.hdCtxIndex = index;
54629         var ms = this.hmenu.items, cm = this.cm;
54630         ms.get("asc").setDisabled(!cm.isSortable(index));
54631         ms.get("desc").setDisabled(!cm.isSortable(index));
54632         if(this.grid.enableColLock !== false){
54633             ms.get("lock").setDisabled(cm.isLocked(index));
54634             ms.get("unlock").setDisabled(!cm.isLocked(index));
54635         }
54636         this.hmenu.show(hd, "tl-bl");
54637     },
54638
54639     handleHdOver : function(e){
54640         var hd = this.findHeaderCell(e.getTarget());
54641         if(hd && !this.headersDisabled){
54642             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54643                this.fly(hd).addClass("x-grid-hd-over");
54644             }
54645         }
54646     },
54647
54648     handleHdOut : function(e){
54649         var hd = this.findHeaderCell(e.getTarget());
54650         if(hd){
54651             this.fly(hd).removeClass("x-grid-hd-over");
54652         }
54653     },
54654
54655     handleSplitDblClick : function(e, t){
54656         var i = this.getCellIndex(t);
54657         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54658             this.autoSizeColumn(i, true);
54659             this.layout();
54660         }
54661     },
54662
54663     render : function(){
54664
54665         var cm = this.cm;
54666         var colCount = cm.getColumnCount();
54667
54668         if(this.grid.monitorWindowResize === true){
54669             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54670         }
54671         var header = this.renderHeaders();
54672         var body = this.templates.body.apply({rows:""});
54673         var html = this.templates.master.apply({
54674             lockedBody: body,
54675             body: body,
54676             lockedHeader: header[0],
54677             header: header[1]
54678         });
54679
54680         //this.updateColumns();
54681
54682         this.grid.getGridEl().dom.innerHTML = html;
54683
54684         this.initElements();
54685         
54686         // a kludge to fix the random scolling effect in webkit
54687         this.el.on("scroll", function() {
54688             this.el.dom.scrollTop=0; // hopefully not recursive..
54689         },this);
54690
54691         this.scroller.on("scroll", this.handleScroll, this);
54692         this.lockedBody.on("mousewheel", this.handleWheel, this);
54693         this.mainBody.on("mousewheel", this.handleWheel, this);
54694
54695         this.mainHd.on("mouseover", this.handleHdOver, this);
54696         this.mainHd.on("mouseout", this.handleHdOut, this);
54697         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54698                 {delegate: "."+this.splitClass});
54699
54700         this.lockedHd.on("mouseover", this.handleHdOver, this);
54701         this.lockedHd.on("mouseout", this.handleHdOut, this);
54702         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54703                 {delegate: "."+this.splitClass});
54704
54705         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54706             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54707         }
54708
54709         this.updateSplitters();
54710
54711         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54712             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54713             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54714         }
54715
54716         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54717             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54718             this.hmenu.add(
54719                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54720                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54721             );
54722             if(this.grid.enableColLock !== false){
54723                 this.hmenu.add('-',
54724                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54725                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54726                 );
54727             }
54728             if (Roo.isTouch) {
54729                  this.hmenu.add('-',
54730                     {id:"wider", text: this.columnsWiderText},
54731                     {id:"narrow", text: this.columnsNarrowText }
54732                 );
54733                 
54734                  
54735             }
54736             
54737             if(this.grid.enableColumnHide !== false){
54738
54739                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54740                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54741                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54742
54743                 this.hmenu.add('-',
54744                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54745                 );
54746             }
54747             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54748
54749             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54750         }
54751
54752         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54753             this.dd = new Roo.grid.GridDragZone(this.grid, {
54754                 ddGroup : this.grid.ddGroup || 'GridDD'
54755             });
54756             
54757         }
54758
54759         /*
54760         for(var i = 0; i < colCount; i++){
54761             if(cm.isHidden(i)){
54762                 this.hideColumn(i);
54763             }
54764             if(cm.config[i].align){
54765                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54766                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54767             }
54768         }*/
54769         
54770         this.updateHeaderSortState();
54771
54772         this.beforeInitialResize();
54773         this.layout(true);
54774
54775         // two part rendering gives faster view to the user
54776         this.renderPhase2.defer(1, this);
54777     },
54778
54779     renderPhase2 : function(){
54780         // render the rows now
54781         this.refresh();
54782         if(this.grid.autoSizeColumns){
54783             this.autoSizeColumns();
54784         }
54785     },
54786
54787     beforeInitialResize : function(){
54788
54789     },
54790
54791     onColumnSplitterMoved : function(i, w){
54792         this.userResized = true;
54793         var cm = this.grid.colModel;
54794         cm.setColumnWidth(i, w, true);
54795         var cid = cm.getColumnId(i);
54796         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54797         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54798         this.updateSplitters();
54799         this.layout();
54800         this.grid.fireEvent("columnresize", i, w);
54801     },
54802
54803     syncRowHeights : function(startIndex, endIndex){
54804         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54805             startIndex = startIndex || 0;
54806             var mrows = this.getBodyTable().rows;
54807             var lrows = this.getLockedTable().rows;
54808             var len = mrows.length-1;
54809             endIndex = Math.min(endIndex || len, len);
54810             for(var i = startIndex; i <= endIndex; i++){
54811                 var m = mrows[i], l = lrows[i];
54812                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54813                 m.style.height = l.style.height = h + "px";
54814             }
54815         }
54816     },
54817
54818     layout : function(initialRender, is2ndPass){
54819         var g = this.grid;
54820         var auto = g.autoHeight;
54821         var scrollOffset = 16;
54822         var c = g.getGridEl(), cm = this.cm,
54823                 expandCol = g.autoExpandColumn,
54824                 gv = this;
54825         //c.beginMeasure();
54826
54827         if(!c.dom.offsetWidth){ // display:none?
54828             if(initialRender){
54829                 this.lockedWrap.show();
54830                 this.mainWrap.show();
54831             }
54832             return;
54833         }
54834
54835         var hasLock = this.cm.isLocked(0);
54836
54837         var tbh = this.headerPanel.getHeight();
54838         var bbh = this.footerPanel.getHeight();
54839
54840         if(auto){
54841             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54842             var newHeight = ch + c.getBorderWidth("tb");
54843             if(g.maxHeight){
54844                 newHeight = Math.min(g.maxHeight, newHeight);
54845             }
54846             c.setHeight(newHeight);
54847         }
54848
54849         if(g.autoWidth){
54850             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54851         }
54852
54853         var s = this.scroller;
54854
54855         var csize = c.getSize(true);
54856
54857         this.el.setSize(csize.width, csize.height);
54858
54859         this.headerPanel.setWidth(csize.width);
54860         this.footerPanel.setWidth(csize.width);
54861
54862         var hdHeight = this.mainHd.getHeight();
54863         var vw = csize.width;
54864         var vh = csize.height - (tbh + bbh);
54865
54866         s.setSize(vw, vh);
54867
54868         var bt = this.getBodyTable();
54869         var ltWidth = hasLock ?
54870                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54871
54872         var scrollHeight = bt.offsetHeight;
54873         var scrollWidth = ltWidth + bt.offsetWidth;
54874         var vscroll = false, hscroll = false;
54875
54876         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54877
54878         var lw = this.lockedWrap, mw = this.mainWrap;
54879         var lb = this.lockedBody, mb = this.mainBody;
54880
54881         setTimeout(function(){
54882             var t = s.dom.offsetTop;
54883             var w = s.dom.clientWidth,
54884                 h = s.dom.clientHeight;
54885
54886             lw.setTop(t);
54887             lw.setSize(ltWidth, h);
54888
54889             mw.setLeftTop(ltWidth, t);
54890             mw.setSize(w-ltWidth, h);
54891
54892             lb.setHeight(h-hdHeight);
54893             mb.setHeight(h-hdHeight);
54894
54895             if(is2ndPass !== true && !gv.userResized && expandCol){
54896                 // high speed resize without full column calculation
54897                 
54898                 var ci = cm.getIndexById(expandCol);
54899                 if (ci < 0) {
54900                     ci = cm.findColumnIndex(expandCol);
54901                 }
54902                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54903                 var expandId = cm.getColumnId(ci);
54904                 var  tw = cm.getTotalWidth(false);
54905                 var currentWidth = cm.getColumnWidth(ci);
54906                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54907                 if(currentWidth != cw){
54908                     cm.setColumnWidth(ci, cw, true);
54909                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54910                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54911                     gv.updateSplitters();
54912                     gv.layout(false, true);
54913                 }
54914             }
54915
54916             if(initialRender){
54917                 lw.show();
54918                 mw.show();
54919             }
54920             //c.endMeasure();
54921         }, 10);
54922     },
54923
54924     onWindowResize : function(){
54925         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54926             return;
54927         }
54928         this.layout();
54929     },
54930
54931     appendFooter : function(parentEl){
54932         return null;
54933     },
54934
54935     sortAscText : "Sort Ascending",
54936     sortDescText : "Sort Descending",
54937     lockText : "Lock Column",
54938     unlockText : "Unlock Column",
54939     columnsText : "Columns",
54940  
54941     columnsWiderText : "Wider",
54942     columnsNarrowText : "Thinner"
54943 });
54944
54945
54946 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54947     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54948     this.proxy.el.addClass('x-grid3-col-dd');
54949 };
54950
54951 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54952     handleMouseDown : function(e){
54953
54954     },
54955
54956     callHandleMouseDown : function(e){
54957         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54958     }
54959 });
54960 /*
54961  * Based on:
54962  * Ext JS Library 1.1.1
54963  * Copyright(c) 2006-2007, Ext JS, LLC.
54964  *
54965  * Originally Released Under LGPL - original licence link has changed is not relivant.
54966  *
54967  * Fork - LGPL
54968  * <script type="text/javascript">
54969  */
54970  
54971 // private
54972 // This is a support class used internally by the Grid components
54973 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54974     this.grid = grid;
54975     this.view = grid.getView();
54976     this.proxy = this.view.resizeProxy;
54977     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54978         "gridSplitters" + this.grid.getGridEl().id, {
54979         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54980     });
54981     this.setHandleElId(Roo.id(hd));
54982     this.setOuterHandleElId(Roo.id(hd2));
54983     this.scroll = false;
54984 };
54985 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54986     fly: Roo.Element.fly,
54987
54988     b4StartDrag : function(x, y){
54989         this.view.headersDisabled = true;
54990         this.proxy.setHeight(this.view.mainWrap.getHeight());
54991         var w = this.cm.getColumnWidth(this.cellIndex);
54992         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54993         this.resetConstraints();
54994         this.setXConstraint(minw, 1000);
54995         this.setYConstraint(0, 0);
54996         this.minX = x - minw;
54997         this.maxX = x + 1000;
54998         this.startPos = x;
54999         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55000     },
55001
55002
55003     handleMouseDown : function(e){
55004         ev = Roo.EventObject.setEvent(e);
55005         var t = this.fly(ev.getTarget());
55006         if(t.hasClass("x-grid-split")){
55007             this.cellIndex = this.view.getCellIndex(t.dom);
55008             this.split = t.dom;
55009             this.cm = this.grid.colModel;
55010             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55011                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55012             }
55013         }
55014     },
55015
55016     endDrag : function(e){
55017         this.view.headersDisabled = false;
55018         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55019         var diff = endX - this.startPos;
55020         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55021     },
55022
55023     autoOffset : function(){
55024         this.setDelta(0,0);
55025     }
55026 });/*
55027  * Based on:
55028  * Ext JS Library 1.1.1
55029  * Copyright(c) 2006-2007, Ext JS, LLC.
55030  *
55031  * Originally Released Under LGPL - original licence link has changed is not relivant.
55032  *
55033  * Fork - LGPL
55034  * <script type="text/javascript">
55035  */
55036  
55037 // private
55038 // This is a support class used internally by the Grid components
55039 Roo.grid.GridDragZone = function(grid, config){
55040     this.view = grid.getView();
55041     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55042     if(this.view.lockedBody){
55043         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55044         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55045     }
55046     this.scroll = false;
55047     this.grid = grid;
55048     this.ddel = document.createElement('div');
55049     this.ddel.className = 'x-grid-dd-wrap';
55050 };
55051
55052 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55053     ddGroup : "GridDD",
55054
55055     getDragData : function(e){
55056         var t = Roo.lib.Event.getTarget(e);
55057         var rowIndex = this.view.findRowIndex(t);
55058         var sm = this.grid.selModel;
55059             
55060         //Roo.log(rowIndex);
55061         
55062         if (sm.getSelectedCell) {
55063             // cell selection..
55064             if (!sm.getSelectedCell()) {
55065                 return false;
55066             }
55067             if (rowIndex != sm.getSelectedCell()[0]) {
55068                 return false;
55069             }
55070         
55071         }
55072         
55073         if(rowIndex !== false){
55074             
55075             // if editorgrid.. 
55076             
55077             
55078             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55079                
55080             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55081               //  
55082             //}
55083             if (e.hasModifier()){
55084                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55085             }
55086             
55087             Roo.log("getDragData");
55088             
55089             return {
55090                 grid: this.grid,
55091                 ddel: this.ddel,
55092                 rowIndex: rowIndex,
55093                 selections:sm.getSelections ? sm.getSelections() : (
55094                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55095                 )
55096             };
55097         }
55098         return false;
55099     },
55100
55101     onInitDrag : function(e){
55102         var data = this.dragData;
55103         this.ddel.innerHTML = this.grid.getDragDropText();
55104         this.proxy.update(this.ddel);
55105         // fire start drag?
55106     },
55107
55108     afterRepair : function(){
55109         this.dragging = false;
55110     },
55111
55112     getRepairXY : function(e, data){
55113         return false;
55114     },
55115
55116     onEndDrag : function(data, e){
55117         // fire end drag?
55118     },
55119
55120     onValidDrop : function(dd, e, id){
55121         // fire drag drop?
55122         this.hideProxy();
55123     },
55124
55125     beforeInvalidDrop : function(e, id){
55126
55127     }
55128 });/*
55129  * Based on:
55130  * Ext JS Library 1.1.1
55131  * Copyright(c) 2006-2007, Ext JS, LLC.
55132  *
55133  * Originally Released Under LGPL - original licence link has changed is not relivant.
55134  *
55135  * Fork - LGPL
55136  * <script type="text/javascript">
55137  */
55138  
55139
55140 /**
55141  * @class Roo.grid.ColumnModel
55142  * @extends Roo.util.Observable
55143  * This is the default implementation of a ColumnModel used by the Grid. It defines
55144  * the columns in the grid.
55145  * <br>Usage:<br>
55146  <pre><code>
55147  var colModel = new Roo.grid.ColumnModel([
55148         {header: "Ticker", width: 60, sortable: true, locked: true},
55149         {header: "Company Name", width: 150, sortable: true},
55150         {header: "Market Cap.", width: 100, sortable: true},
55151         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55152         {header: "Employees", width: 100, sortable: true, resizable: false}
55153  ]);
55154  </code></pre>
55155  * <p>
55156  
55157  * The config options listed for this class are options which may appear in each
55158  * individual column definition.
55159  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55160  * @constructor
55161  * @param {Object} config An Array of column config objects. See this class's
55162  * config objects for details.
55163 */
55164 Roo.grid.ColumnModel = function(config){
55165         /**
55166      * The config passed into the constructor
55167      */
55168     this.config = config;
55169     this.lookup = {};
55170
55171     // if no id, create one
55172     // if the column does not have a dataIndex mapping,
55173     // map it to the order it is in the config
55174     for(var i = 0, len = config.length; i < len; i++){
55175         var c = config[i];
55176         if(typeof c.dataIndex == "undefined"){
55177             c.dataIndex = i;
55178         }
55179         if(typeof c.renderer == "string"){
55180             c.renderer = Roo.util.Format[c.renderer];
55181         }
55182         if(typeof c.id == "undefined"){
55183             c.id = Roo.id();
55184         }
55185         if(c.editor && c.editor.xtype){
55186             c.editor  = Roo.factory(c.editor, Roo.grid);
55187         }
55188         if(c.editor && c.editor.isFormField){
55189             c.editor = new Roo.grid.GridEditor(c.editor);
55190         }
55191         this.lookup[c.id] = c;
55192     }
55193
55194     /**
55195      * The width of columns which have no width specified (defaults to 100)
55196      * @type Number
55197      */
55198     this.defaultWidth = 100;
55199
55200     /**
55201      * Default sortable of columns which have no sortable specified (defaults to false)
55202      * @type Boolean
55203      */
55204     this.defaultSortable = false;
55205
55206     this.addEvents({
55207         /**
55208              * @event widthchange
55209              * Fires when the width of a column changes.
55210              * @param {ColumnModel} this
55211              * @param {Number} columnIndex The column index
55212              * @param {Number} newWidth The new width
55213              */
55214             "widthchange": true,
55215         /**
55216              * @event headerchange
55217              * Fires when the text of a header changes.
55218              * @param {ColumnModel} this
55219              * @param {Number} columnIndex The column index
55220              * @param {Number} newText The new header text
55221              */
55222             "headerchange": true,
55223         /**
55224              * @event hiddenchange
55225              * Fires when a column is hidden or "unhidden".
55226              * @param {ColumnModel} this
55227              * @param {Number} columnIndex The column index
55228              * @param {Boolean} hidden true if hidden, false otherwise
55229              */
55230             "hiddenchange": true,
55231             /**
55232          * @event columnmoved
55233          * Fires when a column is moved.
55234          * @param {ColumnModel} this
55235          * @param {Number} oldIndex
55236          * @param {Number} newIndex
55237          */
55238         "columnmoved" : true,
55239         /**
55240          * @event columlockchange
55241          * Fires when a column's locked state is changed
55242          * @param {ColumnModel} this
55243          * @param {Number} colIndex
55244          * @param {Boolean} locked true if locked
55245          */
55246         "columnlockchange" : true
55247     });
55248     Roo.grid.ColumnModel.superclass.constructor.call(this);
55249 };
55250 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55251     /**
55252      * @cfg {String} header The header text to display in the Grid view.
55253      */
55254     /**
55255      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55256      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55257      * specified, the column's index is used as an index into the Record's data Array.
55258      */
55259     /**
55260      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55261      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55262      */
55263     /**
55264      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55265      * Defaults to the value of the {@link #defaultSortable} property.
55266      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55267      */
55268     /**
55269      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55270      */
55271     /**
55272      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55273      */
55274     /**
55275      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55276      */
55277     /**
55278      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55279      */
55280     /**
55281      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55282      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55283      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55284      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55285      */
55286        /**
55287      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55288      */
55289     /**
55290      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55291      */
55292     /**
55293      * @cfg {String} cursor (Optional)
55294      */
55295     /**
55296      * @cfg {String} tooltip (Optional)
55297      */
55298     /**
55299      * Returns the id of the column at the specified index.
55300      * @param {Number} index The column index
55301      * @return {String} the id
55302      */
55303     getColumnId : function(index){
55304         return this.config[index].id;
55305     },
55306
55307     /**
55308      * Returns the column for a specified id.
55309      * @param {String} id The column id
55310      * @return {Object} the column
55311      */
55312     getColumnById : function(id){
55313         return this.lookup[id];
55314     },
55315
55316     
55317     /**
55318      * Returns the column for a specified dataIndex.
55319      * @param {String} dataIndex The column dataIndex
55320      * @return {Object|Boolean} the column or false if not found
55321      */
55322     getColumnByDataIndex: function(dataIndex){
55323         var index = this.findColumnIndex(dataIndex);
55324         return index > -1 ? this.config[index] : false;
55325     },
55326     
55327     /**
55328      * Returns the index for a specified column id.
55329      * @param {String} id The column id
55330      * @return {Number} the index, or -1 if not found
55331      */
55332     getIndexById : function(id){
55333         for(var i = 0, len = this.config.length; i < len; i++){
55334             if(this.config[i].id == id){
55335                 return i;
55336             }
55337         }
55338         return -1;
55339     },
55340     
55341     /**
55342      * Returns the index for a specified column dataIndex.
55343      * @param {String} dataIndex The column dataIndex
55344      * @return {Number} the index, or -1 if not found
55345      */
55346     
55347     findColumnIndex : function(dataIndex){
55348         for(var i = 0, len = this.config.length; i < len; i++){
55349             if(this.config[i].dataIndex == dataIndex){
55350                 return i;
55351             }
55352         }
55353         return -1;
55354     },
55355     
55356     
55357     moveColumn : function(oldIndex, newIndex){
55358         var c = this.config[oldIndex];
55359         this.config.splice(oldIndex, 1);
55360         this.config.splice(newIndex, 0, c);
55361         this.dataMap = null;
55362         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55363     },
55364
55365     isLocked : function(colIndex){
55366         return this.config[colIndex].locked === true;
55367     },
55368
55369     setLocked : function(colIndex, value, suppressEvent){
55370         if(this.isLocked(colIndex) == value){
55371             return;
55372         }
55373         this.config[colIndex].locked = value;
55374         if(!suppressEvent){
55375             this.fireEvent("columnlockchange", this, colIndex, value);
55376         }
55377     },
55378
55379     getTotalLockedWidth : function(){
55380         var totalWidth = 0;
55381         for(var i = 0; i < this.config.length; i++){
55382             if(this.isLocked(i) && !this.isHidden(i)){
55383                 this.totalWidth += this.getColumnWidth(i);
55384             }
55385         }
55386         return totalWidth;
55387     },
55388
55389     getLockedCount : function(){
55390         for(var i = 0, len = this.config.length; i < len; i++){
55391             if(!this.isLocked(i)){
55392                 return i;
55393             }
55394         }
55395     },
55396
55397     /**
55398      * Returns the number of columns.
55399      * @return {Number}
55400      */
55401     getColumnCount : function(visibleOnly){
55402         if(visibleOnly === true){
55403             var c = 0;
55404             for(var i = 0, len = this.config.length; i < len; i++){
55405                 if(!this.isHidden(i)){
55406                     c++;
55407                 }
55408             }
55409             return c;
55410         }
55411         return this.config.length;
55412     },
55413
55414     /**
55415      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55416      * @param {Function} fn
55417      * @param {Object} scope (optional)
55418      * @return {Array} result
55419      */
55420     getColumnsBy : function(fn, scope){
55421         var r = [];
55422         for(var i = 0, len = this.config.length; i < len; i++){
55423             var c = this.config[i];
55424             if(fn.call(scope||this, c, i) === true){
55425                 r[r.length] = c;
55426             }
55427         }
55428         return r;
55429     },
55430
55431     /**
55432      * Returns true if the specified column is sortable.
55433      * @param {Number} col The column index
55434      * @return {Boolean}
55435      */
55436     isSortable : function(col){
55437         if(typeof this.config[col].sortable == "undefined"){
55438             return this.defaultSortable;
55439         }
55440         return this.config[col].sortable;
55441     },
55442
55443     /**
55444      * Returns the rendering (formatting) function defined for the column.
55445      * @param {Number} col The column index.
55446      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55447      */
55448     getRenderer : function(col){
55449         if(!this.config[col].renderer){
55450             return Roo.grid.ColumnModel.defaultRenderer;
55451         }
55452         return this.config[col].renderer;
55453     },
55454
55455     /**
55456      * Sets the rendering (formatting) function for a column.
55457      * @param {Number} col The column index
55458      * @param {Function} fn The function to use to process the cell's raw data
55459      * to return HTML markup for the grid view. The render function is called with
55460      * the following parameters:<ul>
55461      * <li>Data value.</li>
55462      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55463      * <li>css A CSS style string to apply to the table cell.</li>
55464      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55465      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55466      * <li>Row index</li>
55467      * <li>Column index</li>
55468      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55469      */
55470     setRenderer : function(col, fn){
55471         this.config[col].renderer = fn;
55472     },
55473
55474     /**
55475      * Returns the width for the specified column.
55476      * @param {Number} col The column index
55477      * @return {Number}
55478      */
55479     getColumnWidth : function(col){
55480         return this.config[col].width * 1 || this.defaultWidth;
55481     },
55482
55483     /**
55484      * Sets the width for a column.
55485      * @param {Number} col The column index
55486      * @param {Number} width The new width
55487      */
55488     setColumnWidth : function(col, width, suppressEvent){
55489         this.config[col].width = width;
55490         this.totalWidth = null;
55491         if(!suppressEvent){
55492              this.fireEvent("widthchange", this, col, width);
55493         }
55494     },
55495
55496     /**
55497      * Returns the total width of all columns.
55498      * @param {Boolean} includeHidden True to include hidden column widths
55499      * @return {Number}
55500      */
55501     getTotalWidth : function(includeHidden){
55502         if(!this.totalWidth){
55503             this.totalWidth = 0;
55504             for(var i = 0, len = this.config.length; i < len; i++){
55505                 if(includeHidden || !this.isHidden(i)){
55506                     this.totalWidth += this.getColumnWidth(i);
55507                 }
55508             }
55509         }
55510         return this.totalWidth;
55511     },
55512
55513     /**
55514      * Returns the header for the specified column.
55515      * @param {Number} col The column index
55516      * @return {String}
55517      */
55518     getColumnHeader : function(col){
55519         return this.config[col].header;
55520     },
55521
55522     /**
55523      * Sets the header for a column.
55524      * @param {Number} col The column index
55525      * @param {String} header The new header
55526      */
55527     setColumnHeader : function(col, header){
55528         this.config[col].header = header;
55529         this.fireEvent("headerchange", this, col, header);
55530     },
55531
55532     /**
55533      * Returns the tooltip for the specified column.
55534      * @param {Number} col The column index
55535      * @return {String}
55536      */
55537     getColumnTooltip : function(col){
55538             return this.config[col].tooltip;
55539     },
55540     /**
55541      * Sets the tooltip for a column.
55542      * @param {Number} col The column index
55543      * @param {String} tooltip The new tooltip
55544      */
55545     setColumnTooltip : function(col, tooltip){
55546             this.config[col].tooltip = tooltip;
55547     },
55548
55549     /**
55550      * Returns the dataIndex for the specified column.
55551      * @param {Number} col The column index
55552      * @return {Number}
55553      */
55554     getDataIndex : function(col){
55555         return this.config[col].dataIndex;
55556     },
55557
55558     /**
55559      * Sets the dataIndex for a column.
55560      * @param {Number} col The column index
55561      * @param {Number} dataIndex The new dataIndex
55562      */
55563     setDataIndex : function(col, dataIndex){
55564         this.config[col].dataIndex = dataIndex;
55565     },
55566
55567     
55568     
55569     /**
55570      * Returns true if the cell is editable.
55571      * @param {Number} colIndex The column index
55572      * @param {Number} rowIndex The row index
55573      * @return {Boolean}
55574      */
55575     isCellEditable : function(colIndex, rowIndex){
55576         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55577     },
55578
55579     /**
55580      * Returns the editor defined for the cell/column.
55581      * return false or null to disable editing.
55582      * @param {Number} colIndex The column index
55583      * @param {Number} rowIndex The row index
55584      * @return {Object}
55585      */
55586     getCellEditor : function(colIndex, rowIndex){
55587         return this.config[colIndex].editor;
55588     },
55589
55590     /**
55591      * Sets if a column is editable.
55592      * @param {Number} col The column index
55593      * @param {Boolean} editable True if the column is editable
55594      */
55595     setEditable : function(col, editable){
55596         this.config[col].editable = editable;
55597     },
55598
55599
55600     /**
55601      * Returns true if the column is hidden.
55602      * @param {Number} colIndex The column index
55603      * @return {Boolean}
55604      */
55605     isHidden : function(colIndex){
55606         return this.config[colIndex].hidden;
55607     },
55608
55609
55610     /**
55611      * Returns true if the column width cannot be changed
55612      */
55613     isFixed : function(colIndex){
55614         return this.config[colIndex].fixed;
55615     },
55616
55617     /**
55618      * Returns true if the column can be resized
55619      * @return {Boolean}
55620      */
55621     isResizable : function(colIndex){
55622         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55623     },
55624     /**
55625      * Sets if a column is hidden.
55626      * @param {Number} colIndex The column index
55627      * @param {Boolean} hidden True if the column is hidden
55628      */
55629     setHidden : function(colIndex, hidden){
55630         this.config[colIndex].hidden = hidden;
55631         this.totalWidth = null;
55632         this.fireEvent("hiddenchange", this, colIndex, hidden);
55633     },
55634
55635     /**
55636      * Sets the editor for a column.
55637      * @param {Number} col The column index
55638      * @param {Object} editor The editor object
55639      */
55640     setEditor : function(col, editor){
55641         this.config[col].editor = editor;
55642     }
55643 });
55644
55645 Roo.grid.ColumnModel.defaultRenderer = function(value){
55646         if(typeof value == "string" && value.length < 1){
55647             return "&#160;";
55648         }
55649         return value;
55650 };
55651
55652 // Alias for backwards compatibility
55653 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55654 /*
55655  * Based on:
55656  * Ext JS Library 1.1.1
55657  * Copyright(c) 2006-2007, Ext JS, LLC.
55658  *
55659  * Originally Released Under LGPL - original licence link has changed is not relivant.
55660  *
55661  * Fork - LGPL
55662  * <script type="text/javascript">
55663  */
55664
55665 /**
55666  * @class Roo.grid.AbstractSelectionModel
55667  * @extends Roo.util.Observable
55668  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55669  * implemented by descendant classes.  This class should not be directly instantiated.
55670  * @constructor
55671  */
55672 Roo.grid.AbstractSelectionModel = function(){
55673     this.locked = false;
55674     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55675 };
55676
55677 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55678     /** @ignore Called by the grid automatically. Do not call directly. */
55679     init : function(grid){
55680         this.grid = grid;
55681         this.initEvents();
55682     },
55683
55684     /**
55685      * Locks the selections.
55686      */
55687     lock : function(){
55688         this.locked = true;
55689     },
55690
55691     /**
55692      * Unlocks the selections.
55693      */
55694     unlock : function(){
55695         this.locked = false;
55696     },
55697
55698     /**
55699      * Returns true if the selections are locked.
55700      * @return {Boolean}
55701      */
55702     isLocked : function(){
55703         return this.locked;
55704     }
55705 });/*
55706  * Based on:
55707  * Ext JS Library 1.1.1
55708  * Copyright(c) 2006-2007, Ext JS, LLC.
55709  *
55710  * Originally Released Under LGPL - original licence link has changed is not relivant.
55711  *
55712  * Fork - LGPL
55713  * <script type="text/javascript">
55714  */
55715 /**
55716  * @extends Roo.grid.AbstractSelectionModel
55717  * @class Roo.grid.RowSelectionModel
55718  * The default SelectionModel used by {@link Roo.grid.Grid}.
55719  * It supports multiple selections and keyboard selection/navigation. 
55720  * @constructor
55721  * @param {Object} config
55722  */
55723 Roo.grid.RowSelectionModel = function(config){
55724     Roo.apply(this, config);
55725     this.selections = new Roo.util.MixedCollection(false, function(o){
55726         return o.id;
55727     });
55728
55729     this.last = false;
55730     this.lastActive = false;
55731
55732     this.addEvents({
55733         /**
55734              * @event selectionchange
55735              * Fires when the selection changes
55736              * @param {SelectionModel} this
55737              */
55738             "selectionchange" : true,
55739         /**
55740              * @event afterselectionchange
55741              * Fires after the selection changes (eg. by key press or clicking)
55742              * @param {SelectionModel} this
55743              */
55744             "afterselectionchange" : true,
55745         /**
55746              * @event beforerowselect
55747              * Fires when a row is selected being selected, return false to cancel.
55748              * @param {SelectionModel} this
55749              * @param {Number} rowIndex The selected index
55750              * @param {Boolean} keepExisting False if other selections will be cleared
55751              */
55752             "beforerowselect" : true,
55753         /**
55754              * @event rowselect
55755              * Fires when a row is selected.
55756              * @param {SelectionModel} this
55757              * @param {Number} rowIndex The selected index
55758              * @param {Roo.data.Record} r The record
55759              */
55760             "rowselect" : true,
55761         /**
55762              * @event rowdeselect
55763              * Fires when a row is deselected.
55764              * @param {SelectionModel} this
55765              * @param {Number} rowIndex The selected index
55766              */
55767         "rowdeselect" : true
55768     });
55769     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55770     this.locked = false;
55771 };
55772
55773 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55774     /**
55775      * @cfg {Boolean} singleSelect
55776      * True to allow selection of only one row at a time (defaults to false)
55777      */
55778     singleSelect : false,
55779
55780     // private
55781     initEvents : function(){
55782
55783         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55784             this.grid.on("mousedown", this.handleMouseDown, this);
55785         }else{ // allow click to work like normal
55786             this.grid.on("rowclick", this.handleDragableRowClick, this);
55787         }
55788
55789         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55790             "up" : function(e){
55791                 if(!e.shiftKey){
55792                     this.selectPrevious(e.shiftKey);
55793                 }else if(this.last !== false && this.lastActive !== false){
55794                     var last = this.last;
55795                     this.selectRange(this.last,  this.lastActive-1);
55796                     this.grid.getView().focusRow(this.lastActive);
55797                     if(last !== false){
55798                         this.last = last;
55799                     }
55800                 }else{
55801                     this.selectFirstRow();
55802                 }
55803                 this.fireEvent("afterselectionchange", this);
55804             },
55805             "down" : function(e){
55806                 if(!e.shiftKey){
55807                     this.selectNext(e.shiftKey);
55808                 }else if(this.last !== false && this.lastActive !== false){
55809                     var last = this.last;
55810                     this.selectRange(this.last,  this.lastActive+1);
55811                     this.grid.getView().focusRow(this.lastActive);
55812                     if(last !== false){
55813                         this.last = last;
55814                     }
55815                 }else{
55816                     this.selectFirstRow();
55817                 }
55818                 this.fireEvent("afterselectionchange", this);
55819             },
55820             scope: this
55821         });
55822
55823         var view = this.grid.view;
55824         view.on("refresh", this.onRefresh, this);
55825         view.on("rowupdated", this.onRowUpdated, this);
55826         view.on("rowremoved", this.onRemove, this);
55827     },
55828
55829     // private
55830     onRefresh : function(){
55831         var ds = this.grid.dataSource, i, v = this.grid.view;
55832         var s = this.selections;
55833         s.each(function(r){
55834             if((i = ds.indexOfId(r.id)) != -1){
55835                 v.onRowSelect(i);
55836             }else{
55837                 s.remove(r);
55838             }
55839         });
55840     },
55841
55842     // private
55843     onRemove : function(v, index, r){
55844         this.selections.remove(r);
55845     },
55846
55847     // private
55848     onRowUpdated : function(v, index, r){
55849         if(this.isSelected(r)){
55850             v.onRowSelect(index);
55851         }
55852     },
55853
55854     /**
55855      * Select records.
55856      * @param {Array} records The records to select
55857      * @param {Boolean} keepExisting (optional) True to keep existing selections
55858      */
55859     selectRecords : function(records, keepExisting){
55860         if(!keepExisting){
55861             this.clearSelections();
55862         }
55863         var ds = this.grid.dataSource;
55864         for(var i = 0, len = records.length; i < len; i++){
55865             this.selectRow(ds.indexOf(records[i]), true);
55866         }
55867     },
55868
55869     /**
55870      * Gets the number of selected rows.
55871      * @return {Number}
55872      */
55873     getCount : function(){
55874         return this.selections.length;
55875     },
55876
55877     /**
55878      * Selects the first row in the grid.
55879      */
55880     selectFirstRow : function(){
55881         this.selectRow(0);
55882     },
55883
55884     /**
55885      * Select the last row.
55886      * @param {Boolean} keepExisting (optional) True to keep existing selections
55887      */
55888     selectLastRow : function(keepExisting){
55889         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55890     },
55891
55892     /**
55893      * Selects the row immediately following the last selected row.
55894      * @param {Boolean} keepExisting (optional) True to keep existing selections
55895      */
55896     selectNext : function(keepExisting){
55897         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55898             this.selectRow(this.last+1, keepExisting);
55899             this.grid.getView().focusRow(this.last);
55900         }
55901     },
55902
55903     /**
55904      * Selects the row that precedes the last selected row.
55905      * @param {Boolean} keepExisting (optional) True to keep existing selections
55906      */
55907     selectPrevious : function(keepExisting){
55908         if(this.last){
55909             this.selectRow(this.last-1, keepExisting);
55910             this.grid.getView().focusRow(this.last);
55911         }
55912     },
55913
55914     /**
55915      * Returns the selected records
55916      * @return {Array} Array of selected records
55917      */
55918     getSelections : function(){
55919         return [].concat(this.selections.items);
55920     },
55921
55922     /**
55923      * Returns the first selected record.
55924      * @return {Record}
55925      */
55926     getSelected : function(){
55927         return this.selections.itemAt(0);
55928     },
55929
55930
55931     /**
55932      * Clears all selections.
55933      */
55934     clearSelections : function(fast){
55935         if(this.locked) return;
55936         if(fast !== true){
55937             var ds = this.grid.dataSource;
55938             var s = this.selections;
55939             s.each(function(r){
55940                 this.deselectRow(ds.indexOfId(r.id));
55941             }, this);
55942             s.clear();
55943         }else{
55944             this.selections.clear();
55945         }
55946         this.last = false;
55947     },
55948
55949
55950     /**
55951      * Selects all rows.
55952      */
55953     selectAll : function(){
55954         if(this.locked) return;
55955         this.selections.clear();
55956         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55957             this.selectRow(i, true);
55958         }
55959     },
55960
55961     /**
55962      * Returns True if there is a selection.
55963      * @return {Boolean}
55964      */
55965     hasSelection : function(){
55966         return this.selections.length > 0;
55967     },
55968
55969     /**
55970      * Returns True if the specified row is selected.
55971      * @param {Number/Record} record The record or index of the record to check
55972      * @return {Boolean}
55973      */
55974     isSelected : function(index){
55975         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55976         return (r && this.selections.key(r.id) ? true : false);
55977     },
55978
55979     /**
55980      * Returns True if the specified record id is selected.
55981      * @param {String} id The id of record to check
55982      * @return {Boolean}
55983      */
55984     isIdSelected : function(id){
55985         return (this.selections.key(id) ? true : false);
55986     },
55987
55988     // private
55989     handleMouseDown : function(e, t){
55990         var view = this.grid.getView(), rowIndex;
55991         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55992             return;
55993         };
55994         if(e.shiftKey && this.last !== false){
55995             var last = this.last;
55996             this.selectRange(last, rowIndex, e.ctrlKey);
55997             this.last = last; // reset the last
55998             view.focusRow(rowIndex);
55999         }else{
56000             var isSelected = this.isSelected(rowIndex);
56001             if(e.button !== 0 && isSelected){
56002                 view.focusRow(rowIndex);
56003             }else if(e.ctrlKey && isSelected){
56004                 this.deselectRow(rowIndex);
56005             }else if(!isSelected){
56006                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56007                 view.focusRow(rowIndex);
56008             }
56009         }
56010         this.fireEvent("afterselectionchange", this);
56011     },
56012     // private
56013     handleDragableRowClick :  function(grid, rowIndex, e) 
56014     {
56015         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56016             this.selectRow(rowIndex, false);
56017             grid.view.focusRow(rowIndex);
56018              this.fireEvent("afterselectionchange", this);
56019         }
56020     },
56021     
56022     /**
56023      * Selects multiple rows.
56024      * @param {Array} rows Array of the indexes of the row to select
56025      * @param {Boolean} keepExisting (optional) True to keep existing selections
56026      */
56027     selectRows : function(rows, keepExisting){
56028         if(!keepExisting){
56029             this.clearSelections();
56030         }
56031         for(var i = 0, len = rows.length; i < len; i++){
56032             this.selectRow(rows[i], true);
56033         }
56034     },
56035
56036     /**
56037      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56038      * @param {Number} startRow The index of the first row in the range
56039      * @param {Number} endRow The index of the last row in the range
56040      * @param {Boolean} keepExisting (optional) True to retain existing selections
56041      */
56042     selectRange : function(startRow, endRow, keepExisting){
56043         if(this.locked) return;
56044         if(!keepExisting){
56045             this.clearSelections();
56046         }
56047         if(startRow <= endRow){
56048             for(var i = startRow; i <= endRow; i++){
56049                 this.selectRow(i, true);
56050             }
56051         }else{
56052             for(var i = startRow; i >= endRow; i--){
56053                 this.selectRow(i, true);
56054             }
56055         }
56056     },
56057
56058     /**
56059      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56060      * @param {Number} startRow The index of the first row in the range
56061      * @param {Number} endRow The index of the last row in the range
56062      */
56063     deselectRange : function(startRow, endRow, preventViewNotify){
56064         if(this.locked) return;
56065         for(var i = startRow; i <= endRow; i++){
56066             this.deselectRow(i, preventViewNotify);
56067         }
56068     },
56069
56070     /**
56071      * Selects a row.
56072      * @param {Number} row The index of the row to select
56073      * @param {Boolean} keepExisting (optional) True to keep existing selections
56074      */
56075     selectRow : function(index, keepExisting, preventViewNotify){
56076         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56077         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56078             if(!keepExisting || this.singleSelect){
56079                 this.clearSelections();
56080             }
56081             var r = this.grid.dataSource.getAt(index);
56082             this.selections.add(r);
56083             this.last = this.lastActive = index;
56084             if(!preventViewNotify){
56085                 this.grid.getView().onRowSelect(index);
56086             }
56087             this.fireEvent("rowselect", this, index, r);
56088             this.fireEvent("selectionchange", this);
56089         }
56090     },
56091
56092     /**
56093      * Deselects a row.
56094      * @param {Number} row The index of the row to deselect
56095      */
56096     deselectRow : function(index, preventViewNotify){
56097         if(this.locked) return;
56098         if(this.last == index){
56099             this.last = false;
56100         }
56101         if(this.lastActive == index){
56102             this.lastActive = false;
56103         }
56104         var r = this.grid.dataSource.getAt(index);
56105         this.selections.remove(r);
56106         if(!preventViewNotify){
56107             this.grid.getView().onRowDeselect(index);
56108         }
56109         this.fireEvent("rowdeselect", this, index);
56110         this.fireEvent("selectionchange", this);
56111     },
56112
56113     // private
56114     restoreLast : function(){
56115         if(this._last){
56116             this.last = this._last;
56117         }
56118     },
56119
56120     // private
56121     acceptsNav : function(row, col, cm){
56122         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56123     },
56124
56125     // private
56126     onEditorKey : function(field, e){
56127         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56128         if(k == e.TAB){
56129             e.stopEvent();
56130             ed.completeEdit();
56131             if(e.shiftKey){
56132                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56133             }else{
56134                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56135             }
56136         }else if(k == e.ENTER && !e.ctrlKey){
56137             e.stopEvent();
56138             ed.completeEdit();
56139             if(e.shiftKey){
56140                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56141             }else{
56142                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56143             }
56144         }else if(k == e.ESC){
56145             ed.cancelEdit();
56146         }
56147         if(newCell){
56148             g.startEditing(newCell[0], newCell[1]);
56149         }
56150     }
56151 });/*
56152  * Based on:
56153  * Ext JS Library 1.1.1
56154  * Copyright(c) 2006-2007, Ext JS, LLC.
56155  *
56156  * Originally Released Under LGPL - original licence link has changed is not relivant.
56157  *
56158  * Fork - LGPL
56159  * <script type="text/javascript">
56160  */
56161 /**
56162  * @class Roo.grid.CellSelectionModel
56163  * @extends Roo.grid.AbstractSelectionModel
56164  * This class provides the basic implementation for cell selection in a grid.
56165  * @constructor
56166  * @param {Object} config The object containing the configuration of this model.
56167  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56168  */
56169 Roo.grid.CellSelectionModel = function(config){
56170     Roo.apply(this, config);
56171
56172     this.selection = null;
56173
56174     this.addEvents({
56175         /**
56176              * @event beforerowselect
56177              * Fires before a cell is selected.
56178              * @param {SelectionModel} this
56179              * @param {Number} rowIndex The selected row index
56180              * @param {Number} colIndex The selected cell index
56181              */
56182             "beforecellselect" : true,
56183         /**
56184              * @event cellselect
56185              * Fires when a cell is selected.
56186              * @param {SelectionModel} this
56187              * @param {Number} rowIndex The selected row index
56188              * @param {Number} colIndex The selected cell index
56189              */
56190             "cellselect" : true,
56191         /**
56192              * @event selectionchange
56193              * Fires when the active selection changes.
56194              * @param {SelectionModel} this
56195              * @param {Object} selection null for no selection or an object (o) with two properties
56196                 <ul>
56197                 <li>o.record: the record object for the row the selection is in</li>
56198                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56199                 </ul>
56200              */
56201             "selectionchange" : true,
56202         /**
56203              * @event tabend
56204              * Fires when the tab (or enter) was pressed on the last editable cell
56205              * You can use this to trigger add new row.
56206              * @param {SelectionModel} this
56207              */
56208             "tabend" : true,
56209          /**
56210              * @event beforeeditnext
56211              * Fires before the next editable sell is made active
56212              * You can use this to skip to another cell or fire the tabend
56213              *    if you set cell to false
56214              * @param {Object} eventdata object : { cell : [ row, col ] } 
56215              */
56216             "beforeeditnext" : true
56217     });
56218     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56219 };
56220
56221 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56222     
56223     enter_is_tab: false,
56224
56225     /** @ignore */
56226     initEvents : function(){
56227         this.grid.on("mousedown", this.handleMouseDown, this);
56228         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56229         var view = this.grid.view;
56230         view.on("refresh", this.onViewChange, this);
56231         view.on("rowupdated", this.onRowUpdated, this);
56232         view.on("beforerowremoved", this.clearSelections, this);
56233         view.on("beforerowsinserted", this.clearSelections, this);
56234         if(this.grid.isEditor){
56235             this.grid.on("beforeedit", this.beforeEdit,  this);
56236         }
56237     },
56238
56239         //private
56240     beforeEdit : function(e){
56241         this.select(e.row, e.column, false, true, e.record);
56242     },
56243
56244         //private
56245     onRowUpdated : function(v, index, r){
56246         if(this.selection && this.selection.record == r){
56247             v.onCellSelect(index, this.selection.cell[1]);
56248         }
56249     },
56250
56251         //private
56252     onViewChange : function(){
56253         this.clearSelections(true);
56254     },
56255
56256         /**
56257          * Returns the currently selected cell,.
56258          * @return {Array} The selected cell (row, column) or null if none selected.
56259          */
56260     getSelectedCell : function(){
56261         return this.selection ? this.selection.cell : null;
56262     },
56263
56264     /**
56265      * Clears all selections.
56266      * @param {Boolean} true to prevent the gridview from being notified about the change.
56267      */
56268     clearSelections : function(preventNotify){
56269         var s = this.selection;
56270         if(s){
56271             if(preventNotify !== true){
56272                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56273             }
56274             this.selection = null;
56275             this.fireEvent("selectionchange", this, null);
56276         }
56277     },
56278
56279     /**
56280      * Returns true if there is a selection.
56281      * @return {Boolean}
56282      */
56283     hasSelection : function(){
56284         return this.selection ? true : false;
56285     },
56286
56287     /** @ignore */
56288     handleMouseDown : function(e, t){
56289         var v = this.grid.getView();
56290         if(this.isLocked()){
56291             return;
56292         };
56293         var row = v.findRowIndex(t);
56294         var cell = v.findCellIndex(t);
56295         if(row !== false && cell !== false){
56296             this.select(row, cell);
56297         }
56298     },
56299
56300     /**
56301      * Selects a cell.
56302      * @param {Number} rowIndex
56303      * @param {Number} collIndex
56304      */
56305     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56306         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56307             this.clearSelections();
56308             r = r || this.grid.dataSource.getAt(rowIndex);
56309             this.selection = {
56310                 record : r,
56311                 cell : [rowIndex, colIndex]
56312             };
56313             if(!preventViewNotify){
56314                 var v = this.grid.getView();
56315                 v.onCellSelect(rowIndex, colIndex);
56316                 if(preventFocus !== true){
56317                     v.focusCell(rowIndex, colIndex);
56318                 }
56319             }
56320             this.fireEvent("cellselect", this, rowIndex, colIndex);
56321             this.fireEvent("selectionchange", this, this.selection);
56322         }
56323     },
56324
56325         //private
56326     isSelectable : function(rowIndex, colIndex, cm){
56327         return !cm.isHidden(colIndex);
56328     },
56329
56330     /** @ignore */
56331     handleKeyDown : function(e){
56332         //Roo.log('Cell Sel Model handleKeyDown');
56333         if(!e.isNavKeyPress()){
56334             return;
56335         }
56336         var g = this.grid, s = this.selection;
56337         if(!s){
56338             e.stopEvent();
56339             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56340             if(cell){
56341                 this.select(cell[0], cell[1]);
56342             }
56343             return;
56344         }
56345         var sm = this;
56346         var walk = function(row, col, step){
56347             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56348         };
56349         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56350         var newCell;
56351
56352       
56353
56354         switch(k){
56355             case e.TAB:
56356                 // handled by onEditorKey
56357                 if (g.isEditor && g.editing) {
56358                     return;
56359                 }
56360                 if(e.shiftKey) {
56361                     newCell = walk(r, c-1, -1);
56362                 } else {
56363                     newCell = walk(r, c+1, 1);
56364                 }
56365                 break;
56366             
56367             case e.DOWN:
56368                newCell = walk(r+1, c, 1);
56369                 break;
56370             
56371             case e.UP:
56372                 newCell = walk(r-1, c, -1);
56373                 break;
56374             
56375             case e.RIGHT:
56376                 newCell = walk(r, c+1, 1);
56377                 break;
56378             
56379             case e.LEFT:
56380                 newCell = walk(r, c-1, -1);
56381                 break;
56382             
56383             case e.ENTER:
56384                 
56385                 if(g.isEditor && !g.editing){
56386                    g.startEditing(r, c);
56387                    e.stopEvent();
56388                    return;
56389                 }
56390                 
56391                 
56392              break;
56393         };
56394         if(newCell){
56395             this.select(newCell[0], newCell[1]);
56396             e.stopEvent();
56397             
56398         }
56399     },
56400
56401     acceptsNav : function(row, col, cm){
56402         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56403     },
56404     /**
56405      * Selects a cell.
56406      * @param {Number} field (not used) - as it's normally used as a listener
56407      * @param {Number} e - event - fake it by using
56408      *
56409      * var e = Roo.EventObjectImpl.prototype;
56410      * e.keyCode = e.TAB
56411      *
56412      * 
56413      */
56414     onEditorKey : function(field, e){
56415         
56416         var k = e.getKey(),
56417             newCell,
56418             g = this.grid,
56419             ed = g.activeEditor,
56420             forward = false;
56421         ///Roo.log('onEditorKey' + k);
56422         
56423         
56424         if (this.enter_is_tab && k == e.ENTER) {
56425             k = e.TAB;
56426         }
56427         
56428         if(k == e.TAB){
56429             if(e.shiftKey){
56430                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56431             }else{
56432                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56433                 forward = true;
56434             }
56435             
56436             e.stopEvent();
56437             
56438         } else if(k == e.ENTER &&  !e.ctrlKey){
56439             ed.completeEdit();
56440             e.stopEvent();
56441             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56442         
56443                 } else if(k == e.ESC){
56444             ed.cancelEdit();
56445         }
56446                 
56447         if (newCell) {
56448             var ecall = { cell : newCell, forward : forward };
56449             this.fireEvent('beforeeditnext', ecall );
56450             newCell = ecall.cell;
56451                         forward = ecall.forward;
56452         }
56453                 
56454         if(newCell){
56455             //Roo.log('next cell after edit');
56456             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56457         } else if (forward) {
56458             // tabbed past last
56459             this.fireEvent.defer(100, this, ['tabend',this]);
56460         }
56461     }
56462 });/*
56463  * Based on:
56464  * Ext JS Library 1.1.1
56465  * Copyright(c) 2006-2007, Ext JS, LLC.
56466  *
56467  * Originally Released Under LGPL - original licence link has changed is not relivant.
56468  *
56469  * Fork - LGPL
56470  * <script type="text/javascript">
56471  */
56472  
56473 /**
56474  * @class Roo.grid.EditorGrid
56475  * @extends Roo.grid.Grid
56476  * Class for creating and editable grid.
56477  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56478  * The container MUST have some type of size defined for the grid to fill. The container will be 
56479  * automatically set to position relative if it isn't already.
56480  * @param {Object} dataSource The data model to bind to
56481  * @param {Object} colModel The column model with info about this grid's columns
56482  */
56483 Roo.grid.EditorGrid = function(container, config){
56484     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56485     this.getGridEl().addClass("xedit-grid");
56486
56487     if(!this.selModel){
56488         this.selModel = new Roo.grid.CellSelectionModel();
56489     }
56490
56491     this.activeEditor = null;
56492
56493         this.addEvents({
56494             /**
56495              * @event beforeedit
56496              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56497              * <ul style="padding:5px;padding-left:16px;">
56498              * <li>grid - This grid</li>
56499              * <li>record - The record being edited</li>
56500              * <li>field - The field name being edited</li>
56501              * <li>value - The value for the field being edited.</li>
56502              * <li>row - The grid row index</li>
56503              * <li>column - The grid column index</li>
56504              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56505              * </ul>
56506              * @param {Object} e An edit event (see above for description)
56507              */
56508             "beforeedit" : true,
56509             /**
56510              * @event afteredit
56511              * Fires after a cell is edited. <br />
56512              * <ul style="padding:5px;padding-left:16px;">
56513              * <li>grid - This grid</li>
56514              * <li>record - The record being edited</li>
56515              * <li>field - The field name being edited</li>
56516              * <li>value - The value being set</li>
56517              * <li>originalValue - The original value for the field, before the edit.</li>
56518              * <li>row - The grid row index</li>
56519              * <li>column - The grid column index</li>
56520              * </ul>
56521              * @param {Object} e An edit event (see above for description)
56522              */
56523             "afteredit" : true,
56524             /**
56525              * @event validateedit
56526              * Fires after a cell is edited, but before the value is set in the record. 
56527          * You can use this to modify the value being set in the field, Return false
56528              * to cancel the change. The edit event object has the following properties <br />
56529              * <ul style="padding:5px;padding-left:16px;">
56530          * <li>editor - This editor</li>
56531              * <li>grid - This grid</li>
56532              * <li>record - The record being edited</li>
56533              * <li>field - The field name being edited</li>
56534              * <li>value - The value being set</li>
56535              * <li>originalValue - The original value for the field, before the edit.</li>
56536              * <li>row - The grid row index</li>
56537              * <li>column - The grid column index</li>
56538              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56539              * </ul>
56540              * @param {Object} e An edit event (see above for description)
56541              */
56542             "validateedit" : true
56543         });
56544     this.on("bodyscroll", this.stopEditing,  this);
56545     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56546 };
56547
56548 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56549     /**
56550      * @cfg {Number} clicksToEdit
56551      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56552      */
56553     clicksToEdit: 2,
56554
56555     // private
56556     isEditor : true,
56557     // private
56558     trackMouseOver: false, // causes very odd FF errors
56559
56560     onCellDblClick : function(g, row, col){
56561         this.startEditing(row, col);
56562     },
56563
56564     onEditComplete : function(ed, value, startValue){
56565         this.editing = false;
56566         this.activeEditor = null;
56567         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56568         var r = ed.record;
56569         var field = this.colModel.getDataIndex(ed.col);
56570         var e = {
56571             grid: this,
56572             record: r,
56573             field: field,
56574             originalValue: startValue,
56575             value: value,
56576             row: ed.row,
56577             column: ed.col,
56578             cancel:false,
56579             editor: ed
56580         };
56581         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56582         cell.show();
56583           
56584         if(String(value) !== String(startValue)){
56585             
56586             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56587                 r.set(field, e.value);
56588                 // if we are dealing with a combo box..
56589                 // then we also set the 'name' colum to be the displayField
56590                 if (ed.field.displayField && ed.field.name) {
56591                     r.set(ed.field.name, ed.field.el.dom.value);
56592                 }
56593                 
56594                 delete e.cancel; //?? why!!!
56595                 this.fireEvent("afteredit", e);
56596             }
56597         } else {
56598             this.fireEvent("afteredit", e); // always fire it!
56599         }
56600         this.view.focusCell(ed.row, ed.col);
56601     },
56602
56603     /**
56604      * Starts editing the specified for the specified row/column
56605      * @param {Number} rowIndex
56606      * @param {Number} colIndex
56607      */
56608     startEditing : function(row, col){
56609         this.stopEditing();
56610         if(this.colModel.isCellEditable(col, row)){
56611             this.view.ensureVisible(row, col, true);
56612           
56613             var r = this.dataSource.getAt(row);
56614             var field = this.colModel.getDataIndex(col);
56615             var cell = Roo.get(this.view.getCell(row,col));
56616             var e = {
56617                 grid: this,
56618                 record: r,
56619                 field: field,
56620                 value: r.data[field],
56621                 row: row,
56622                 column: col,
56623                 cancel:false 
56624             };
56625             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56626                 this.editing = true;
56627                 var ed = this.colModel.getCellEditor(col, row);
56628                 
56629                 if (!ed) {
56630                     return;
56631                 }
56632                 if(!ed.rendered){
56633                     ed.render(ed.parentEl || document.body);
56634                 }
56635                 ed.field.reset();
56636                
56637                 cell.hide();
56638                 
56639                 (function(){ // complex but required for focus issues in safari, ie and opera
56640                     ed.row = row;
56641                     ed.col = col;
56642                     ed.record = r;
56643                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56644                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56645                     this.activeEditor = ed;
56646                     var v = r.data[field];
56647                     ed.startEdit(this.view.getCell(row, col), v);
56648                     // combo's with 'displayField and name set
56649                     if (ed.field.displayField && ed.field.name) {
56650                         ed.field.el.dom.value = r.data[ed.field.name];
56651                     }
56652                     
56653                     
56654                 }).defer(50, this);
56655             }
56656         }
56657     },
56658         
56659     /**
56660      * Stops any active editing
56661      */
56662     stopEditing : function(){
56663         if(this.activeEditor){
56664             this.activeEditor.completeEdit();
56665         }
56666         this.activeEditor = null;
56667     },
56668         
56669          /**
56670      * Called to get grid's drag proxy text, by default returns this.ddText.
56671      * @return {String}
56672      */
56673     getDragDropText : function(){
56674         var count = this.selModel.getSelectedCell() ? 1 : 0;
56675         return String.format(this.ddText, count, count == 1 ? '' : 's');
56676     }
56677         
56678 });/*
56679  * Based on:
56680  * Ext JS Library 1.1.1
56681  * Copyright(c) 2006-2007, Ext JS, LLC.
56682  *
56683  * Originally Released Under LGPL - original licence link has changed is not relivant.
56684  *
56685  * Fork - LGPL
56686  * <script type="text/javascript">
56687  */
56688
56689 // private - not really -- you end up using it !
56690 // This is a support class used internally by the Grid components
56691
56692 /**
56693  * @class Roo.grid.GridEditor
56694  * @extends Roo.Editor
56695  * Class for creating and editable grid elements.
56696  * @param {Object} config any settings (must include field)
56697  */
56698 Roo.grid.GridEditor = function(field, config){
56699     if (!config && field.field) {
56700         config = field;
56701         field = Roo.factory(config.field, Roo.form);
56702     }
56703     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56704     field.monitorTab = false;
56705 };
56706
56707 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56708     
56709     /**
56710      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56711      */
56712     
56713     alignment: "tl-tl",
56714     autoSize: "width",
56715     hideEl : false,
56716     cls: "x-small-editor x-grid-editor",
56717     shim:false,
56718     shadow:"frame"
56719 });/*
56720  * Based on:
56721  * Ext JS Library 1.1.1
56722  * Copyright(c) 2006-2007, Ext JS, LLC.
56723  *
56724  * Originally Released Under LGPL - original licence link has changed is not relivant.
56725  *
56726  * Fork - LGPL
56727  * <script type="text/javascript">
56728  */
56729   
56730
56731   
56732 Roo.grid.PropertyRecord = Roo.data.Record.create([
56733     {name:'name',type:'string'},  'value'
56734 ]);
56735
56736
56737 Roo.grid.PropertyStore = function(grid, source){
56738     this.grid = grid;
56739     this.store = new Roo.data.Store({
56740         recordType : Roo.grid.PropertyRecord
56741     });
56742     this.store.on('update', this.onUpdate,  this);
56743     if(source){
56744         this.setSource(source);
56745     }
56746     Roo.grid.PropertyStore.superclass.constructor.call(this);
56747 };
56748
56749
56750
56751 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56752     setSource : function(o){
56753         this.source = o;
56754         this.store.removeAll();
56755         var data = [];
56756         for(var k in o){
56757             if(this.isEditableValue(o[k])){
56758                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56759             }
56760         }
56761         this.store.loadRecords({records: data}, {}, true);
56762     },
56763
56764     onUpdate : function(ds, record, type){
56765         if(type == Roo.data.Record.EDIT){
56766             var v = record.data['value'];
56767             var oldValue = record.modified['value'];
56768             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56769                 this.source[record.id] = v;
56770                 record.commit();
56771                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56772             }else{
56773                 record.reject();
56774             }
56775         }
56776     },
56777
56778     getProperty : function(row){
56779        return this.store.getAt(row);
56780     },
56781
56782     isEditableValue: function(val){
56783         if(val && val instanceof Date){
56784             return true;
56785         }else if(typeof val == 'object' || typeof val == 'function'){
56786             return false;
56787         }
56788         return true;
56789     },
56790
56791     setValue : function(prop, value){
56792         this.source[prop] = value;
56793         this.store.getById(prop).set('value', value);
56794     },
56795
56796     getSource : function(){
56797         return this.source;
56798     }
56799 });
56800
56801 Roo.grid.PropertyColumnModel = function(grid, store){
56802     this.grid = grid;
56803     var g = Roo.grid;
56804     g.PropertyColumnModel.superclass.constructor.call(this, [
56805         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56806         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56807     ]);
56808     this.store = store;
56809     this.bselect = Roo.DomHelper.append(document.body, {
56810         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56811             {tag: 'option', value: 'true', html: 'true'},
56812             {tag: 'option', value: 'false', html: 'false'}
56813         ]
56814     });
56815     Roo.id(this.bselect);
56816     var f = Roo.form;
56817     this.editors = {
56818         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56819         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56820         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56821         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56822         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56823     };
56824     this.renderCellDelegate = this.renderCell.createDelegate(this);
56825     this.renderPropDelegate = this.renderProp.createDelegate(this);
56826 };
56827
56828 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56829     
56830     
56831     nameText : 'Name',
56832     valueText : 'Value',
56833     
56834     dateFormat : 'm/j/Y',
56835     
56836     
56837     renderDate : function(dateVal){
56838         return dateVal.dateFormat(this.dateFormat);
56839     },
56840
56841     renderBool : function(bVal){
56842         return bVal ? 'true' : 'false';
56843     },
56844
56845     isCellEditable : function(colIndex, rowIndex){
56846         return colIndex == 1;
56847     },
56848
56849     getRenderer : function(col){
56850         return col == 1 ?
56851             this.renderCellDelegate : this.renderPropDelegate;
56852     },
56853
56854     renderProp : function(v){
56855         return this.getPropertyName(v);
56856     },
56857
56858     renderCell : function(val){
56859         var rv = val;
56860         if(val instanceof Date){
56861             rv = this.renderDate(val);
56862         }else if(typeof val == 'boolean'){
56863             rv = this.renderBool(val);
56864         }
56865         return Roo.util.Format.htmlEncode(rv);
56866     },
56867
56868     getPropertyName : function(name){
56869         var pn = this.grid.propertyNames;
56870         return pn && pn[name] ? pn[name] : name;
56871     },
56872
56873     getCellEditor : function(colIndex, rowIndex){
56874         var p = this.store.getProperty(rowIndex);
56875         var n = p.data['name'], val = p.data['value'];
56876         
56877         if(typeof(this.grid.customEditors[n]) == 'string'){
56878             return this.editors[this.grid.customEditors[n]];
56879         }
56880         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56881             return this.grid.customEditors[n];
56882         }
56883         if(val instanceof Date){
56884             return this.editors['date'];
56885         }else if(typeof val == 'number'){
56886             return this.editors['number'];
56887         }else if(typeof val == 'boolean'){
56888             return this.editors['boolean'];
56889         }else{
56890             return this.editors['string'];
56891         }
56892     }
56893 });
56894
56895 /**
56896  * @class Roo.grid.PropertyGrid
56897  * @extends Roo.grid.EditorGrid
56898  * This class represents the  interface of a component based property grid control.
56899  * <br><br>Usage:<pre><code>
56900  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56901       
56902  });
56903  // set any options
56904  grid.render();
56905  * </code></pre>
56906   
56907  * @constructor
56908  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56909  * The container MUST have some type of size defined for the grid to fill. The container will be
56910  * automatically set to position relative if it isn't already.
56911  * @param {Object} config A config object that sets properties on this grid.
56912  */
56913 Roo.grid.PropertyGrid = function(container, config){
56914     config = config || {};
56915     var store = new Roo.grid.PropertyStore(this);
56916     this.store = store;
56917     var cm = new Roo.grid.PropertyColumnModel(this, store);
56918     store.store.sort('name', 'ASC');
56919     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56920         ds: store.store,
56921         cm: cm,
56922         enableColLock:false,
56923         enableColumnMove:false,
56924         stripeRows:false,
56925         trackMouseOver: false,
56926         clicksToEdit:1
56927     }, config));
56928     this.getGridEl().addClass('x-props-grid');
56929     this.lastEditRow = null;
56930     this.on('columnresize', this.onColumnResize, this);
56931     this.addEvents({
56932          /**
56933              * @event beforepropertychange
56934              * Fires before a property changes (return false to stop?)
56935              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56936              * @param {String} id Record Id
56937              * @param {String} newval New Value
56938          * @param {String} oldval Old Value
56939              */
56940         "beforepropertychange": true,
56941         /**
56942              * @event propertychange
56943              * Fires after a property changes
56944              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56945              * @param {String} id Record Id
56946              * @param {String} newval New Value
56947          * @param {String} oldval Old Value
56948              */
56949         "propertychange": true
56950     });
56951     this.customEditors = this.customEditors || {};
56952 };
56953 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56954     
56955      /**
56956      * @cfg {Object} customEditors map of colnames=> custom editors.
56957      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56958      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56959      * false disables editing of the field.
56960          */
56961     
56962       /**
56963      * @cfg {Object} propertyNames map of property Names to their displayed value
56964          */
56965     
56966     render : function(){
56967         Roo.grid.PropertyGrid.superclass.render.call(this);
56968         this.autoSize.defer(100, this);
56969     },
56970
56971     autoSize : function(){
56972         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56973         if(this.view){
56974             this.view.fitColumns();
56975         }
56976     },
56977
56978     onColumnResize : function(){
56979         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56980         this.autoSize();
56981     },
56982     /**
56983      * Sets the data for the Grid
56984      * accepts a Key => Value object of all the elements avaiable.
56985      * @param {Object} data  to appear in grid.
56986      */
56987     setSource : function(source){
56988         this.store.setSource(source);
56989         //this.autoSize();
56990     },
56991     /**
56992      * Gets all the data from the grid.
56993      * @return {Object} data  data stored in grid
56994      */
56995     getSource : function(){
56996         return this.store.getSource();
56997     }
56998 });/*
56999   
57000  * Licence LGPL
57001  
57002  */
57003  
57004 /**
57005  * @class Roo.grid.Calendar
57006  * @extends Roo.util.Grid
57007  * This class extends the Grid to provide a calendar widget
57008  * <br><br>Usage:<pre><code>
57009  var grid = new Roo.grid.Calendar("my-container-id", {
57010      ds: myDataStore,
57011      cm: myColModel,
57012      selModel: mySelectionModel,
57013      autoSizeColumns: true,
57014      monitorWindowResize: false,
57015      trackMouseOver: true
57016      eventstore : real data store..
57017  });
57018  // set any options
57019  grid.render();
57020   
57021   * @constructor
57022  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57023  * The container MUST have some type of size defined for the grid to fill. The container will be
57024  * automatically set to position relative if it isn't already.
57025  * @param {Object} config A config object that sets properties on this grid.
57026  */
57027 Roo.grid.Calendar = function(container, config){
57028         // initialize the container
57029         this.container = Roo.get(container);
57030         this.container.update("");
57031         this.container.setStyle("overflow", "hidden");
57032     this.container.addClass('x-grid-container');
57033
57034     this.id = this.container.id;
57035
57036     Roo.apply(this, config);
57037     // check and correct shorthanded configs
57038     
57039     var rows = [];
57040     var d =1;
57041     for (var r = 0;r < 6;r++) {
57042         
57043         rows[r]=[];
57044         for (var c =0;c < 7;c++) {
57045             rows[r][c]= '';
57046         }
57047     }
57048     if (this.eventStore) {
57049         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57050         this.eventStore.on('load',this.onLoad, this);
57051         this.eventStore.on('beforeload',this.clearEvents, this);
57052          
57053     }
57054     
57055     this.dataSource = new Roo.data.Store({
57056             proxy: new Roo.data.MemoryProxy(rows),
57057             reader: new Roo.data.ArrayReader({}, [
57058                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57059     });
57060
57061     this.dataSource.load();
57062     this.ds = this.dataSource;
57063     this.ds.xmodule = this.xmodule || false;
57064     
57065     
57066     var cellRender = function(v,x,r)
57067     {
57068         return String.format(
57069             '<div class="fc-day  fc-widget-content"><div>' +
57070                 '<div class="fc-event-container"></div>' +
57071                 '<div class="fc-day-number">{0}</div>'+
57072                 
57073                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57074             '</div></div>', v);
57075     
57076     }
57077     
57078     
57079     this.colModel = new Roo.grid.ColumnModel( [
57080         {
57081             xtype: 'ColumnModel',
57082             xns: Roo.grid,
57083             dataIndex : 'weekday0',
57084             header : 'Sunday',
57085             renderer : cellRender
57086         },
57087         {
57088             xtype: 'ColumnModel',
57089             xns: Roo.grid,
57090             dataIndex : 'weekday1',
57091             header : 'Monday',
57092             renderer : cellRender
57093         },
57094         {
57095             xtype: 'ColumnModel',
57096             xns: Roo.grid,
57097             dataIndex : 'weekday2',
57098             header : 'Tuesday',
57099             renderer : cellRender
57100         },
57101         {
57102             xtype: 'ColumnModel',
57103             xns: Roo.grid,
57104             dataIndex : 'weekday3',
57105             header : 'Wednesday',
57106             renderer : cellRender
57107         },
57108         {
57109             xtype: 'ColumnModel',
57110             xns: Roo.grid,
57111             dataIndex : 'weekday4',
57112             header : 'Thursday',
57113             renderer : cellRender
57114         },
57115         {
57116             xtype: 'ColumnModel',
57117             xns: Roo.grid,
57118             dataIndex : 'weekday5',
57119             header : 'Friday',
57120             renderer : cellRender
57121         },
57122         {
57123             xtype: 'ColumnModel',
57124             xns: Roo.grid,
57125             dataIndex : 'weekday6',
57126             header : 'Saturday',
57127             renderer : cellRender
57128         }
57129     ]);
57130     this.cm = this.colModel;
57131     this.cm.xmodule = this.xmodule || false;
57132  
57133         
57134           
57135     //this.selModel = new Roo.grid.CellSelectionModel();
57136     //this.sm = this.selModel;
57137     //this.selModel.init(this);
57138     
57139     
57140     if(this.width){
57141         this.container.setWidth(this.width);
57142     }
57143
57144     if(this.height){
57145         this.container.setHeight(this.height);
57146     }
57147     /** @private */
57148         this.addEvents({
57149         // raw events
57150         /**
57151          * @event click
57152          * The raw click event for the entire grid.
57153          * @param {Roo.EventObject} e
57154          */
57155         "click" : true,
57156         /**
57157          * @event dblclick
57158          * The raw dblclick event for the entire grid.
57159          * @param {Roo.EventObject} e
57160          */
57161         "dblclick" : true,
57162         /**
57163          * @event contextmenu
57164          * The raw contextmenu event for the entire grid.
57165          * @param {Roo.EventObject} e
57166          */
57167         "contextmenu" : true,
57168         /**
57169          * @event mousedown
57170          * The raw mousedown event for the entire grid.
57171          * @param {Roo.EventObject} e
57172          */
57173         "mousedown" : true,
57174         /**
57175          * @event mouseup
57176          * The raw mouseup event for the entire grid.
57177          * @param {Roo.EventObject} e
57178          */
57179         "mouseup" : true,
57180         /**
57181          * @event mouseover
57182          * The raw mouseover event for the entire grid.
57183          * @param {Roo.EventObject} e
57184          */
57185         "mouseover" : true,
57186         /**
57187          * @event mouseout
57188          * The raw mouseout event for the entire grid.
57189          * @param {Roo.EventObject} e
57190          */
57191         "mouseout" : true,
57192         /**
57193          * @event keypress
57194          * The raw keypress event for the entire grid.
57195          * @param {Roo.EventObject} e
57196          */
57197         "keypress" : true,
57198         /**
57199          * @event keydown
57200          * The raw keydown event for the entire grid.
57201          * @param {Roo.EventObject} e
57202          */
57203         "keydown" : true,
57204
57205         // custom events
57206
57207         /**
57208          * @event cellclick
57209          * Fires when a cell is clicked
57210          * @param {Grid} this
57211          * @param {Number} rowIndex
57212          * @param {Number} columnIndex
57213          * @param {Roo.EventObject} e
57214          */
57215         "cellclick" : true,
57216         /**
57217          * @event celldblclick
57218          * Fires when a cell is double clicked
57219          * @param {Grid} this
57220          * @param {Number} rowIndex
57221          * @param {Number} columnIndex
57222          * @param {Roo.EventObject} e
57223          */
57224         "celldblclick" : true,
57225         /**
57226          * @event rowclick
57227          * Fires when a row is clicked
57228          * @param {Grid} this
57229          * @param {Number} rowIndex
57230          * @param {Roo.EventObject} e
57231          */
57232         "rowclick" : true,
57233         /**
57234          * @event rowdblclick
57235          * Fires when a row is double clicked
57236          * @param {Grid} this
57237          * @param {Number} rowIndex
57238          * @param {Roo.EventObject} e
57239          */
57240         "rowdblclick" : true,
57241         /**
57242          * @event headerclick
57243          * Fires when a header is clicked
57244          * @param {Grid} this
57245          * @param {Number} columnIndex
57246          * @param {Roo.EventObject} e
57247          */
57248         "headerclick" : true,
57249         /**
57250          * @event headerdblclick
57251          * Fires when a header cell is double clicked
57252          * @param {Grid} this
57253          * @param {Number} columnIndex
57254          * @param {Roo.EventObject} e
57255          */
57256         "headerdblclick" : true,
57257         /**
57258          * @event rowcontextmenu
57259          * Fires when a row is right clicked
57260          * @param {Grid} this
57261          * @param {Number} rowIndex
57262          * @param {Roo.EventObject} e
57263          */
57264         "rowcontextmenu" : true,
57265         /**
57266          * @event cellcontextmenu
57267          * Fires when a cell is right clicked
57268          * @param {Grid} this
57269          * @param {Number} rowIndex
57270          * @param {Number} cellIndex
57271          * @param {Roo.EventObject} e
57272          */
57273          "cellcontextmenu" : true,
57274         /**
57275          * @event headercontextmenu
57276          * Fires when a header is right clicked
57277          * @param {Grid} this
57278          * @param {Number} columnIndex
57279          * @param {Roo.EventObject} e
57280          */
57281         "headercontextmenu" : true,
57282         /**
57283          * @event bodyscroll
57284          * Fires when the body element is scrolled
57285          * @param {Number} scrollLeft
57286          * @param {Number} scrollTop
57287          */
57288         "bodyscroll" : true,
57289         /**
57290          * @event columnresize
57291          * Fires when the user resizes a column
57292          * @param {Number} columnIndex
57293          * @param {Number} newSize
57294          */
57295         "columnresize" : true,
57296         /**
57297          * @event columnmove
57298          * Fires when the user moves a column
57299          * @param {Number} oldIndex
57300          * @param {Number} newIndex
57301          */
57302         "columnmove" : true,
57303         /**
57304          * @event startdrag
57305          * Fires when row(s) start being dragged
57306          * @param {Grid} this
57307          * @param {Roo.GridDD} dd The drag drop object
57308          * @param {event} e The raw browser event
57309          */
57310         "startdrag" : true,
57311         /**
57312          * @event enddrag
57313          * Fires when a drag operation is complete
57314          * @param {Grid} this
57315          * @param {Roo.GridDD} dd The drag drop object
57316          * @param {event} e The raw browser event
57317          */
57318         "enddrag" : true,
57319         /**
57320          * @event dragdrop
57321          * Fires when dragged row(s) are dropped on a valid DD target
57322          * @param {Grid} this
57323          * @param {Roo.GridDD} dd The drag drop object
57324          * @param {String} targetId The target drag drop object
57325          * @param {event} e The raw browser event
57326          */
57327         "dragdrop" : true,
57328         /**
57329          * @event dragover
57330          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57331          * @param {Grid} this
57332          * @param {Roo.GridDD} dd The drag drop object
57333          * @param {String} targetId The target drag drop object
57334          * @param {event} e The raw browser event
57335          */
57336         "dragover" : true,
57337         /**
57338          * @event dragenter
57339          *  Fires when the dragged row(s) first cross another DD target while being dragged
57340          * @param {Grid} this
57341          * @param {Roo.GridDD} dd The drag drop object
57342          * @param {String} targetId The target drag drop object
57343          * @param {event} e The raw browser event
57344          */
57345         "dragenter" : true,
57346         /**
57347          * @event dragout
57348          * Fires when the dragged row(s) leave another DD target while being dragged
57349          * @param {Grid} this
57350          * @param {Roo.GridDD} dd The drag drop object
57351          * @param {String} targetId The target drag drop object
57352          * @param {event} e The raw browser event
57353          */
57354         "dragout" : true,
57355         /**
57356          * @event rowclass
57357          * Fires when a row is rendered, so you can change add a style to it.
57358          * @param {GridView} gridview   The grid view
57359          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57360          */
57361         'rowclass' : true,
57362
57363         /**
57364          * @event render
57365          * Fires when the grid is rendered
57366          * @param {Grid} grid
57367          */
57368         'render' : true,
57369             /**
57370              * @event select
57371              * Fires when a date is selected
57372              * @param {DatePicker} this
57373              * @param {Date} date The selected date
57374              */
57375         'select': true,
57376         /**
57377              * @event monthchange
57378              * Fires when the displayed month changes 
57379              * @param {DatePicker} this
57380              * @param {Date} date The selected month
57381              */
57382         'monthchange': true,
57383         /**
57384              * @event evententer
57385              * Fires when mouse over an event
57386              * @param {Calendar} this
57387              * @param {event} Event
57388              */
57389         'evententer': true,
57390         /**
57391              * @event eventleave
57392              * Fires when the mouse leaves an
57393              * @param {Calendar} this
57394              * @param {event}
57395              */
57396         'eventleave': true,
57397         /**
57398              * @event eventclick
57399              * Fires when the mouse click an
57400              * @param {Calendar} this
57401              * @param {event}
57402              */
57403         'eventclick': true,
57404         /**
57405              * @event eventrender
57406              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57407              * @param {Calendar} this
57408              * @param {data} data to be modified
57409              */
57410         'eventrender': true
57411         
57412     });
57413
57414     Roo.grid.Grid.superclass.constructor.call(this);
57415     this.on('render', function() {
57416         this.view.el.addClass('x-grid-cal'); 
57417         
57418         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57419
57420     },this);
57421     
57422     if (!Roo.grid.Calendar.style) {
57423         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57424             
57425             
57426             '.x-grid-cal .x-grid-col' :  {
57427                 height: 'auto !important',
57428                 'vertical-align': 'top'
57429             },
57430             '.x-grid-cal  .fc-event-hori' : {
57431                 height: '14px'
57432             }
57433              
57434             
57435         }, Roo.id());
57436     }
57437
57438     
57439     
57440 };
57441 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57442     /**
57443      * @cfg {Store} eventStore The store that loads events.
57444      */
57445     eventStore : 25,
57446
57447      
57448     activeDate : false,
57449     startDay : 0,
57450     autoWidth : true,
57451     monitorWindowResize : false,
57452
57453     
57454     resizeColumns : function() {
57455         var col = (this.view.el.getWidth() / 7) - 3;
57456         // loop through cols, and setWidth
57457         for(var i =0 ; i < 7 ; i++){
57458             this.cm.setColumnWidth(i, col);
57459         }
57460     },
57461      setDate :function(date) {
57462         
57463         Roo.log('setDate?');
57464         
57465         this.resizeColumns();
57466         var vd = this.activeDate;
57467         this.activeDate = date;
57468 //        if(vd && this.el){
57469 //            var t = date.getTime();
57470 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57471 //                Roo.log('using add remove');
57472 //                
57473 //                this.fireEvent('monthchange', this, date);
57474 //                
57475 //                this.cells.removeClass("fc-state-highlight");
57476 //                this.cells.each(function(c){
57477 //                   if(c.dateValue == t){
57478 //                       c.addClass("fc-state-highlight");
57479 //                       setTimeout(function(){
57480 //                            try{c.dom.firstChild.focus();}catch(e){}
57481 //                       }, 50);
57482 //                       return false;
57483 //                   }
57484 //                   return true;
57485 //                });
57486 //                return;
57487 //            }
57488 //        }
57489         
57490         var days = date.getDaysInMonth();
57491         
57492         var firstOfMonth = date.getFirstDateOfMonth();
57493         var startingPos = firstOfMonth.getDay()-this.startDay;
57494         
57495         if(startingPos < this.startDay){
57496             startingPos += 7;
57497         }
57498         
57499         var pm = date.add(Date.MONTH, -1);
57500         var prevStart = pm.getDaysInMonth()-startingPos;
57501 //        
57502         
57503         
57504         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57505         
57506         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57507         //this.cells.addClassOnOver('fc-state-hover');
57508         
57509         var cells = this.cells.elements;
57510         var textEls = this.textNodes;
57511         
57512         //Roo.each(cells, function(cell){
57513         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57514         //});
57515         
57516         days += startingPos;
57517
57518         // convert everything to numbers so it's fast
57519         var day = 86400000;
57520         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57521         //Roo.log(d);
57522         //Roo.log(pm);
57523         //Roo.log(prevStart);
57524         
57525         var today = new Date().clearTime().getTime();
57526         var sel = date.clearTime().getTime();
57527         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57528         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57529         var ddMatch = this.disabledDatesRE;
57530         var ddText = this.disabledDatesText;
57531         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57532         var ddaysText = this.disabledDaysText;
57533         var format = this.format;
57534         
57535         var setCellClass = function(cal, cell){
57536             
57537             //Roo.log('set Cell Class');
57538             cell.title = "";
57539             var t = d.getTime();
57540             
57541             //Roo.log(d);
57542             
57543             
57544             cell.dateValue = t;
57545             if(t == today){
57546                 cell.className += " fc-today";
57547                 cell.className += " fc-state-highlight";
57548                 cell.title = cal.todayText;
57549             }
57550             if(t == sel){
57551                 // disable highlight in other month..
57552                 cell.className += " fc-state-highlight";
57553                 
57554             }
57555             // disabling
57556             if(t < min) {
57557                 //cell.className = " fc-state-disabled";
57558                 cell.title = cal.minText;
57559                 return;
57560             }
57561             if(t > max) {
57562                 //cell.className = " fc-state-disabled";
57563                 cell.title = cal.maxText;
57564                 return;
57565             }
57566             if(ddays){
57567                 if(ddays.indexOf(d.getDay()) != -1){
57568                     // cell.title = ddaysText;
57569                    // cell.className = " fc-state-disabled";
57570                 }
57571             }
57572             if(ddMatch && format){
57573                 var fvalue = d.dateFormat(format);
57574                 if(ddMatch.test(fvalue)){
57575                     cell.title = ddText.replace("%0", fvalue);
57576                    cell.className = " fc-state-disabled";
57577                 }
57578             }
57579             
57580             if (!cell.initialClassName) {
57581                 cell.initialClassName = cell.dom.className;
57582             }
57583             
57584             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57585         };
57586
57587         var i = 0;
57588         
57589         for(; i < startingPos; i++) {
57590             cells[i].dayName =  (++prevStart);
57591             Roo.log(textEls[i]);
57592             d.setDate(d.getDate()+1);
57593             
57594             //cells[i].className = "fc-past fc-other-month";
57595             setCellClass(this, cells[i]);
57596         }
57597         
57598         var intDay = 0;
57599         
57600         for(; i < days; i++){
57601             intDay = i - startingPos + 1;
57602             cells[i].dayName =  (intDay);
57603             d.setDate(d.getDate()+1);
57604             
57605             cells[i].className = ''; // "x-date-active";
57606             setCellClass(this, cells[i]);
57607         }
57608         var extraDays = 0;
57609         
57610         for(; i < 42; i++) {
57611             //textEls[i].innerHTML = (++extraDays);
57612             
57613             d.setDate(d.getDate()+1);
57614             cells[i].dayName = (++extraDays);
57615             cells[i].className = "fc-future fc-other-month";
57616             setCellClass(this, cells[i]);
57617         }
57618         
57619         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57620         
57621         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57622         
57623         // this will cause all the cells to mis
57624         var rows= [];
57625         var i =0;
57626         for (var r = 0;r < 6;r++) {
57627             for (var c =0;c < 7;c++) {
57628                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57629             }    
57630         }
57631         
57632         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57633         for(i=0;i<cells.length;i++) {
57634             
57635             this.cells.elements[i].dayName = cells[i].dayName ;
57636             this.cells.elements[i].className = cells[i].className;
57637             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57638             this.cells.elements[i].title = cells[i].title ;
57639             this.cells.elements[i].dateValue = cells[i].dateValue ;
57640         }
57641         
57642         
57643         
57644         
57645         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57646         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57647         
57648         ////if(totalRows != 6){
57649             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57650            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57651        // }
57652         
57653         this.fireEvent('monthchange', this, date);
57654         
57655         
57656     },
57657  /**
57658      * Returns the grid's SelectionModel.
57659      * @return {SelectionModel}
57660      */
57661     getSelectionModel : function(){
57662         if(!this.selModel){
57663             this.selModel = new Roo.grid.CellSelectionModel();
57664         }
57665         return this.selModel;
57666     },
57667
57668     load: function() {
57669         this.eventStore.load()
57670         
57671         
57672         
57673     },
57674     
57675     findCell : function(dt) {
57676         dt = dt.clearTime().getTime();
57677         var ret = false;
57678         this.cells.each(function(c){
57679             //Roo.log("check " +c.dateValue + '?=' + dt);
57680             if(c.dateValue == dt){
57681                 ret = c;
57682                 return false;
57683             }
57684             return true;
57685         });
57686         
57687         return ret;
57688     },
57689     
57690     findCells : function(rec) {
57691         var s = rec.data.start_dt.clone().clearTime().getTime();
57692        // Roo.log(s);
57693         var e= rec.data.end_dt.clone().clearTime().getTime();
57694        // Roo.log(e);
57695         var ret = [];
57696         this.cells.each(function(c){
57697              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57698             
57699             if(c.dateValue > e){
57700                 return ;
57701             }
57702             if(c.dateValue < s){
57703                 return ;
57704             }
57705             ret.push(c);
57706         });
57707         
57708         return ret;    
57709     },
57710     
57711     findBestRow: function(cells)
57712     {
57713         var ret = 0;
57714         
57715         for (var i =0 ; i < cells.length;i++) {
57716             ret  = Math.max(cells[i].rows || 0,ret);
57717         }
57718         return ret;
57719         
57720     },
57721     
57722     
57723     addItem : function(rec)
57724     {
57725         // look for vertical location slot in
57726         var cells = this.findCells(rec);
57727         
57728         rec.row = this.findBestRow(cells);
57729         
57730         // work out the location.
57731         
57732         var crow = false;
57733         var rows = [];
57734         for(var i =0; i < cells.length; i++) {
57735             if (!crow) {
57736                 crow = {
57737                     start : cells[i],
57738                     end :  cells[i]
57739                 };
57740                 continue;
57741             }
57742             if (crow.start.getY() == cells[i].getY()) {
57743                 // on same row.
57744                 crow.end = cells[i];
57745                 continue;
57746             }
57747             // different row.
57748             rows.push(crow);
57749             crow = {
57750                 start: cells[i],
57751                 end : cells[i]
57752             };
57753             
57754         }
57755         
57756         rows.push(crow);
57757         rec.els = [];
57758         rec.rows = rows;
57759         rec.cells = cells;
57760         for (var i = 0; i < cells.length;i++) {
57761             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57762             
57763         }
57764         
57765         
57766     },
57767     
57768     clearEvents: function() {
57769         
57770         if (!this.eventStore.getCount()) {
57771             return;
57772         }
57773         // reset number of rows in cells.
57774         Roo.each(this.cells.elements, function(c){
57775             c.rows = 0;
57776         });
57777         
57778         this.eventStore.each(function(e) {
57779             this.clearEvent(e);
57780         },this);
57781         
57782     },
57783     
57784     clearEvent : function(ev)
57785     {
57786         if (ev.els) {
57787             Roo.each(ev.els, function(el) {
57788                 el.un('mouseenter' ,this.onEventEnter, this);
57789                 el.un('mouseleave' ,this.onEventLeave, this);
57790                 el.remove();
57791             },this);
57792             ev.els = [];
57793         }
57794     },
57795     
57796     
57797     renderEvent : function(ev,ctr) {
57798         if (!ctr) {
57799              ctr = this.view.el.select('.fc-event-container',true).first();
57800         }
57801         
57802          
57803         this.clearEvent(ev);
57804             //code
57805        
57806         
57807         
57808         ev.els = [];
57809         var cells = ev.cells;
57810         var rows = ev.rows;
57811         this.fireEvent('eventrender', this, ev);
57812         
57813         for(var i =0; i < rows.length; i++) {
57814             
57815             cls = '';
57816             if (i == 0) {
57817                 cls += ' fc-event-start';
57818             }
57819             if ((i+1) == rows.length) {
57820                 cls += ' fc-event-end';
57821             }
57822             
57823             //Roo.log(ev.data);
57824             // how many rows should it span..
57825             var cg = this.eventTmpl.append(ctr,Roo.apply({
57826                 fccls : cls
57827                 
57828             }, ev.data) , true);
57829             
57830             
57831             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57832             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57833             cg.on('click', this.onEventClick, this, ev);
57834             
57835             ev.els.push(cg);
57836             
57837             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57838             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57839             //Roo.log(cg);
57840              
57841             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57842             cg.setWidth(ebox.right - sbox.x -2);
57843         }
57844     },
57845     
57846     renderEvents: function()
57847     {   
57848         // first make sure there is enough space..
57849         
57850         if (!this.eventTmpl) {
57851             this.eventTmpl = new Roo.Template(
57852                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57853                     '<div class="fc-event-inner">' +
57854                         '<span class="fc-event-time">{time}</span>' +
57855                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57856                     '</div>' +
57857                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57858                 '</div>'
57859             );
57860                 
57861         }
57862                
57863         
57864         
57865         this.cells.each(function(c) {
57866             //Roo.log(c.select('.fc-day-content div',true).first());
57867             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57868         });
57869         
57870         var ctr = this.view.el.select('.fc-event-container',true).first();
57871         
57872         var cls;
57873         this.eventStore.each(function(ev){
57874             
57875             this.renderEvent(ev);
57876              
57877              
57878         }, this);
57879         this.view.layout();
57880         
57881     },
57882     
57883     onEventEnter: function (e, el,event,d) {
57884         this.fireEvent('evententer', this, el, event);
57885     },
57886     
57887     onEventLeave: function (e, el,event,d) {
57888         this.fireEvent('eventleave', this, el, event);
57889     },
57890     
57891     onEventClick: function (e, el,event,d) {
57892         this.fireEvent('eventclick', this, el, event);
57893     },
57894     
57895     onMonthChange: function () {
57896         this.store.load();
57897     },
57898     
57899     onLoad: function () {
57900         
57901         //Roo.log('calendar onload');
57902 //         
57903         if(this.eventStore.getCount() > 0){
57904             
57905            
57906             
57907             this.eventStore.each(function(d){
57908                 
57909                 
57910                 // FIXME..
57911                 var add =   d.data;
57912                 if (typeof(add.end_dt) == 'undefined')  {
57913                     Roo.log("Missing End time in calendar data: ");
57914                     Roo.log(d);
57915                     return;
57916                 }
57917                 if (typeof(add.start_dt) == 'undefined')  {
57918                     Roo.log("Missing Start time in calendar data: ");
57919                     Roo.log(d);
57920                     return;
57921                 }
57922                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57923                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57924                 add.id = add.id || d.id;
57925                 add.title = add.title || '??';
57926                 
57927                 this.addItem(d);
57928                 
57929              
57930             },this);
57931         }
57932         
57933         this.renderEvents();
57934     }
57935     
57936
57937 });
57938 /*
57939  grid : {
57940                 xtype: 'Grid',
57941                 xns: Roo.grid,
57942                 listeners : {
57943                     render : function ()
57944                     {
57945                         _this.grid = this;
57946                         
57947                         if (!this.view.el.hasClass('course-timesheet')) {
57948                             this.view.el.addClass('course-timesheet');
57949                         }
57950                         if (this.tsStyle) {
57951                             this.ds.load({});
57952                             return; 
57953                         }
57954                         Roo.log('width');
57955                         Roo.log(_this.grid.view.el.getWidth());
57956                         
57957                         
57958                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57959                             '.course-timesheet .x-grid-row' : {
57960                                 height: '80px'
57961                             },
57962                             '.x-grid-row td' : {
57963                                 'vertical-align' : 0
57964                             },
57965                             '.course-edit-link' : {
57966                                 'color' : 'blue',
57967                                 'text-overflow' : 'ellipsis',
57968                                 'overflow' : 'hidden',
57969                                 'white-space' : 'nowrap',
57970                                 'cursor' : 'pointer'
57971                             },
57972                             '.sub-link' : {
57973                                 'color' : 'green'
57974                             },
57975                             '.de-act-sup-link' : {
57976                                 'color' : 'purple',
57977                                 'text-decoration' : 'line-through'
57978                             },
57979                             '.de-act-link' : {
57980                                 'color' : 'red',
57981                                 'text-decoration' : 'line-through'
57982                             },
57983                             '.course-timesheet .course-highlight' : {
57984                                 'border-top-style': 'dashed !important',
57985                                 'border-bottom-bottom': 'dashed !important'
57986                             },
57987                             '.course-timesheet .course-item' : {
57988                                 'font-family'   : 'tahoma, arial, helvetica',
57989                                 'font-size'     : '11px',
57990                                 'overflow'      : 'hidden',
57991                                 'padding-left'  : '10px',
57992                                 'padding-right' : '10px',
57993                                 'padding-top' : '10px' 
57994                             }
57995                             
57996                         }, Roo.id());
57997                                 this.ds.load({});
57998                     }
57999                 },
58000                 autoWidth : true,
58001                 monitorWindowResize : false,
58002                 cellrenderer : function(v,x,r)
58003                 {
58004                     return v;
58005                 },
58006                 sm : {
58007                     xtype: 'CellSelectionModel',
58008                     xns: Roo.grid
58009                 },
58010                 dataSource : {
58011                     xtype: 'Store',
58012                     xns: Roo.data,
58013                     listeners : {
58014                         beforeload : function (_self, options)
58015                         {
58016                             options.params = options.params || {};
58017                             options.params._month = _this.monthField.getValue();
58018                             options.params.limit = 9999;
58019                             options.params['sort'] = 'when_dt';    
58020                             options.params['dir'] = 'ASC';    
58021                             this.proxy.loadResponse = this.loadResponse;
58022                             Roo.log("load?");
58023                             //this.addColumns();
58024                         },
58025                         load : function (_self, records, options)
58026                         {
58027                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58028                                 // if you click on the translation.. you can edit it...
58029                                 var el = Roo.get(this);
58030                                 var id = el.dom.getAttribute('data-id');
58031                                 var d = el.dom.getAttribute('data-date');
58032                                 var t = el.dom.getAttribute('data-time');
58033                                 //var id = this.child('span').dom.textContent;
58034                                 
58035                                 //Roo.log(this);
58036                                 Pman.Dialog.CourseCalendar.show({
58037                                     id : id,
58038                                     when_d : d,
58039                                     when_t : t,
58040                                     productitem_active : id ? 1 : 0
58041                                 }, function() {
58042                                     _this.grid.ds.load({});
58043                                 });
58044                            
58045                            });
58046                            
58047                            _this.panel.fireEvent('resize', [ '', '' ]);
58048                         }
58049                     },
58050                     loadResponse : function(o, success, response){
58051                             // this is overridden on before load..
58052                             
58053                             Roo.log("our code?");       
58054                             //Roo.log(success);
58055                             //Roo.log(response)
58056                             delete this.activeRequest;
58057                             if(!success){
58058                                 this.fireEvent("loadexception", this, o, response);
58059                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58060                                 return;
58061                             }
58062                             var result;
58063                             try {
58064                                 result = o.reader.read(response);
58065                             }catch(e){
58066                                 Roo.log("load exception?");
58067                                 this.fireEvent("loadexception", this, o, response, e);
58068                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58069                                 return;
58070                             }
58071                             Roo.log("ready...");        
58072                             // loop through result.records;
58073                             // and set this.tdate[date] = [] << array of records..
58074                             _this.tdata  = {};
58075                             Roo.each(result.records, function(r){
58076                                 //Roo.log(r.data);
58077                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58078                                     _this.tdata[r.data.when_dt.format('j')] = [];
58079                                 }
58080                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58081                             });
58082                             
58083                             //Roo.log(_this.tdata);
58084                             
58085                             result.records = [];
58086                             result.totalRecords = 6;
58087                     
58088                             // let's generate some duumy records for the rows.
58089                             //var st = _this.dateField.getValue();
58090                             
58091                             // work out monday..
58092                             //st = st.add(Date.DAY, -1 * st.format('w'));
58093                             
58094                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58095                             
58096                             var firstOfMonth = date.getFirstDayOfMonth();
58097                             var days = date.getDaysInMonth();
58098                             var d = 1;
58099                             var firstAdded = false;
58100                             for (var i = 0; i < result.totalRecords ; i++) {
58101                                 //var d= st.add(Date.DAY, i);
58102                                 var row = {};
58103                                 var added = 0;
58104                                 for(var w = 0 ; w < 7 ; w++){
58105                                     if(!firstAdded && firstOfMonth != w){
58106                                         continue;
58107                                     }
58108                                     if(d > days){
58109                                         continue;
58110                                     }
58111                                     firstAdded = true;
58112                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58113                                     row['weekday'+w] = String.format(
58114                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58115                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58116                                                     d,
58117                                                     date.format('Y-m-')+dd
58118                                                 );
58119                                     added++;
58120                                     if(typeof(_this.tdata[d]) != 'undefined'){
58121                                         Roo.each(_this.tdata[d], function(r){
58122                                             var is_sub = '';
58123                                             var deactive = '';
58124                                             var id = r.id;
58125                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58126                                             if(r.parent_id*1>0){
58127                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58128                                                 id = r.parent_id;
58129                                             }
58130                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58131                                                 deactive = 'de-act-link';
58132                                             }
58133                                             
58134                                             row['weekday'+w] += String.format(
58135                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58136                                                     id, //0
58137                                                     r.product_id_name, //1
58138                                                     r.when_dt.format('h:ia'), //2
58139                                                     is_sub, //3
58140                                                     deactive, //4
58141                                                     desc // 5
58142                                             );
58143                                         });
58144                                     }
58145                                     d++;
58146                                 }
58147                                 
58148                                 // only do this if something added..
58149                                 if(added > 0){ 
58150                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58151                                 }
58152                                 
58153                                 
58154                                 // push it twice. (second one with an hour..
58155                                 
58156                             }
58157                             //Roo.log(result);
58158                             this.fireEvent("load", this, o, o.request.arg);
58159                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58160                         },
58161                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58162                     proxy : {
58163                         xtype: 'HttpProxy',
58164                         xns: Roo.data,
58165                         method : 'GET',
58166                         url : baseURL + '/Roo/Shop_course.php'
58167                     },
58168                     reader : {
58169                         xtype: 'JsonReader',
58170                         xns: Roo.data,
58171                         id : 'id',
58172                         fields : [
58173                             {
58174                                 'name': 'id',
58175                                 'type': 'int'
58176                             },
58177                             {
58178                                 'name': 'when_dt',
58179                                 'type': 'string'
58180                             },
58181                             {
58182                                 'name': 'end_dt',
58183                                 'type': 'string'
58184                             },
58185                             {
58186                                 'name': 'parent_id',
58187                                 'type': 'int'
58188                             },
58189                             {
58190                                 'name': 'product_id',
58191                                 'type': 'int'
58192                             },
58193                             {
58194                                 'name': 'productitem_id',
58195                                 'type': 'int'
58196                             },
58197                             {
58198                                 'name': 'guid',
58199                                 'type': 'int'
58200                             }
58201                         ]
58202                     }
58203                 },
58204                 toolbar : {
58205                     xtype: 'Toolbar',
58206                     xns: Roo,
58207                     items : [
58208                         {
58209                             xtype: 'Button',
58210                             xns: Roo.Toolbar,
58211                             listeners : {
58212                                 click : function (_self, e)
58213                                 {
58214                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58215                                     sd.setMonth(sd.getMonth()-1);
58216                                     _this.monthField.setValue(sd.format('Y-m-d'));
58217                                     _this.grid.ds.load({});
58218                                 }
58219                             },
58220                             text : "Back"
58221                         },
58222                         {
58223                             xtype: 'Separator',
58224                             xns: Roo.Toolbar
58225                         },
58226                         {
58227                             xtype: 'MonthField',
58228                             xns: Roo.form,
58229                             listeners : {
58230                                 render : function (_self)
58231                                 {
58232                                     _this.monthField = _self;
58233                                    // _this.monthField.set  today
58234                                 },
58235                                 select : function (combo, date)
58236                                 {
58237                                     _this.grid.ds.load({});
58238                                 }
58239                             },
58240                             value : (function() { return new Date(); })()
58241                         },
58242                         {
58243                             xtype: 'Separator',
58244                             xns: Roo.Toolbar
58245                         },
58246                         {
58247                             xtype: 'TextItem',
58248                             xns: Roo.Toolbar,
58249                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58250                         },
58251                         {
58252                             xtype: 'Fill',
58253                             xns: Roo.Toolbar
58254                         },
58255                         {
58256                             xtype: 'Button',
58257                             xns: Roo.Toolbar,
58258                             listeners : {
58259                                 click : function (_self, e)
58260                                 {
58261                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58262                                     sd.setMonth(sd.getMonth()+1);
58263                                     _this.monthField.setValue(sd.format('Y-m-d'));
58264                                     _this.grid.ds.load({});
58265                                 }
58266                             },
58267                             text : "Next"
58268                         }
58269                     ]
58270                 },
58271                  
58272             }
58273         };
58274         
58275         *//*
58276  * Based on:
58277  * Ext JS Library 1.1.1
58278  * Copyright(c) 2006-2007, Ext JS, LLC.
58279  *
58280  * Originally Released Under LGPL - original licence link has changed is not relivant.
58281  *
58282  * Fork - LGPL
58283  * <script type="text/javascript">
58284  */
58285  
58286 /**
58287  * @class Roo.LoadMask
58288  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58289  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58290  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58291  * element's UpdateManager load indicator and will be destroyed after the initial load.
58292  * @constructor
58293  * Create a new LoadMask
58294  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58295  * @param {Object} config The config object
58296  */
58297 Roo.LoadMask = function(el, config){
58298     this.el = Roo.get(el);
58299     Roo.apply(this, config);
58300     if(this.store){
58301         this.store.on('beforeload', this.onBeforeLoad, this);
58302         this.store.on('load', this.onLoad, this);
58303         this.store.on('loadexception', this.onLoadException, this);
58304         this.removeMask = false;
58305     }else{
58306         var um = this.el.getUpdateManager();
58307         um.showLoadIndicator = false; // disable the default indicator
58308         um.on('beforeupdate', this.onBeforeLoad, this);
58309         um.on('update', this.onLoad, this);
58310         um.on('failure', this.onLoad, this);
58311         this.removeMask = true;
58312     }
58313 };
58314
58315 Roo.LoadMask.prototype = {
58316     /**
58317      * @cfg {Boolean} removeMask
58318      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58319      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58320      */
58321     /**
58322      * @cfg {String} msg
58323      * The text to display in a centered loading message box (defaults to 'Loading...')
58324      */
58325     msg : 'Loading...',
58326     /**
58327      * @cfg {String} msgCls
58328      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58329      */
58330     msgCls : 'x-mask-loading',
58331
58332     /**
58333      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58334      * @type Boolean
58335      */
58336     disabled: false,
58337
58338     /**
58339      * Disables the mask to prevent it from being displayed
58340      */
58341     disable : function(){
58342        this.disabled = true;
58343     },
58344
58345     /**
58346      * Enables the mask so that it can be displayed
58347      */
58348     enable : function(){
58349         this.disabled = false;
58350     },
58351     
58352     onLoadException : function()
58353     {
58354         Roo.log(arguments);
58355         
58356         if (typeof(arguments[3]) != 'undefined') {
58357             Roo.MessageBox.alert("Error loading",arguments[3]);
58358         } 
58359         /*
58360         try {
58361             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58362                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58363             }   
58364         } catch(e) {
58365             
58366         }
58367         */
58368     
58369         
58370         
58371         this.el.unmask(this.removeMask);
58372     },
58373     // private
58374     onLoad : function()
58375     {
58376         this.el.unmask(this.removeMask);
58377     },
58378
58379     // private
58380     onBeforeLoad : function(){
58381         if(!this.disabled){
58382             this.el.mask(this.msg, this.msgCls);
58383         }
58384     },
58385
58386     // private
58387     destroy : function(){
58388         if(this.store){
58389             this.store.un('beforeload', this.onBeforeLoad, this);
58390             this.store.un('load', this.onLoad, this);
58391             this.store.un('loadexception', this.onLoadException, this);
58392         }else{
58393             var um = this.el.getUpdateManager();
58394             um.un('beforeupdate', this.onBeforeLoad, this);
58395             um.un('update', this.onLoad, this);
58396             um.un('failure', this.onLoad, this);
58397         }
58398     }
58399 };/*
58400  * Based on:
58401  * Ext JS Library 1.1.1
58402  * Copyright(c) 2006-2007, Ext JS, LLC.
58403  *
58404  * Originally Released Under LGPL - original licence link has changed is not relivant.
58405  *
58406  * Fork - LGPL
58407  * <script type="text/javascript">
58408  */
58409
58410
58411 /**
58412  * @class Roo.XTemplate
58413  * @extends Roo.Template
58414  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58415 <pre><code>
58416 var t = new Roo.XTemplate(
58417         '&lt;select name="{name}"&gt;',
58418                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58419         '&lt;/select&gt;'
58420 );
58421  
58422 // then append, applying the master template values
58423  </code></pre>
58424  *
58425  * Supported features:
58426  *
58427  *  Tags:
58428
58429 <pre><code>
58430       {a_variable} - output encoded.
58431       {a_variable.format:("Y-m-d")} - call a method on the variable
58432       {a_variable:raw} - unencoded output
58433       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58434       {a_variable:this.method_on_template(...)} - call a method on the template object.
58435  
58436 </code></pre>
58437  *  The tpl tag:
58438 <pre><code>
58439         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58440         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58441         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58442         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58443   
58444         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58445         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58446 </code></pre>
58447  *      
58448  */
58449 Roo.XTemplate = function()
58450 {
58451     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58452     if (this.html) {
58453         this.compile();
58454     }
58455 };
58456
58457
58458 Roo.extend(Roo.XTemplate, Roo.Template, {
58459
58460     /**
58461      * The various sub templates
58462      */
58463     tpls : false,
58464     /**
58465      *
58466      * basic tag replacing syntax
58467      * WORD:WORD()
58468      *
58469      * // you can fake an object call by doing this
58470      *  x.t:(test,tesT) 
58471      * 
58472      */
58473     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58474
58475     /**
58476      * compile the template
58477      *
58478      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58479      *
58480      */
58481     compile: function()
58482     {
58483         var s = this.html;
58484      
58485         s = ['<tpl>', s, '</tpl>'].join('');
58486     
58487         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58488             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58489             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58490             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58491             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58492             m,
58493             id     = 0,
58494             tpls   = [];
58495     
58496         while(true == !!(m = s.match(re))){
58497             var forMatch   = m[0].match(nameRe),
58498                 ifMatch   = m[0].match(ifRe),
58499                 execMatch   = m[0].match(execRe),
58500                 namedMatch   = m[0].match(namedRe),
58501                 
58502                 exp  = null, 
58503                 fn   = null,
58504                 exec = null,
58505                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58506                 
58507             if (ifMatch) {
58508                 // if - puts fn into test..
58509                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58510                 if(exp){
58511                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58512                 }
58513             }
58514             
58515             if (execMatch) {
58516                 // exec - calls a function... returns empty if true is  returned.
58517                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58518                 if(exp){
58519                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58520                 }
58521             }
58522             
58523             
58524             if (name) {
58525                 // for = 
58526                 switch(name){
58527                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58528                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58529                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58530                 }
58531             }
58532             var uid = namedMatch ? namedMatch[1] : id;
58533             
58534             
58535             tpls.push({
58536                 id:     namedMatch ? namedMatch[1] : id,
58537                 target: name,
58538                 exec:   exec,
58539                 test:   fn,
58540                 body:   m[1] || ''
58541             });
58542             if (namedMatch) {
58543                 s = s.replace(m[0], '');
58544             } else { 
58545                 s = s.replace(m[0], '{xtpl'+ id + '}');
58546             }
58547             ++id;
58548         }
58549         this.tpls = [];
58550         for(var i = tpls.length-1; i >= 0; --i){
58551             this.compileTpl(tpls[i]);
58552             this.tpls[tpls[i].id] = tpls[i];
58553         }
58554         this.master = tpls[tpls.length-1];
58555         return this;
58556     },
58557     /**
58558      * same as applyTemplate, except it's done to one of the subTemplates
58559      * when using named templates, you can do:
58560      *
58561      * var str = pl.applySubTemplate('your-name', values);
58562      *
58563      * 
58564      * @param {Number} id of the template
58565      * @param {Object} values to apply to template
58566      * @param {Object} parent (normaly the instance of this object)
58567      */
58568     applySubTemplate : function(id, values, parent)
58569     {
58570         
58571         
58572         var t = this.tpls[id];
58573         
58574         
58575         try { 
58576             if(t.test && !t.test.call(this, values, parent)){
58577                 return '';
58578             }
58579         } catch(e) {
58580             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58581             Roo.log(e.toString());
58582             Roo.log(t.test);
58583             return ''
58584         }
58585         try { 
58586             
58587             if(t.exec && t.exec.call(this, values, parent)){
58588                 return '';
58589             }
58590         } catch(e) {
58591             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58592             Roo.log(e.toString());
58593             Roo.log(t.exec);
58594             return ''
58595         }
58596         try {
58597             var vs = t.target ? t.target.call(this, values, parent) : values;
58598             parent = t.target ? values : parent;
58599             if(t.target && vs instanceof Array){
58600                 var buf = [];
58601                 for(var i = 0, len = vs.length; i < len; i++){
58602                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58603                 }
58604                 return buf.join('');
58605             }
58606             return t.compiled.call(this, vs, parent);
58607         } catch (e) {
58608             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58609             Roo.log(e.toString());
58610             Roo.log(t.compiled);
58611             return '';
58612         }
58613     },
58614
58615     compileTpl : function(tpl)
58616     {
58617         var fm = Roo.util.Format;
58618         var useF = this.disableFormats !== true;
58619         var sep = Roo.isGecko ? "+" : ",";
58620         var undef = function(str) {
58621             Roo.log("Property not found :"  + str);
58622             return '';
58623         };
58624         
58625         var fn = function(m, name, format, args)
58626         {
58627             //Roo.log(arguments);
58628             args = args ? args.replace(/\\'/g,"'") : args;
58629             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58630             if (typeof(format) == 'undefined') {
58631                 format= 'htmlEncode';
58632             }
58633             if (format == 'raw' ) {
58634                 format = false;
58635             }
58636             
58637             if(name.substr(0, 4) == 'xtpl'){
58638                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58639             }
58640             
58641             // build an array of options to determine if value is undefined..
58642             
58643             // basically get 'xxxx.yyyy' then do
58644             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58645             //    (function () { Roo.log("Property not found"); return ''; })() :
58646             //    ......
58647             
58648             var udef_ar = [];
58649             var lookfor = '';
58650             Roo.each(name.split('.'), function(st) {
58651                 lookfor += (lookfor.length ? '.': '') + st;
58652                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58653             });
58654             
58655             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58656             
58657             
58658             if(format && useF){
58659                 
58660                 args = args ? ',' + args : "";
58661                  
58662                 if(format.substr(0, 5) != "this."){
58663                     format = "fm." + format + '(';
58664                 }else{
58665                     format = 'this.call("'+ format.substr(5) + '", ';
58666                     args = ", values";
58667                 }
58668                 
58669                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58670             }
58671              
58672             if (args.length) {
58673                 // called with xxyx.yuu:(test,test)
58674                 // change to ()
58675                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58676             }
58677             // raw.. - :raw modifier..
58678             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58679             
58680         };
58681         var body;
58682         // branched to use + in gecko and [].join() in others
58683         if(Roo.isGecko){
58684             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58685                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58686                     "';};};";
58687         }else{
58688             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58689             body.push(tpl.body.replace(/(\r\n|\n)/g,
58690                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58691             body.push("'].join('');};};");
58692             body = body.join('');
58693         }
58694         
58695         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58696        
58697         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58698         eval(body);
58699         
58700         return this;
58701     },
58702
58703     applyTemplate : function(values){
58704         return this.master.compiled.call(this, values, {});
58705         //var s = this.subs;
58706     },
58707
58708     apply : function(){
58709         return this.applyTemplate.apply(this, arguments);
58710     }
58711
58712  });
58713
58714 Roo.XTemplate.from = function(el){
58715     el = Roo.getDom(el);
58716     return new Roo.XTemplate(el.value || el.innerHTML);
58717 };