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         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * 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.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * 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]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 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
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * 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>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
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     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @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..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @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'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @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'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @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'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @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'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @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'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @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'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 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).
4848 <p>
4849 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>
4850
4851 <p>
4852 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.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <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>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <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>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * 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;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @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).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * 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)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * 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).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * 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).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * 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).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * 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).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * 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).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
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         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
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         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * 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.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <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
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @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}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @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.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 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)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * 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.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * 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)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @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:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * 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().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (typeof(name) == 'object') {
9582                 for(var i in name) {
9583                     this.attr(i, name[i]);
9584                 }
9585                 return name;
9586             }
9587             
9588             
9589             if (!this.dom.hasAttribute(name)) {
9590                 return undefined;
9591             }
9592             return this.dom.getAttribute(name);
9593         }
9594         
9595         
9596         
9597     };
9598
9599     var ep = El.prototype;
9600
9601     /**
9602      * Appends an event handler (Shorthand for addListener)
9603      * @param {String}   eventName     The type of event to append
9604      * @param {Function} fn        The method the event invokes
9605      * @param {Object} scope       (optional) The scope (this object) of the fn
9606      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9607      * @method
9608      */
9609     ep.on = ep.addListener;
9610         // backwards compat
9611     ep.mon = ep.addListener;
9612
9613     /**
9614      * Removes an event handler from this element (shorthand for removeListener)
9615      * @param {String} eventName the type of event to remove
9616      * @param {Function} fn the method the event invokes
9617      * @return {Roo.Element} this
9618      * @method
9619      */
9620     ep.un = ep.removeListener;
9621
9622     /**
9623      * true to automatically adjust width and height settings for box-model issues (default to true)
9624      */
9625     ep.autoBoxAdjust = true;
9626
9627     // private
9628     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9629
9630     // private
9631     El.addUnits = function(v, defaultUnit){
9632         if(v === "" || v == "auto"){
9633             return v;
9634         }
9635         if(v === undefined){
9636             return '';
9637         }
9638         if(typeof v == "number" || !El.unitPattern.test(v)){
9639             return v + (defaultUnit || 'px');
9640         }
9641         return v;
9642     };
9643
9644     // special markup used throughout Roo when box wrapping elements
9645     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>';
9646     /**
9647      * Visibility mode constant - Use visibility to hide element
9648      * @static
9649      * @type Number
9650      */
9651     El.VISIBILITY = 1;
9652     /**
9653      * Visibility mode constant - Use display to hide element
9654      * @static
9655      * @type Number
9656      */
9657     El.DISPLAY = 2;
9658
9659     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9660     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9661     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9662
9663
9664
9665     /**
9666      * @private
9667      */
9668     El.cache = {};
9669
9670     var docEl;
9671
9672     /**
9673      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9674      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9675      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9676      * @return {Element} The Element object
9677      * @static
9678      */
9679     El.get = function(el){
9680         var ex, elm, id;
9681         if(!el){ return null; }
9682         if(typeof el == "string"){ // element id
9683             if(!(elm = document.getElementById(el))){
9684                 return null;
9685             }
9686             if(ex = El.cache[el]){
9687                 ex.dom = elm;
9688             }else{
9689                 ex = El.cache[el] = new El(elm);
9690             }
9691             return ex;
9692         }else if(el.tagName){ // dom element
9693             if(!(id = el.id)){
9694                 id = Roo.id(el);
9695             }
9696             if(ex = El.cache[id]){
9697                 ex.dom = el;
9698             }else{
9699                 ex = El.cache[id] = new El(el);
9700             }
9701             return ex;
9702         }else if(el instanceof El){
9703             if(el != docEl){
9704                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9705                                                               // catch case where it hasn't been appended
9706                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9707             }
9708             return el;
9709         }else if(el.isComposite){
9710             return el;
9711         }else if(el instanceof Array){
9712             return El.select(el);
9713         }else if(el == document){
9714             // create a bogus element object representing the document object
9715             if(!docEl){
9716                 var f = function(){};
9717                 f.prototype = El.prototype;
9718                 docEl = new f();
9719                 docEl.dom = document;
9720             }
9721             return docEl;
9722         }
9723         return null;
9724     };
9725
9726     // private
9727     El.uncache = function(el){
9728         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9729             if(a[i]){
9730                 delete El.cache[a[i].id || a[i]];
9731             }
9732         }
9733     };
9734
9735     // private
9736     // Garbage collection - uncache elements/purge listeners on orphaned elements
9737     // so we don't hold a reference and cause the browser to retain them
9738     El.garbageCollect = function(){
9739         if(!Roo.enableGarbageCollector){
9740             clearInterval(El.collectorThread);
9741             return;
9742         }
9743         for(var eid in El.cache){
9744             var el = El.cache[eid], d = el.dom;
9745             // -------------------------------------------------------
9746             // Determining what is garbage:
9747             // -------------------------------------------------------
9748             // !d
9749             // dom node is null, definitely garbage
9750             // -------------------------------------------------------
9751             // !d.parentNode
9752             // no parentNode == direct orphan, definitely garbage
9753             // -------------------------------------------------------
9754             // !d.offsetParent && !document.getElementById(eid)
9755             // display none elements have no offsetParent so we will
9756             // also try to look it up by it's id. However, check
9757             // offsetParent first so we don't do unneeded lookups.
9758             // This enables collection of elements that are not orphans
9759             // directly, but somewhere up the line they have an orphan
9760             // parent.
9761             // -------------------------------------------------------
9762             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9763                 delete El.cache[eid];
9764                 if(d && Roo.enableListenerCollection){
9765                     E.purgeElement(d);
9766                 }
9767             }
9768         }
9769     }
9770     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9771
9772
9773     // dom is optional
9774     El.Flyweight = function(dom){
9775         this.dom = dom;
9776     };
9777     El.Flyweight.prototype = El.prototype;
9778
9779     El._flyweights = {};
9780     /**
9781      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9782      * the dom node can be overwritten by other code.
9783      * @param {String/HTMLElement} el The dom node or id
9784      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9785      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9786      * @static
9787      * @return {Element} The shared Element object
9788      */
9789     El.fly = function(el, named){
9790         named = named || '_global';
9791         el = Roo.getDom(el);
9792         if(!el){
9793             return null;
9794         }
9795         if(!El._flyweights[named]){
9796             El._flyweights[named] = new El.Flyweight();
9797         }
9798         El._flyweights[named].dom = el;
9799         return El._flyweights[named];
9800     };
9801
9802     /**
9803      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9804      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9805      * Shorthand of {@link Roo.Element#get}
9806      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9807      * @return {Element} The Element object
9808      * @member Roo
9809      * @method get
9810      */
9811     Roo.get = El.get;
9812     /**
9813      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9814      * the dom node can be overwritten by other code.
9815      * Shorthand of {@link Roo.Element#fly}
9816      * @param {String/HTMLElement} el The dom node or id
9817      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9818      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9819      * @static
9820      * @return {Element} The shared Element object
9821      * @member Roo
9822      * @method fly
9823      */
9824     Roo.fly = El.fly;
9825
9826     // speedy lookup for elements never to box adjust
9827     var noBoxAdjust = Roo.isStrict ? {
9828         select:1
9829     } : {
9830         input:1, select:1, textarea:1
9831     };
9832     if(Roo.isIE || Roo.isGecko){
9833         noBoxAdjust['button'] = 1;
9834     }
9835
9836
9837     Roo.EventManager.on(window, 'unload', function(){
9838         delete El.cache;
9839         delete El._flyweights;
9840     });
9841 })();
9842
9843
9844
9845
9846 if(Roo.DomQuery){
9847     Roo.Element.selectorFunction = Roo.DomQuery.select;
9848 }
9849
9850 Roo.Element.select = function(selector, unique, root){
9851     var els;
9852     if(typeof selector == "string"){
9853         els = Roo.Element.selectorFunction(selector, root);
9854     }else if(selector.length !== undefined){
9855         els = selector;
9856     }else{
9857         throw "Invalid selector";
9858     }
9859     if(unique === true){
9860         return new Roo.CompositeElement(els);
9861     }else{
9862         return new Roo.CompositeElementLite(els);
9863     }
9864 };
9865 /**
9866  * Selects elements based on the passed CSS selector to enable working on them as 1.
9867  * @param {String/Array} selector The CSS selector or an array of elements
9868  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9869  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9870  * @return {CompositeElementLite/CompositeElement}
9871  * @member Roo
9872  * @method select
9873  */
9874 Roo.select = Roo.Element.select;
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
9889 /*
9890  * Based on:
9891  * Ext JS Library 1.1.1
9892  * Copyright(c) 2006-2007, Ext JS, LLC.
9893  *
9894  * Originally Released Under LGPL - original licence link has changed is not relivant.
9895  *
9896  * Fork - LGPL
9897  * <script type="text/javascript">
9898  */
9899
9900
9901
9902 //Notifies Element that fx methods are available
9903 Roo.enableFx = true;
9904
9905 /**
9906  * @class Roo.Fx
9907  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9908  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9909  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9910  * Element effects to work.</p><br/>
9911  *
9912  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9913  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9914  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9915  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9916  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9917  * expected results and should be done with care.</p><br/>
9918  *
9919  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9920  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9921 <pre>
9922 Value  Description
9923 -----  -----------------------------
9924 tl     The top left corner
9925 t      The center of the top edge
9926 tr     The top right corner
9927 l      The center of the left edge
9928 r      The center of the right edge
9929 bl     The bottom left corner
9930 b      The center of the bottom edge
9931 br     The bottom right corner
9932 </pre>
9933  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9934  * below are common options that can be passed to any Fx method.</b>
9935  * @cfg {Function} callback A function called when the effect is finished
9936  * @cfg {Object} scope The scope of the effect function
9937  * @cfg {String} easing A valid Easing value for the effect
9938  * @cfg {String} afterCls A css class to apply after the effect
9939  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9940  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9941  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9942  * effects that end with the element being visually hidden, ignored otherwise)
9943  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9944  * a function which returns such a specification that will be applied to the Element after the effect finishes
9945  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9946  * @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
9947  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9948  */
9949 Roo.Fx = {
9950         /**
9951          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9952          * origin for the slide effect.  This function automatically handles wrapping the element with
9953          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9954          * Usage:
9955          *<pre><code>
9956 // default: slide the element in from the top
9957 el.slideIn();
9958
9959 // custom: slide the element in from the right with a 2-second duration
9960 el.slideIn('r', { duration: 2 });
9961
9962 // common config options shown with default values
9963 el.slideIn('t', {
9964     easing: 'easeOut',
9965     duration: .5
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideIn : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // fix display to visibility
9981             this.fixDisplay();
9982
9983             // restore values after effect
9984             var r = this.getFxRestore();
9985             var b = this.getBox();
9986             // fixed size for slide
9987             this.setSize(b);
9988
9989             // wrap if needed
9990             var wrap = this.fxWrap(r.pos, o, "hidden");
9991
9992             var st = this.dom.style;
9993             st.visibility = "visible";
9994             st.position = "absolute";
9995
9996             // clear out temp styles after slide and unwrap
9997             var after = function(){
9998                 el.fxUnwrap(wrap, r.pos, o);
9999                 st.width = r.width;
10000                 st.height = r.height;
10001                 el.afterFx(o);
10002             };
10003             // time to calc the positions
10004             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10005
10006             switch(anchor.toLowerCase()){
10007                 case "t":
10008                     wrap.setSize(b.width, 0);
10009                     st.left = st.bottom = "0";
10010                     a = {height: bh};
10011                 break;
10012                 case "l":
10013                     wrap.setSize(0, b.height);
10014                     st.right = st.top = "0";
10015                     a = {width: bw};
10016                 break;
10017                 case "r":
10018                     wrap.setSize(0, b.height);
10019                     wrap.setX(b.right);
10020                     st.left = st.top = "0";
10021                     a = {width: bw, points: pt};
10022                 break;
10023                 case "b":
10024                     wrap.setSize(b.width, 0);
10025                     wrap.setY(b.bottom);
10026                     st.left = st.top = "0";
10027                     a = {height: bh, points: pt};
10028                 break;
10029                 case "tl":
10030                     wrap.setSize(0, 0);
10031                     st.right = st.bottom = "0";
10032                     a = {width: bw, height: bh};
10033                 break;
10034                 case "bl":
10035                     wrap.setSize(0, 0);
10036                     wrap.setY(b.y+b.height);
10037                     st.right = st.top = "0";
10038                     a = {width: bw, height: bh, points: pt};
10039                 break;
10040                 case "br":
10041                     wrap.setSize(0, 0);
10042                     wrap.setXY([b.right, b.bottom]);
10043                     st.left = st.top = "0";
10044                     a = {width: bw, height: bh, points: pt};
10045                 break;
10046                 case "tr":
10047                     wrap.setSize(0, 0);
10048                     wrap.setX(b.x+b.width);
10049                     st.left = st.bottom = "0";
10050                     a = {width: bw, height: bh, points: pt};
10051                 break;
10052             }
10053             this.dom.style.visibility = "visible";
10054             wrap.show();
10055
10056             arguments.callee.anim = wrap.fxanim(a,
10057                 o,
10058                 'motion',
10059                 .5,
10060                 'easeOut', after);
10061         });
10062         return this;
10063     },
10064     
10065         /**
10066          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10067          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10068          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10069          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10070          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10071          * Usage:
10072          *<pre><code>
10073 // default: slide the element out to the top
10074 el.slideOut();
10075
10076 // custom: slide the element out to the right with a 2-second duration
10077 el.slideOut('r', { duration: 2 });
10078
10079 // common config options shown with default values
10080 el.slideOut('t', {
10081     easing: 'easeOut',
10082     duration: .5,
10083     remove: false,
10084     useDisplay: false
10085 });
10086 </code></pre>
10087          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10088          * @param {Object} options (optional) Object literal with any of the Fx config options
10089          * @return {Roo.Element} The Element
10090          */
10091     slideOut : function(anchor, o){
10092         var el = this.getFxEl();
10093         o = o || {};
10094
10095         el.queueFx(o, function(){
10096
10097             anchor = anchor || "t";
10098
10099             // restore values after effect
10100             var r = this.getFxRestore();
10101             
10102             var b = this.getBox();
10103             // fixed size for slide
10104             this.setSize(b);
10105
10106             // wrap if needed
10107             var wrap = this.fxWrap(r.pos, o, "visible");
10108
10109             var st = this.dom.style;
10110             st.visibility = "visible";
10111             st.position = "absolute";
10112
10113             wrap.setSize(b);
10114
10115             var after = function(){
10116                 if(o.useDisplay){
10117                     el.setDisplayed(false);
10118                 }else{
10119                     el.hide();
10120                 }
10121
10122                 el.fxUnwrap(wrap, r.pos, o);
10123
10124                 st.width = r.width;
10125                 st.height = r.height;
10126
10127                 el.afterFx(o);
10128             };
10129
10130             var a, zero = {to: 0};
10131             switch(anchor.toLowerCase()){
10132                 case "t":
10133                     st.left = st.bottom = "0";
10134                     a = {height: zero};
10135                 break;
10136                 case "l":
10137                     st.right = st.top = "0";
10138                     a = {width: zero};
10139                 break;
10140                 case "r":
10141                     st.left = st.top = "0";
10142                     a = {width: zero, points: {to:[b.right, b.y]}};
10143                 break;
10144                 case "b":
10145                     st.left = st.top = "0";
10146                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "tl":
10149                     st.right = st.bottom = "0";
10150                     a = {width: zero, height: zero};
10151                 break;
10152                 case "bl":
10153                     st.right = st.top = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10155                 break;
10156                 case "br":
10157                     st.left = st.top = "0";
10158                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10159                 break;
10160                 case "tr":
10161                     st.left = st.bottom = "0";
10162                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10163                 break;
10164             }
10165
10166             arguments.callee.anim = wrap.fxanim(a,
10167                 o,
10168                 'motion',
10169                 .5,
10170                 "easeOut", after);
10171         });
10172         return this;
10173     },
10174
10175         /**
10176          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10177          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10178          * The element must be removed from the DOM using the 'remove' config option if desired.
10179          * Usage:
10180          *<pre><code>
10181 // default
10182 el.puff();
10183
10184 // common config options shown with default values
10185 el.puff({
10186     easing: 'easeOut',
10187     duration: .5,
10188     remove: false,
10189     useDisplay: false
10190 });
10191 </code></pre>
10192          * @param {Object} options (optional) Object literal with any of the Fx config options
10193          * @return {Roo.Element} The Element
10194          */
10195     puff : function(o){
10196         var el = this.getFxEl();
10197         o = o || {};
10198
10199         el.queueFx(o, function(){
10200             this.clearOpacity();
10201             this.show();
10202
10203             // restore values after effect
10204             var r = this.getFxRestore();
10205             var st = this.dom.style;
10206
10207             var after = function(){
10208                 if(o.useDisplay){
10209                     el.setDisplayed(false);
10210                 }else{
10211                     el.hide();
10212                 }
10213
10214                 el.clearOpacity();
10215
10216                 el.setPositioning(r.pos);
10217                 st.width = r.width;
10218                 st.height = r.height;
10219                 st.fontSize = '';
10220                 el.afterFx(o);
10221             };
10222
10223             var width = this.getWidth();
10224             var height = this.getHeight();
10225
10226             arguments.callee.anim = this.fxanim({
10227                     width : {to: this.adjustWidth(width * 2)},
10228                     height : {to: this.adjustHeight(height * 2)},
10229                     points : {by: [-(width * .5), -(height * .5)]},
10230                     opacity : {to: 0},
10231                     fontSize: {to:200, unit: "%"}
10232                 },
10233                 o,
10234                 'motion',
10235                 .5,
10236                 "easeOut", after);
10237         });
10238         return this;
10239     },
10240
10241         /**
10242          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10243          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10244          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10245          * Usage:
10246          *<pre><code>
10247 // default
10248 el.switchOff();
10249
10250 // all config options shown with default values
10251 el.switchOff({
10252     easing: 'easeIn',
10253     duration: .3,
10254     remove: false,
10255     useDisplay: false
10256 });
10257 </code></pre>
10258          * @param {Object} options (optional) Object literal with any of the Fx config options
10259          * @return {Roo.Element} The Element
10260          */
10261     switchOff : function(o){
10262         var el = this.getFxEl();
10263         o = o || {};
10264
10265         el.queueFx(o, function(){
10266             this.clearOpacity();
10267             this.clip();
10268
10269             // restore values after effect
10270             var r = this.getFxRestore();
10271             var st = this.dom.style;
10272
10273             var after = function(){
10274                 if(o.useDisplay){
10275                     el.setDisplayed(false);
10276                 }else{
10277                     el.hide();
10278                 }
10279
10280                 el.clearOpacity();
10281                 el.setPositioning(r.pos);
10282                 st.width = r.width;
10283                 st.height = r.height;
10284
10285                 el.afterFx(o);
10286             };
10287
10288             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10289                 this.clearOpacity();
10290                 (function(){
10291                     this.fxanim({
10292                         height:{to:1},
10293                         points:{by:[0, this.getHeight() * .5]}
10294                     }, o, 'motion', 0.3, 'easeIn', after);
10295                 }).defer(100, this);
10296             });
10297         });
10298         return this;
10299     },
10300
10301     /**
10302      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10303      * changed using the "attr" config option) and then fading back to the original color. If no original
10304      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10305      * Usage:
10306 <pre><code>
10307 // default: highlight background to yellow
10308 el.highlight();
10309
10310 // custom: highlight foreground text to blue for 2 seconds
10311 el.highlight("0000ff", { attr: 'color', duration: 2 });
10312
10313 // common config options shown with default values
10314 el.highlight("ffff9c", {
10315     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10316     endColor: (current color) or "ffffff",
10317     easing: 'easeIn',
10318     duration: 1
10319 });
10320 </code></pre>
10321      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10322      * @param {Object} options (optional) Object literal with any of the Fx config options
10323      * @return {Roo.Element} The Element
10324      */ 
10325     highlight : function(color, o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330             color = color || "ffff9c";
10331             attr = o.attr || "backgroundColor";
10332
10333             this.clearOpacity();
10334             this.show();
10335
10336             var origColor = this.getColor(attr);
10337             var restoreColor = this.dom.style[attr];
10338             endColor = (o.endColor || origColor) || "ffffff";
10339
10340             var after = function(){
10341                 el.dom.style[attr] = restoreColor;
10342                 el.afterFx(o);
10343             };
10344
10345             var a = {};
10346             a[attr] = {from: color, to: endColor};
10347             arguments.callee.anim = this.fxanim(a,
10348                 o,
10349                 'color',
10350                 1,
10351                 'easeIn', after);
10352         });
10353         return this;
10354     },
10355
10356    /**
10357     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10358     * Usage:
10359 <pre><code>
10360 // default: a single light blue ripple
10361 el.frame();
10362
10363 // custom: 3 red ripples lasting 3 seconds total
10364 el.frame("ff0000", 3, { duration: 3 });
10365
10366 // common config options shown with default values
10367 el.frame("C3DAF9", 1, {
10368     duration: 1 //duration of entire animation (not each individual ripple)
10369     // Note: Easing is not configurable and will be ignored if included
10370 });
10371 </code></pre>
10372     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10373     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10374     * @param {Object} options (optional) Object literal with any of the Fx config options
10375     * @return {Roo.Element} The Element
10376     */
10377     frame : function(color, count, o){
10378         var el = this.getFxEl();
10379         o = o || {};
10380
10381         el.queueFx(o, function(){
10382             color = color || "#C3DAF9";
10383             if(color.length == 6){
10384                 color = "#" + color;
10385             }
10386             count = count || 1;
10387             duration = o.duration || 1;
10388             this.show();
10389
10390             var b = this.getBox();
10391             var animFn = function(){
10392                 var proxy = this.createProxy({
10393
10394                      style:{
10395                         visbility:"hidden",
10396                         position:"absolute",
10397                         "z-index":"35000", // yee haw
10398                         border:"0px solid " + color
10399                      }
10400                   });
10401                 var scale = Roo.isBorderBox ? 2 : 1;
10402                 proxy.animate({
10403                     top:{from:b.y, to:b.y - 20},
10404                     left:{from:b.x, to:b.x - 20},
10405                     borderWidth:{from:0, to:10},
10406                     opacity:{from:1, to:0},
10407                     height:{from:b.height, to:(b.height + (20*scale))},
10408                     width:{from:b.width, to:(b.width + (20*scale))}
10409                 }, duration, function(){
10410                     proxy.remove();
10411                 });
10412                 if(--count > 0){
10413                      animFn.defer((duration/2)*1000, this);
10414                 }else{
10415                     el.afterFx(o);
10416                 }
10417             };
10418             animFn.call(this);
10419         });
10420         return this;
10421     },
10422
10423    /**
10424     * Creates a pause before any subsequent queued effects begin.  If there are
10425     * no effects queued after the pause it will have no effect.
10426     * Usage:
10427 <pre><code>
10428 el.pause(1);
10429 </code></pre>
10430     * @param {Number} seconds The length of time to pause (in seconds)
10431     * @return {Roo.Element} The Element
10432     */
10433     pause : function(seconds){
10434         var el = this.getFxEl();
10435         var o = {};
10436
10437         el.queueFx(o, function(){
10438             setTimeout(function(){
10439                 el.afterFx(o);
10440             }, seconds * 1000);
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10447     * using the "endOpacity" config option.
10448     * Usage:
10449 <pre><code>
10450 // default: fade in from opacity 0 to 100%
10451 el.fadeIn();
10452
10453 // custom: fade in from opacity 0 to 75% over 2 seconds
10454 el.fadeIn({ endOpacity: .75, duration: 2});
10455
10456 // common config options shown with default values
10457 el.fadeIn({
10458     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10459     easing: 'easeOut',
10460     duration: .5
10461 });
10462 </code></pre>
10463     * @param {Object} options (optional) Object literal with any of the Fx config options
10464     * @return {Roo.Element} The Element
10465     */
10466     fadeIn : function(o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469         el.queueFx(o, function(){
10470             this.setOpacity(0);
10471             this.fixDisplay();
10472             this.dom.style.visibility = 'visible';
10473             var to = o.endOpacity || 1;
10474             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10475                 o, null, .5, "easeOut", function(){
10476                 if(to == 1){
10477                     this.clearOpacity();
10478                 }
10479                 el.afterFx(o);
10480             });
10481         });
10482         return this;
10483     },
10484
10485    /**
10486     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10487     * using the "endOpacity" config option.
10488     * Usage:
10489 <pre><code>
10490 // default: fade out from the element's current opacity to 0
10491 el.fadeOut();
10492
10493 // custom: fade out from the element's current opacity to 25% over 2 seconds
10494 el.fadeOut({ endOpacity: .25, duration: 2});
10495
10496 // common config options shown with default values
10497 el.fadeOut({
10498     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10499     easing: 'easeOut',
10500     duration: .5
10501     remove: false,
10502     useDisplay: false
10503 });
10504 </code></pre>
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     fadeOut : function(o){
10509         var el = this.getFxEl();
10510         o = o || {};
10511         el.queueFx(o, function(){
10512             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10513                 o, null, .5, "easeOut", function(){
10514                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10515                      this.dom.style.display = "none";
10516                 }else{
10517                      this.dom.style.visibility = "hidden";
10518                 }
10519                 this.clearOpacity();
10520                 el.afterFx(o);
10521             });
10522         });
10523         return this;
10524     },
10525
10526    /**
10527     * Animates the transition of an element's dimensions from a starting height/width
10528     * to an ending height/width.
10529     * Usage:
10530 <pre><code>
10531 // change height and width to 100x100 pixels
10532 el.scale(100, 100);
10533
10534 // common config options shown with default values.  The height and width will default to
10535 // the element's existing values if passed as null.
10536 el.scale(
10537     [element's width],
10538     [element's height], {
10539     easing: 'easeOut',
10540     duration: .35
10541 });
10542 </code></pre>
10543     * @param {Number} width  The new width (pass undefined to keep the original width)
10544     * @param {Number} height  The new height (pass undefined to keep the original height)
10545     * @param {Object} options (optional) Object literal with any of the Fx config options
10546     * @return {Roo.Element} The Element
10547     */
10548     scale : function(w, h, o){
10549         this.shift(Roo.apply({}, o, {
10550             width: w,
10551             height: h
10552         }));
10553         return this;
10554     },
10555
10556    /**
10557     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10558     * Any of these properties not specified in the config object will not be changed.  This effect 
10559     * requires that at least one new dimension, position or opacity setting must be passed in on
10560     * the config object in order for the function to have any effect.
10561     * Usage:
10562 <pre><code>
10563 // slide the element horizontally to x position 200 while changing the height and opacity
10564 el.shift({ x: 200, height: 50, opacity: .8 });
10565
10566 // common config options shown with default values.
10567 el.shift({
10568     width: [element's width],
10569     height: [element's height],
10570     x: [element's x position],
10571     y: [element's y position],
10572     opacity: [element's opacity],
10573     easing: 'easeOut',
10574     duration: .35
10575 });
10576 </code></pre>
10577     * @param {Object} options  Object literal with any of the Fx config options
10578     * @return {Roo.Element} The Element
10579     */
10580     shift : function(o){
10581         var el = this.getFxEl();
10582         o = o || {};
10583         el.queueFx(o, function(){
10584             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10585             if(w !== undefined){
10586                 a.width = {to: this.adjustWidth(w)};
10587             }
10588             if(h !== undefined){
10589                 a.height = {to: this.adjustHeight(h)};
10590             }
10591             if(x !== undefined || y !== undefined){
10592                 a.points = {to: [
10593                     x !== undefined ? x : this.getX(),
10594                     y !== undefined ? y : this.getY()
10595                 ]};
10596             }
10597             if(op !== undefined){
10598                 a.opacity = {to: op};
10599             }
10600             if(o.xy !== undefined){
10601                 a.points = {to: o.xy};
10602             }
10603             arguments.callee.anim = this.fxanim(a,
10604                 o, 'motion', .35, "easeOut", function(){
10605                 el.afterFx(o);
10606             });
10607         });
10608         return this;
10609     },
10610
10611         /**
10612          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10613          * ending point of the effect.
10614          * Usage:
10615          *<pre><code>
10616 // default: slide the element downward while fading out
10617 el.ghost();
10618
10619 // custom: slide the element out to the right with a 2-second duration
10620 el.ghost('r', { duration: 2 });
10621
10622 // common config options shown with default values
10623 el.ghost('b', {
10624     easing: 'easeOut',
10625     duration: .5
10626     remove: false,
10627     useDisplay: false
10628 });
10629 </code></pre>
10630          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10631          * @param {Object} options (optional) Object literal with any of the Fx config options
10632          * @return {Roo.Element} The Element
10633          */
10634     ghost : function(anchor, o){
10635         var el = this.getFxEl();
10636         o = o || {};
10637
10638         el.queueFx(o, function(){
10639             anchor = anchor || "b";
10640
10641             // restore values after effect
10642             var r = this.getFxRestore();
10643             var w = this.getWidth(),
10644                 h = this.getHeight();
10645
10646             var st = this.dom.style;
10647
10648             var after = function(){
10649                 if(o.useDisplay){
10650                     el.setDisplayed(false);
10651                 }else{
10652                     el.hide();
10653                 }
10654
10655                 el.clearOpacity();
10656                 el.setPositioning(r.pos);
10657                 st.width = r.width;
10658                 st.height = r.height;
10659
10660                 el.afterFx(o);
10661             };
10662
10663             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10664             switch(anchor.toLowerCase()){
10665                 case "t":
10666                     pt.by = [0, -h];
10667                 break;
10668                 case "l":
10669                     pt.by = [-w, 0];
10670                 break;
10671                 case "r":
10672                     pt.by = [w, 0];
10673                 break;
10674                 case "b":
10675                     pt.by = [0, h];
10676                 break;
10677                 case "tl":
10678                     pt.by = [-w, -h];
10679                 break;
10680                 case "bl":
10681                     pt.by = [-w, h];
10682                 break;
10683                 case "br":
10684                     pt.by = [w, h];
10685                 break;
10686                 case "tr":
10687                     pt.by = [w, -h];
10688                 break;
10689             }
10690
10691             arguments.callee.anim = this.fxanim(a,
10692                 o,
10693                 'motion',
10694                 .5,
10695                 "easeOut", after);
10696         });
10697         return this;
10698     },
10699
10700         /**
10701          * Ensures that all effects queued after syncFx is called on the element are
10702          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10703          * @return {Roo.Element} The Element
10704          */
10705     syncFx : function(){
10706         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10707             block : false,
10708             concurrent : true,
10709             stopFx : false
10710         });
10711         return this;
10712     },
10713
10714         /**
10715          * Ensures that all effects queued after sequenceFx is called on the element are
10716          * run in sequence.  This is the opposite of {@link #syncFx}.
10717          * @return {Roo.Element} The Element
10718          */
10719     sequenceFx : function(){
10720         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10721             block : false,
10722             concurrent : false,
10723             stopFx : false
10724         });
10725         return this;
10726     },
10727
10728         /* @private */
10729     nextFx : function(){
10730         var ef = this.fxQueue[0];
10731         if(ef){
10732             ef.call(this);
10733         }
10734     },
10735
10736         /**
10737          * Returns true if the element has any effects actively running or queued, else returns false.
10738          * @return {Boolean} True if element has active effects, else false
10739          */
10740     hasActiveFx : function(){
10741         return this.fxQueue && this.fxQueue[0];
10742     },
10743
10744         /**
10745          * Stops any running effects and clears the element's internal effects queue if it contains
10746          * any additional effects that haven't started yet.
10747          * @return {Roo.Element} The Element
10748          */
10749     stopFx : function(){
10750         if(this.hasActiveFx()){
10751             var cur = this.fxQueue[0];
10752             if(cur && cur.anim && cur.anim.isAnimated()){
10753                 this.fxQueue = [cur]; // clear out others
10754                 cur.anim.stop(true);
10755             }
10756         }
10757         return this;
10758     },
10759
10760         /* @private */
10761     beforeFx : function(o){
10762         if(this.hasActiveFx() && !o.concurrent){
10763            if(o.stopFx){
10764                this.stopFx();
10765                return true;
10766            }
10767            return false;
10768         }
10769         return true;
10770     },
10771
10772         /**
10773          * Returns true if the element is currently blocking so that no other effect can be queued
10774          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10775          * used to ensure that an effect initiated by a user action runs to completion prior to the
10776          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10777          * @return {Boolean} True if blocking, else false
10778          */
10779     hasFxBlock : function(){
10780         var q = this.fxQueue;
10781         return q && q[0] && q[0].block;
10782     },
10783
10784         /* @private */
10785     queueFx : function(o, fn){
10786         if(!this.fxQueue){
10787             this.fxQueue = [];
10788         }
10789         if(!this.hasFxBlock()){
10790             Roo.applyIf(o, this.fxDefaults);
10791             if(!o.concurrent){
10792                 var run = this.beforeFx(o);
10793                 fn.block = o.block;
10794                 this.fxQueue.push(fn);
10795                 if(run){
10796                     this.nextFx();
10797                 }
10798             }else{
10799                 fn.call(this);
10800             }
10801         }
10802         return this;
10803     },
10804
10805         /* @private */
10806     fxWrap : function(pos, o, vis){
10807         var wrap;
10808         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10809             var wrapXY;
10810             if(o.fixPosition){
10811                 wrapXY = this.getXY();
10812             }
10813             var div = document.createElement("div");
10814             div.style.visibility = vis;
10815             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10816             wrap.setPositioning(pos);
10817             if(wrap.getStyle("position") == "static"){
10818                 wrap.position("relative");
10819             }
10820             this.clearPositioning('auto');
10821             wrap.clip();
10822             wrap.dom.appendChild(this.dom);
10823             if(wrapXY){
10824                 wrap.setXY(wrapXY);
10825             }
10826         }
10827         return wrap;
10828     },
10829
10830         /* @private */
10831     fxUnwrap : function(wrap, pos, o){
10832         this.clearPositioning();
10833         this.setPositioning(pos);
10834         if(!o.wrap){
10835             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10836             wrap.remove();
10837         }
10838     },
10839
10840         /* @private */
10841     getFxRestore : function(){
10842         var st = this.dom.style;
10843         return {pos: this.getPositioning(), width: st.width, height : st.height};
10844     },
10845
10846         /* @private */
10847     afterFx : function(o){
10848         if(o.afterStyle){
10849             this.applyStyles(o.afterStyle);
10850         }
10851         if(o.afterCls){
10852             this.addClass(o.afterCls);
10853         }
10854         if(o.remove === true){
10855             this.remove();
10856         }
10857         Roo.callback(o.callback, o.scope, [this]);
10858         if(!o.concurrent){
10859             this.fxQueue.shift();
10860             this.nextFx();
10861         }
10862     },
10863
10864         /* @private */
10865     getFxEl : function(){ // support for composite element fx
10866         return Roo.get(this.dom);
10867     },
10868
10869         /* @private */
10870     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10871         animType = animType || 'run';
10872         opt = opt || {};
10873         var anim = Roo.lib.Anim[animType](
10874             this.dom, args,
10875             (opt.duration || defaultDur) || .35,
10876             (opt.easing || defaultEase) || 'easeOut',
10877             function(){
10878                 Roo.callback(cb, this);
10879             },
10880             this
10881         );
10882         opt.anim = anim;
10883         return anim;
10884     }
10885 };
10886
10887 // backwords compat
10888 Roo.Fx.resize = Roo.Fx.scale;
10889
10890 //When included, Roo.Fx is automatically applied to Element so that all basic
10891 //effects are available directly via the Element API
10892 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10893  * Based on:
10894  * Ext JS Library 1.1.1
10895  * Copyright(c) 2006-2007, Ext JS, LLC.
10896  *
10897  * Originally Released Under LGPL - original licence link has changed is not relivant.
10898  *
10899  * Fork - LGPL
10900  * <script type="text/javascript">
10901  */
10902
10903
10904 /**
10905  * @class Roo.CompositeElement
10906  * Standard composite class. Creates a Roo.Element for every element in the collection.
10907  * <br><br>
10908  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10909  * actions will be performed on all the elements in this collection.</b>
10910  * <br><br>
10911  * All methods return <i>this</i> and can be chained.
10912  <pre><code>
10913  var els = Roo.select("#some-el div.some-class", true);
10914  // or select directly from an existing element
10915  var el = Roo.get('some-el');
10916  el.select('div.some-class', true);
10917
10918  els.setWidth(100); // all elements become 100 width
10919  els.hide(true); // all elements fade out and hide
10920  // or
10921  els.setWidth(100).hide(true);
10922  </code></pre>
10923  */
10924 Roo.CompositeElement = function(els){
10925     this.elements = [];
10926     this.addElements(els);
10927 };
10928 Roo.CompositeElement.prototype = {
10929     isComposite: true,
10930     addElements : function(els){
10931         if(!els) return this;
10932         if(typeof els == "string"){
10933             els = Roo.Element.selectorFunction(els);
10934         }
10935         var yels = this.elements;
10936         var index = yels.length-1;
10937         for(var i = 0, len = els.length; i < len; i++) {
10938                 yels[++index] = Roo.get(els[i]);
10939         }
10940         return this;
10941     },
10942
10943     /**
10944     * Clears this composite and adds the elements returned by the passed selector.
10945     * @param {String/Array} els A string CSS selector, an array of elements or an element
10946     * @return {CompositeElement} this
10947     */
10948     fill : function(els){
10949         this.elements = [];
10950         this.add(els);
10951         return this;
10952     },
10953
10954     /**
10955     * Filters this composite to only elements that match the passed selector.
10956     * @param {String} selector A string CSS selector
10957     * @param {Boolean} inverse return inverse filter (not matches)
10958     * @return {CompositeElement} this
10959     */
10960     filter : function(selector, inverse){
10961         var els = [];
10962         inverse = inverse || false;
10963         this.each(function(el){
10964             var match = inverse ? !el.is(selector) : el.is(selector);
10965             if(match){
10966                 els[els.length] = el.dom;
10967             }
10968         });
10969         this.fill(els);
10970         return this;
10971     },
10972
10973     invoke : function(fn, args){
10974         var els = this.elements;
10975         for(var i = 0, len = els.length; i < len; i++) {
10976                 Roo.Element.prototype[fn].apply(els[i], args);
10977         }
10978         return this;
10979     },
10980     /**
10981     * Adds elements to this composite.
10982     * @param {String/Array} els A string CSS selector, an array of elements or an element
10983     * @return {CompositeElement} this
10984     */
10985     add : function(els){
10986         if(typeof els == "string"){
10987             this.addElements(Roo.Element.selectorFunction(els));
10988         }else if(els.length !== undefined){
10989             this.addElements(els);
10990         }else{
10991             this.addElements([els]);
10992         }
10993         return this;
10994     },
10995     /**
10996     * Calls the passed function passing (el, this, index) for each element in this composite.
10997     * @param {Function} fn The function to call
10998     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10999     * @return {CompositeElement} this
11000     */
11001     each : function(fn, scope){
11002         var els = this.elements;
11003         for(var i = 0, len = els.length; i < len; i++){
11004             if(fn.call(scope || els[i], els[i], this, i) === false) {
11005                 break;
11006             }
11007         }
11008         return this;
11009     },
11010
11011     /**
11012      * Returns the Element object at the specified index
11013      * @param {Number} index
11014      * @return {Roo.Element}
11015      */
11016     item : function(index){
11017         return this.elements[index] || null;
11018     },
11019
11020     /**
11021      * Returns the first Element
11022      * @return {Roo.Element}
11023      */
11024     first : function(){
11025         return this.item(0);
11026     },
11027
11028     /**
11029      * Returns the last Element
11030      * @return {Roo.Element}
11031      */
11032     last : function(){
11033         return this.item(this.elements.length-1);
11034     },
11035
11036     /**
11037      * Returns the number of elements in this composite
11038      * @return Number
11039      */
11040     getCount : function(){
11041         return this.elements.length;
11042     },
11043
11044     /**
11045      * Returns true if this composite contains the passed element
11046      * @return Boolean
11047      */
11048     contains : function(el){
11049         return this.indexOf(el) !== -1;
11050     },
11051
11052     /**
11053      * Returns true if this composite contains the passed element
11054      * @return Boolean
11055      */
11056     indexOf : function(el){
11057         return this.elements.indexOf(Roo.get(el));
11058     },
11059
11060
11061     /**
11062     * Removes the specified element(s).
11063     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11064     * or an array of any of those.
11065     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11066     * @return {CompositeElement} this
11067     */
11068     removeElement : function(el, removeDom){
11069         if(el instanceof Array){
11070             for(var i = 0, len = el.length; i < len; i++){
11071                 this.removeElement(el[i]);
11072             }
11073             return this;
11074         }
11075         var index = typeof el == 'number' ? el : this.indexOf(el);
11076         if(index !== -1){
11077             if(removeDom){
11078                 var d = this.elements[index];
11079                 if(d.dom){
11080                     d.remove();
11081                 }else{
11082                     d.parentNode.removeChild(d);
11083                 }
11084             }
11085             this.elements.splice(index, 1);
11086         }
11087         return this;
11088     },
11089
11090     /**
11091     * Replaces the specified element with the passed element.
11092     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11093     * to replace.
11094     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11095     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11096     * @return {CompositeElement} this
11097     */
11098     replaceElement : function(el, replacement, domReplace){
11099         var index = typeof el == 'number' ? el : this.indexOf(el);
11100         if(index !== -1){
11101             if(domReplace){
11102                 this.elements[index].replaceWith(replacement);
11103             }else{
11104                 this.elements.splice(index, 1, Roo.get(replacement))
11105             }
11106         }
11107         return this;
11108     },
11109
11110     /**
11111      * Removes all elements.
11112      */
11113     clear : function(){
11114         this.elements = [];
11115     }
11116 };
11117 (function(){
11118     Roo.CompositeElement.createCall = function(proto, fnName){
11119         if(!proto[fnName]){
11120             proto[fnName] = function(){
11121                 return this.invoke(fnName, arguments);
11122             };
11123         }
11124     };
11125     for(var fnName in Roo.Element.prototype){
11126         if(typeof Roo.Element.prototype[fnName] == "function"){
11127             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11128         }
11129     };
11130 })();
11131 /*
11132  * Based on:
11133  * Ext JS Library 1.1.1
11134  * Copyright(c) 2006-2007, Ext JS, LLC.
11135  *
11136  * Originally Released Under LGPL - original licence link has changed is not relivant.
11137  *
11138  * Fork - LGPL
11139  * <script type="text/javascript">
11140  */
11141
11142 /**
11143  * @class Roo.CompositeElementLite
11144  * @extends Roo.CompositeElement
11145  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11146  <pre><code>
11147  var els = Roo.select("#some-el div.some-class");
11148  // or select directly from an existing element
11149  var el = Roo.get('some-el');
11150  el.select('div.some-class');
11151
11152  els.setWidth(100); // all elements become 100 width
11153  els.hide(true); // all elements fade out and hide
11154  // or
11155  els.setWidth(100).hide(true);
11156  </code></pre><br><br>
11157  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11158  * actions will be performed on all the elements in this collection.</b>
11159  */
11160 Roo.CompositeElementLite = function(els){
11161     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11162     this.el = new Roo.Element.Flyweight();
11163 };
11164 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11165     addElements : function(els){
11166         if(els){
11167             if(els instanceof Array){
11168                 this.elements = this.elements.concat(els);
11169             }else{
11170                 var yels = this.elements;
11171                 var index = yels.length-1;
11172                 for(var i = 0, len = els.length; i < len; i++) {
11173                     yels[++index] = els[i];
11174                 }
11175             }
11176         }
11177         return this;
11178     },
11179     invoke : function(fn, args){
11180         var els = this.elements;
11181         var el = this.el;
11182         for(var i = 0, len = els.length; i < len; i++) {
11183             el.dom = els[i];
11184                 Roo.Element.prototype[fn].apply(el, args);
11185         }
11186         return this;
11187     },
11188     /**
11189      * Returns a flyweight Element of the dom element object at the specified index
11190      * @param {Number} index
11191      * @return {Roo.Element}
11192      */
11193     item : function(index){
11194         if(!this.elements[index]){
11195             return null;
11196         }
11197         this.el.dom = this.elements[index];
11198         return this.el;
11199     },
11200
11201     // fixes scope with flyweight
11202     addListener : function(eventName, handler, scope, opt){
11203         var els = this.elements;
11204         for(var i = 0, len = els.length; i < len; i++) {
11205             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11206         }
11207         return this;
11208     },
11209
11210     /**
11211     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11212     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11213     * a reference to the dom node, use el.dom.</b>
11214     * @param {Function} fn The function to call
11215     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11216     * @return {CompositeElement} this
11217     */
11218     each : function(fn, scope){
11219         var els = this.elements;
11220         var el = this.el;
11221         for(var i = 0, len = els.length; i < len; i++){
11222             el.dom = els[i];
11223                 if(fn.call(scope || el, el, this, i) === false){
11224                 break;
11225             }
11226         }
11227         return this;
11228     },
11229
11230     indexOf : function(el){
11231         return this.elements.indexOf(Roo.getDom(el));
11232     },
11233
11234     replaceElement : function(el, replacement, domReplace){
11235         var index = typeof el == 'number' ? el : this.indexOf(el);
11236         if(index !== -1){
11237             replacement = Roo.getDom(replacement);
11238             if(domReplace){
11239                 var d = this.elements[index];
11240                 d.parentNode.insertBefore(replacement, d);
11241                 d.parentNode.removeChild(d);
11242             }
11243             this.elements.splice(index, 1, replacement);
11244         }
11245         return this;
11246     }
11247 });
11248 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11249
11250 /*
11251  * Based on:
11252  * Ext JS Library 1.1.1
11253  * Copyright(c) 2006-2007, Ext JS, LLC.
11254  *
11255  * Originally Released Under LGPL - original licence link has changed is not relivant.
11256  *
11257  * Fork - LGPL
11258  * <script type="text/javascript">
11259  */
11260
11261  
11262
11263 /**
11264  * @class Roo.data.Connection
11265  * @extends Roo.util.Observable
11266  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11267  * either to a configured URL, or to a URL specified at request time.<br><br>
11268  * <p>
11269  * Requests made by this class are asynchronous, and will return immediately. No data from
11270  * the server will be available to the statement immediately following the {@link #request} call.
11271  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11272  * <p>
11273  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11274  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11275  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11276  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11277  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11278  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11279  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11280  * standard DOM methods.
11281  * @constructor
11282  * @param {Object} config a configuration object.
11283  */
11284 Roo.data.Connection = function(config){
11285     Roo.apply(this, config);
11286     this.addEvents({
11287         /**
11288          * @event beforerequest
11289          * Fires before a network request is made to retrieve a data object.
11290          * @param {Connection} conn This Connection object.
11291          * @param {Object} options The options config object passed to the {@link #request} method.
11292          */
11293         "beforerequest" : true,
11294         /**
11295          * @event requestcomplete
11296          * Fires if the request was successfully completed.
11297          * @param {Connection} conn This Connection object.
11298          * @param {Object} response The XHR object containing the response data.
11299          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11300          * @param {Object} options The options config object passed to the {@link #request} method.
11301          */
11302         "requestcomplete" : true,
11303         /**
11304          * @event requestexception
11305          * Fires if an error HTTP status was returned from the server.
11306          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11307          * @param {Connection} conn This Connection object.
11308          * @param {Object} response The XHR object containing the response data.
11309          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11310          * @param {Object} options The options config object passed to the {@link #request} method.
11311          */
11312         "requestexception" : true
11313     });
11314     Roo.data.Connection.superclass.constructor.call(this);
11315 };
11316
11317 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11318     /**
11319      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11320      */
11321     /**
11322      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11323      * extra parameters to each request made by this object. (defaults to undefined)
11324      */
11325     /**
11326      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11327      *  to each request made by this object. (defaults to undefined)
11328      */
11329     /**
11330      * @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)
11331      */
11332     /**
11333      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11334      */
11335     timeout : 30000,
11336     /**
11337      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11338      * @type Boolean
11339      */
11340     autoAbort:false,
11341
11342     /**
11343      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11344      * @type Boolean
11345      */
11346     disableCaching: true,
11347
11348     /**
11349      * Sends an HTTP request to a remote server.
11350      * @param {Object} options An object which may contain the following properties:<ul>
11351      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11352      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11353      * request, a url encoded string or a function to call to get either.</li>
11354      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11355      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11356      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11357      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11358      * <li>options {Object} The parameter to the request call.</li>
11359      * <li>success {Boolean} True if the request succeeded.</li>
11360      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11361      * </ul></li>
11362      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11363      * The callback is passed the following parameters:<ul>
11364      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11365      * <li>options {Object} The parameter to the request call.</li>
11366      * </ul></li>
11367      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11368      * The callback is passed the following parameters:<ul>
11369      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11370      * <li>options {Object} The parameter to the request call.</li>
11371      * </ul></li>
11372      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11373      * for the callback function. Defaults to the browser window.</li>
11374      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11375      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11376      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11377      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11378      * params for the post data. Any params will be appended to the URL.</li>
11379      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11380      * </ul>
11381      * @return {Number} transactionId
11382      */
11383     request : function(o){
11384         if(this.fireEvent("beforerequest", this, o) !== false){
11385             var p = o.params;
11386
11387             if(typeof p == "function"){
11388                 p = p.call(o.scope||window, o);
11389             }
11390             if(typeof p == "object"){
11391                 p = Roo.urlEncode(o.params);
11392             }
11393             if(this.extraParams){
11394                 var extras = Roo.urlEncode(this.extraParams);
11395                 p = p ? (p + '&' + extras) : extras;
11396             }
11397
11398             var url = o.url || this.url;
11399             if(typeof url == 'function'){
11400                 url = url.call(o.scope||window, o);
11401             }
11402
11403             if(o.form){
11404                 var form = Roo.getDom(o.form);
11405                 url = url || form.action;
11406
11407                 var enctype = form.getAttribute("enctype");
11408                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11409                     return this.doFormUpload(o, p, url);
11410                 }
11411                 var f = Roo.lib.Ajax.serializeForm(form);
11412                 p = p ? (p + '&' + f) : f;
11413             }
11414
11415             var hs = o.headers;
11416             if(this.defaultHeaders){
11417                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11418                 if(!o.headers){
11419                     o.headers = hs;
11420                 }
11421             }
11422
11423             var cb = {
11424                 success: this.handleResponse,
11425                 failure: this.handleFailure,
11426                 scope: this,
11427                 argument: {options: o},
11428                 timeout : o.timeout || this.timeout
11429             };
11430
11431             var method = o.method||this.method||(p ? "POST" : "GET");
11432
11433             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11434                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11435             }
11436
11437             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11438                 if(o.autoAbort){
11439                     this.abort();
11440                 }
11441             }else if(this.autoAbort !== false){
11442                 this.abort();
11443             }
11444
11445             if((method == 'GET' && p) || o.xmlData){
11446                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11447                 p = '';
11448             }
11449             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11450             return this.transId;
11451         }else{
11452             Roo.callback(o.callback, o.scope, [o, null, null]);
11453             return null;
11454         }
11455     },
11456
11457     /**
11458      * Determine whether this object has a request outstanding.
11459      * @param {Number} transactionId (Optional) defaults to the last transaction
11460      * @return {Boolean} True if there is an outstanding request.
11461      */
11462     isLoading : function(transId){
11463         if(transId){
11464             return Roo.lib.Ajax.isCallInProgress(transId);
11465         }else{
11466             return this.transId ? true : false;
11467         }
11468     },
11469
11470     /**
11471      * Aborts any outstanding request.
11472      * @param {Number} transactionId (Optional) defaults to the last transaction
11473      */
11474     abort : function(transId){
11475         if(transId || this.isLoading()){
11476             Roo.lib.Ajax.abort(transId || this.transId);
11477         }
11478     },
11479
11480     // private
11481     handleResponse : function(response){
11482         this.transId = false;
11483         var options = response.argument.options;
11484         response.argument = options ? options.argument : null;
11485         this.fireEvent("requestcomplete", this, response, options);
11486         Roo.callback(options.success, options.scope, [response, options]);
11487         Roo.callback(options.callback, options.scope, [options, true, response]);
11488     },
11489
11490     // private
11491     handleFailure : function(response, e){
11492         this.transId = false;
11493         var options = response.argument.options;
11494         response.argument = options ? options.argument : null;
11495         this.fireEvent("requestexception", this, response, options, e);
11496         Roo.callback(options.failure, options.scope, [response, options]);
11497         Roo.callback(options.callback, options.scope, [options, false, response]);
11498     },
11499
11500     // private
11501     doFormUpload : function(o, ps, url){
11502         var id = Roo.id();
11503         var frame = document.createElement('iframe');
11504         frame.id = id;
11505         frame.name = id;
11506         frame.className = 'x-hidden';
11507         if(Roo.isIE){
11508             frame.src = Roo.SSL_SECURE_URL;
11509         }
11510         document.body.appendChild(frame);
11511
11512         if(Roo.isIE){
11513            document.frames[id].name = id;
11514         }
11515
11516         var form = Roo.getDom(o.form);
11517         form.target = id;
11518         form.method = 'POST';
11519         form.enctype = form.encoding = 'multipart/form-data';
11520         if(url){
11521             form.action = url;
11522         }
11523
11524         var hiddens, hd;
11525         if(ps){ // add dynamic params
11526             hiddens = [];
11527             ps = Roo.urlDecode(ps, false);
11528             for(var k in ps){
11529                 if(ps.hasOwnProperty(k)){
11530                     hd = document.createElement('input');
11531                     hd.type = 'hidden';
11532                     hd.name = k;
11533                     hd.value = ps[k];
11534                     form.appendChild(hd);
11535                     hiddens.push(hd);
11536                 }
11537             }
11538         }
11539
11540         function cb(){
11541             var r = {  // bogus response object
11542                 responseText : '',
11543                 responseXML : null
11544             };
11545
11546             r.argument = o ? o.argument : null;
11547
11548             try { //
11549                 var doc;
11550                 if(Roo.isIE){
11551                     doc = frame.contentWindow.document;
11552                 }else {
11553                     doc = (frame.contentDocument || window.frames[id].document);
11554                 }
11555                 if(doc && doc.body){
11556                     r.responseText = doc.body.innerHTML;
11557                 }
11558                 if(doc && doc.XMLDocument){
11559                     r.responseXML = doc.XMLDocument;
11560                 }else {
11561                     r.responseXML = doc;
11562                 }
11563             }
11564             catch(e) {
11565                 // ignore
11566             }
11567
11568             Roo.EventManager.removeListener(frame, 'load', cb, this);
11569
11570             this.fireEvent("requestcomplete", this, r, o);
11571             Roo.callback(o.success, o.scope, [r, o]);
11572             Roo.callback(o.callback, o.scope, [o, true, r]);
11573
11574             setTimeout(function(){document.body.removeChild(frame);}, 100);
11575         }
11576
11577         Roo.EventManager.on(frame, 'load', cb, this);
11578         form.submit();
11579
11580         if(hiddens){ // remove dynamic params
11581             for(var i = 0, len = hiddens.length; i < len; i++){
11582                 form.removeChild(hiddens[i]);
11583             }
11584         }
11585     }
11586 });
11587 /*
11588  * Based on:
11589  * Ext JS Library 1.1.1
11590  * Copyright(c) 2006-2007, Ext JS, LLC.
11591  *
11592  * Originally Released Under LGPL - original licence link has changed is not relivant.
11593  *
11594  * Fork - LGPL
11595  * <script type="text/javascript">
11596  */
11597  
11598 /**
11599  * Global Ajax request class.
11600  * 
11601  * @class Roo.Ajax
11602  * @extends Roo.data.Connection
11603  * @static
11604  * 
11605  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11606  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11607  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11608  * @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)
11609  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11610  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11611  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11612  */
11613 Roo.Ajax = new Roo.data.Connection({
11614     // fix up the docs
11615     /**
11616      * @scope Roo.Ajax
11617      * @type {Boolear} 
11618      */
11619     autoAbort : false,
11620
11621     /**
11622      * Serialize the passed form into a url encoded string
11623      * @scope Roo.Ajax
11624      * @param {String/HTMLElement} form
11625      * @return {String}
11626      */
11627     serializeForm : function(form){
11628         return Roo.lib.Ajax.serializeForm(form);
11629     }
11630 });/*
11631  * Based on:
11632  * Ext JS Library 1.1.1
11633  * Copyright(c) 2006-2007, Ext JS, LLC.
11634  *
11635  * Originally Released Under LGPL - original licence link has changed is not relivant.
11636  *
11637  * Fork - LGPL
11638  * <script type="text/javascript">
11639  */
11640
11641  
11642 /**
11643  * @class Roo.UpdateManager
11644  * @extends Roo.util.Observable
11645  * Provides AJAX-style update for Element object.<br><br>
11646  * Usage:<br>
11647  * <pre><code>
11648  * // Get it from a Roo.Element object
11649  * var el = Roo.get("foo");
11650  * var mgr = el.getUpdateManager();
11651  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11652  * ...
11653  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11654  * <br>
11655  * // or directly (returns the same UpdateManager instance)
11656  * var mgr = new Roo.UpdateManager("myElementId");
11657  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11658  * mgr.on("update", myFcnNeedsToKnow);
11659  * <br>
11660    // short handed call directly from the element object
11661    Roo.get("foo").load({
11662         url: "bar.php",
11663         scripts:true,
11664         params: "for=bar",
11665         text: "Loading Foo..."
11666    });
11667  * </code></pre>
11668  * @constructor
11669  * Create new UpdateManager directly.
11670  * @param {String/HTMLElement/Roo.Element} el The element to update
11671  * @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).
11672  */
11673 Roo.UpdateManager = function(el, forceNew){
11674     el = Roo.get(el);
11675     if(!forceNew && el.updateManager){
11676         return el.updateManager;
11677     }
11678     /**
11679      * The Element object
11680      * @type Roo.Element
11681      */
11682     this.el = el;
11683     /**
11684      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11685      * @type String
11686      */
11687     this.defaultUrl = null;
11688
11689     this.addEvents({
11690         /**
11691          * @event beforeupdate
11692          * Fired before an update is made, return false from your handler and the update is cancelled.
11693          * @param {Roo.Element} el
11694          * @param {String/Object/Function} url
11695          * @param {String/Object} params
11696          */
11697         "beforeupdate": true,
11698         /**
11699          * @event update
11700          * Fired after successful update is made.
11701          * @param {Roo.Element} el
11702          * @param {Object} oResponseObject The response Object
11703          */
11704         "update": true,
11705         /**
11706          * @event failure
11707          * Fired on update failure.
11708          * @param {Roo.Element} el
11709          * @param {Object} oResponseObject The response Object
11710          */
11711         "failure": true
11712     });
11713     var d = Roo.UpdateManager.defaults;
11714     /**
11715      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11716      * @type String
11717      */
11718     this.sslBlankUrl = d.sslBlankUrl;
11719     /**
11720      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11721      * @type Boolean
11722      */
11723     this.disableCaching = d.disableCaching;
11724     /**
11725      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11726      * @type String
11727      */
11728     this.indicatorText = d.indicatorText;
11729     /**
11730      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11731      * @type String
11732      */
11733     this.showLoadIndicator = d.showLoadIndicator;
11734     /**
11735      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11736      * @type Number
11737      */
11738     this.timeout = d.timeout;
11739
11740     /**
11741      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11742      * @type Boolean
11743      */
11744     this.loadScripts = d.loadScripts;
11745
11746     /**
11747      * Transaction object of current executing transaction
11748      */
11749     this.transaction = null;
11750
11751     /**
11752      * @private
11753      */
11754     this.autoRefreshProcId = null;
11755     /**
11756      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11757      * @type Function
11758      */
11759     this.refreshDelegate = this.refresh.createDelegate(this);
11760     /**
11761      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11762      * @type Function
11763      */
11764     this.updateDelegate = this.update.createDelegate(this);
11765     /**
11766      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11767      * @type Function
11768      */
11769     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11770     /**
11771      * @private
11772      */
11773     this.successDelegate = this.processSuccess.createDelegate(this);
11774     /**
11775      * @private
11776      */
11777     this.failureDelegate = this.processFailure.createDelegate(this);
11778
11779     if(!this.renderer){
11780      /**
11781       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11782       */
11783     this.renderer = new Roo.UpdateManager.BasicRenderer();
11784     }
11785     
11786     Roo.UpdateManager.superclass.constructor.call(this);
11787 };
11788
11789 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11790     /**
11791      * Get the Element this UpdateManager is bound to
11792      * @return {Roo.Element} The element
11793      */
11794     getEl : function(){
11795         return this.el;
11796     },
11797     /**
11798      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11799      * @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:
11800 <pre><code>
11801 um.update({<br/>
11802     url: "your-url.php",<br/>
11803     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11804     callback: yourFunction,<br/>
11805     scope: yourObject, //(optional scope)  <br/>
11806     discardUrl: false, <br/>
11807     nocache: false,<br/>
11808     text: "Loading...",<br/>
11809     timeout: 30,<br/>
11810     scripts: false<br/>
11811 });
11812 </code></pre>
11813      * The only required property is url. The optional properties nocache, text and scripts
11814      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11815      * @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}
11816      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11817      * @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.
11818      */
11819     update : function(url, params, callback, discardUrl){
11820         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11821             var method = this.method,
11822                 cfg;
11823             if(typeof url == "object"){ // must be config object
11824                 cfg = url;
11825                 url = cfg.url;
11826                 params = params || cfg.params;
11827                 callback = callback || cfg.callback;
11828                 discardUrl = discardUrl || cfg.discardUrl;
11829                 if(callback && cfg.scope){
11830                     callback = callback.createDelegate(cfg.scope);
11831                 }
11832                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11833                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11834                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11835                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11836                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11837             }
11838             this.showLoading();
11839             if(!discardUrl){
11840                 this.defaultUrl = url;
11841             }
11842             if(typeof url == "function"){
11843                 url = url.call(this);
11844             }
11845
11846             method = method || (params ? "POST" : "GET");
11847             if(method == "GET"){
11848                 url = this.prepareUrl(url);
11849             }
11850
11851             var o = Roo.apply(cfg ||{}, {
11852                 url : url,
11853                 params: params,
11854                 success: this.successDelegate,
11855                 failure: this.failureDelegate,
11856                 callback: undefined,
11857                 timeout: (this.timeout*1000),
11858                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11859             });
11860             Roo.log("updated manager called with timeout of " + o.timeout);
11861             this.transaction = Roo.Ajax.request(o);
11862         }
11863     },
11864
11865     /**
11866      * 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.
11867      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11868      * @param {String/HTMLElement} form The form Id or form element
11869      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11870      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11871      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11872      */
11873     formUpdate : function(form, url, reset, callback){
11874         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11875             if(typeof url == "function"){
11876                 url = url.call(this);
11877             }
11878             form = Roo.getDom(form);
11879             this.transaction = Roo.Ajax.request({
11880                 form: form,
11881                 url:url,
11882                 success: this.successDelegate,
11883                 failure: this.failureDelegate,
11884                 timeout: (this.timeout*1000),
11885                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11886             });
11887             this.showLoading.defer(1, this);
11888         }
11889     },
11890
11891     /**
11892      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11893      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11894      */
11895     refresh : function(callback){
11896         if(this.defaultUrl == null){
11897             return;
11898         }
11899         this.update(this.defaultUrl, null, callback, true);
11900     },
11901
11902     /**
11903      * Set this element to auto refresh.
11904      * @param {Number} interval How often to update (in seconds).
11905      * @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)
11906      * @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}
11907      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11908      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11909      */
11910     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11911         if(refreshNow){
11912             this.update(url || this.defaultUrl, params, callback, true);
11913         }
11914         if(this.autoRefreshProcId){
11915             clearInterval(this.autoRefreshProcId);
11916         }
11917         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11918     },
11919
11920     /**
11921      * Stop auto refresh on this element.
11922      */
11923      stopAutoRefresh : function(){
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926             delete this.autoRefreshProcId;
11927         }
11928     },
11929
11930     isAutoRefreshing : function(){
11931        return this.autoRefreshProcId ? true : false;
11932     },
11933     /**
11934      * Called to update the element to "Loading" state. Override to perform custom action.
11935      */
11936     showLoading : function(){
11937         if(this.showLoadIndicator){
11938             this.el.update(this.indicatorText);
11939         }
11940     },
11941
11942     /**
11943      * Adds unique parameter to query string if disableCaching = true
11944      * @private
11945      */
11946     prepareUrl : function(url){
11947         if(this.disableCaching){
11948             var append = "_dc=" + (new Date().getTime());
11949             if(url.indexOf("?") !== -1){
11950                 url += "&" + append;
11951             }else{
11952                 url += "?" + append;
11953             }
11954         }
11955         return url;
11956     },
11957
11958     /**
11959      * @private
11960      */
11961     processSuccess : function(response){
11962         this.transaction = null;
11963         if(response.argument.form && response.argument.reset){
11964             try{ // put in try/catch since some older FF releases had problems with this
11965                 response.argument.form.reset();
11966             }catch(e){}
11967         }
11968         if(this.loadScripts){
11969             this.renderer.render(this.el, response, this,
11970                 this.updateComplete.createDelegate(this, [response]));
11971         }else{
11972             this.renderer.render(this.el, response, this);
11973             this.updateComplete(response);
11974         }
11975     },
11976
11977     updateComplete : function(response){
11978         this.fireEvent("update", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, true, response);
11981         }
11982     },
11983
11984     /**
11985      * @private
11986      */
11987     processFailure : function(response){
11988         this.transaction = null;
11989         this.fireEvent("failure", this.el, response);
11990         if(typeof response.argument.callback == "function"){
11991             response.argument.callback(this.el, false, response);
11992         }
11993     },
11994
11995     /**
11996      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11997      * @param {Object} renderer The object implementing the render() method
11998      */
11999     setRenderer : function(renderer){
12000         this.renderer = renderer;
12001     },
12002
12003     getRenderer : function(){
12004        return this.renderer;
12005     },
12006
12007     /**
12008      * Set the defaultUrl used for updates
12009      * @param {String/Function} defaultUrl The url or a function to call to get the url
12010      */
12011     setDefaultUrl : function(defaultUrl){
12012         this.defaultUrl = defaultUrl;
12013     },
12014
12015     /**
12016      * Aborts the executing transaction
12017      */
12018     abort : function(){
12019         if(this.transaction){
12020             Roo.Ajax.abort(this.transaction);
12021         }
12022     },
12023
12024     /**
12025      * Returns true if an update is in progress
12026      * @return {Boolean}
12027      */
12028     isUpdating : function(){
12029         if(this.transaction){
12030             return Roo.Ajax.isLoading(this.transaction);
12031         }
12032         return false;
12033     }
12034 });
12035
12036 /**
12037  * @class Roo.UpdateManager.defaults
12038  * @static (not really - but it helps the doc tool)
12039  * The defaults collection enables customizing the default properties of UpdateManager
12040  */
12041    Roo.UpdateManager.defaults = {
12042        /**
12043          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12044          * @type Number
12045          */
12046          timeout : 30,
12047
12048          /**
12049          * True to process scripts by default (Defaults to false).
12050          * @type Boolean
12051          */
12052         loadScripts : false,
12053
12054         /**
12055         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12056         * @type String
12057         */
12058         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12059         /**
12060          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12061          * @type Boolean
12062          */
12063         disableCaching : false,
12064         /**
12065          * Whether to show indicatorText when loading (Defaults to true).
12066          * @type Boolean
12067          */
12068         showLoadIndicator : true,
12069         /**
12070          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12071          * @type String
12072          */
12073         indicatorText : '<div class="loading-indicator">Loading...</div>'
12074    };
12075
12076 /**
12077  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12078  *Usage:
12079  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12080  * @param {String/HTMLElement/Roo.Element} el The element to update
12081  * @param {String} url The url
12082  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12083  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12084  * @static
12085  * @deprecated
12086  * @member Roo.UpdateManager
12087  */
12088 Roo.UpdateManager.updateElement = function(el, url, params, options){
12089     var um = Roo.get(el, true).getUpdateManager();
12090     Roo.apply(um, options);
12091     um.update(url, params, options ? options.callback : null);
12092 };
12093 // alias for backwards compat
12094 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12095 /**
12096  * @class Roo.UpdateManager.BasicRenderer
12097  * Default Content renderer. Updates the elements innerHTML with the responseText.
12098  */
12099 Roo.UpdateManager.BasicRenderer = function(){};
12100
12101 Roo.UpdateManager.BasicRenderer.prototype = {
12102     /**
12103      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12104      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12105      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12106      * @param {Roo.Element} el The element being rendered
12107      * @param {Object} response The YUI Connect response object
12108      * @param {UpdateManager} updateManager The calling update manager
12109      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12110      */
12111      render : function(el, response, updateManager, callback){
12112         el.update(response.responseText, updateManager.loadScripts, callback);
12113     }
12114 };
12115 /*
12116  * Based on:
12117  * Roo JS
12118  * (c)) Alan Knowles
12119  * Licence : LGPL
12120  */
12121
12122
12123 /**
12124  * @class Roo.DomTemplate
12125  * @extends Roo.Template
12126  * An effort at a dom based template engine..
12127  *
12128  * Similar to XTemplate, except it uses dom parsing to create the template..
12129  *
12130  * Supported features:
12131  *
12132  *  Tags:
12133
12134 <pre><code>
12135       {a_variable} - output encoded.
12136       {a_variable.format:("Y-m-d")} - call a method on the variable
12137       {a_variable:raw} - unencoded output
12138       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12139       {a_variable:this.method_on_template(...)} - call a method on the template object.
12140  
12141 </code></pre>
12142  *  The tpl tag:
12143 <pre><code>
12144         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12145         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12146         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12147         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12148   
12149 </code></pre>
12150  *      
12151  */
12152 Roo.DomTemplate = function()
12153 {
12154      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12155      if (this.html) {
12156         this.compile();
12157      }
12158 };
12159
12160
12161 Roo.extend(Roo.DomTemplate, Roo.Template, {
12162     /**
12163      * id counter for sub templates.
12164      */
12165     id : 0,
12166     /**
12167      * flag to indicate if dom parser is inside a pre,
12168      * it will strip whitespace if not.
12169      */
12170     inPre : false,
12171     
12172     /**
12173      * The various sub templates
12174      */
12175     tpls : false,
12176     
12177     
12178     
12179     /**
12180      *
12181      * basic tag replacing syntax
12182      * WORD:WORD()
12183      *
12184      * // you can fake an object call by doing this
12185      *  x.t:(test,tesT) 
12186      * 
12187      */
12188     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12189     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12190     
12191     iterChild : function (node, method) {
12192         
12193         var oldPre = this.inPre;
12194         if (node.tagName == 'PRE') {
12195             this.inPre = true;
12196         }
12197         for( var i = 0; i < node.childNodes.length; i++) {
12198             method.call(this, node.childNodes[i]);
12199         }
12200         this.inPre = oldPre;
12201     },
12202     
12203     
12204     
12205     /**
12206      * compile the template
12207      *
12208      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12209      *
12210      */
12211     compile: function()
12212     {
12213         var s = this.html;
12214         
12215         // covert the html into DOM...
12216         var doc = false;
12217         var div =false;
12218         try {
12219             doc = document.implementation.createHTMLDocument("");
12220             doc.documentElement.innerHTML =   this.html  ;
12221             div = doc.documentElement;
12222         } catch (e) {
12223             // old IE... - nasty -- it causes all sorts of issues.. with
12224             // images getting pulled from server..
12225             div = document.createElement('div');
12226             div.innerHTML = this.html;
12227         }
12228         //doc.documentElement.innerHTML = htmlBody
12229          
12230         
12231         
12232         this.tpls = [];
12233         var _t = this;
12234         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12235         
12236         var tpls = this.tpls;
12237         
12238         // create a top level template from the snippet..
12239         
12240         //Roo.log(div.innerHTML);
12241         
12242         var tpl = {
12243             uid : 'master',
12244             id : this.id++,
12245             attr : false,
12246             value : false,
12247             body : div.innerHTML,
12248             
12249             forCall : false,
12250             execCall : false,
12251             dom : div,
12252             isTop : true
12253             
12254         };
12255         tpls.unshift(tpl);
12256         
12257         
12258         // compile them...
12259         this.tpls = [];
12260         Roo.each(tpls, function(tp){
12261             this.compileTpl(tp);
12262             this.tpls[tp.id] = tp;
12263         }, this);
12264         
12265         this.master = tpls[0];
12266         return this;
12267         
12268         
12269     },
12270     
12271     compileNode : function(node, istop) {
12272         // test for
12273         //Roo.log(node);
12274         
12275         
12276         // skip anything not a tag..
12277         if (node.nodeType != 1) {
12278             if (node.nodeType == 3 && !this.inPre) {
12279                 // reduce white space..
12280                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12281                 
12282             }
12283             return;
12284         }
12285         
12286         var tpl = {
12287             uid : false,
12288             id : false,
12289             attr : false,
12290             value : false,
12291             body : '',
12292             
12293             forCall : false,
12294             execCall : false,
12295             dom : false,
12296             isTop : istop
12297             
12298             
12299         };
12300         
12301         
12302         switch(true) {
12303             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12304             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12305             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12306             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12307             // no default..
12308         }
12309         
12310         
12311         if (!tpl.attr) {
12312             // just itterate children..
12313             this.iterChild(node,this.compileNode);
12314             return;
12315         }
12316         tpl.uid = this.id++;
12317         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12318         node.removeAttribute('roo-'+ tpl.attr);
12319         if (tpl.attr != 'name') {
12320             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12321             node.parentNode.replaceChild(placeholder,  node);
12322         } else {
12323             
12324             var placeholder =  document.createElement('span');
12325             placeholder.className = 'roo-tpl-' + tpl.value;
12326             node.parentNode.replaceChild(placeholder,  node);
12327         }
12328         
12329         // parent now sees '{domtplXXXX}
12330         this.iterChild(node,this.compileNode);
12331         
12332         // we should now have node body...
12333         var div = document.createElement('div');
12334         div.appendChild(node);
12335         tpl.dom = node;
12336         // this has the unfortunate side effect of converting tagged attributes
12337         // eg. href="{...}" into %7C...%7D
12338         // this has been fixed by searching for those combo's although it's a bit hacky..
12339         
12340         
12341         tpl.body = div.innerHTML;
12342         
12343         
12344          
12345         tpl.id = tpl.uid;
12346         switch(tpl.attr) {
12347             case 'for' :
12348                 switch (tpl.value) {
12349                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12350                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12351                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12352                 }
12353                 break;
12354             
12355             case 'exec':
12356                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12357                 break;
12358             
12359             case 'if':     
12360                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12361                 break;
12362             
12363             case 'name':
12364                 tpl.id  = tpl.value; // replace non characters???
12365                 break;
12366             
12367         }
12368         
12369         
12370         this.tpls.push(tpl);
12371         
12372         
12373         
12374     },
12375     
12376     
12377     
12378     
12379     /**
12380      * Compile a segment of the template into a 'sub-template'
12381      *
12382      * 
12383      * 
12384      *
12385      */
12386     compileTpl : function(tpl)
12387     {
12388         var fm = Roo.util.Format;
12389         var useF = this.disableFormats !== true;
12390         
12391         var sep = Roo.isGecko ? "+\n" : ",\n";
12392         
12393         var undef = function(str) {
12394             Roo.debug && Roo.log("Property not found :"  + str);
12395             return '';
12396         };
12397           
12398         //Roo.log(tpl.body);
12399         
12400         
12401         
12402         var fn = function(m, lbrace, name, format, args)
12403         {
12404             //Roo.log("ARGS");
12405             //Roo.log(arguments);
12406             args = args ? args.replace(/\\'/g,"'") : args;
12407             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12408             if (typeof(format) == 'undefined') {
12409                 format =  'htmlEncode'; 
12410             }
12411             if (format == 'raw' ) {
12412                 format = false;
12413             }
12414             
12415             if(name.substr(0, 6) == 'domtpl'){
12416                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12417             }
12418             
12419             // build an array of options to determine if value is undefined..
12420             
12421             // basically get 'xxxx.yyyy' then do
12422             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12423             //    (function () { Roo.log("Property not found"); return ''; })() :
12424             //    ......
12425             
12426             var udef_ar = [];
12427             var lookfor = '';
12428             Roo.each(name.split('.'), function(st) {
12429                 lookfor += (lookfor.length ? '.': '') + st;
12430                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12431             });
12432             
12433             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12434             
12435             
12436             if(format && useF){
12437                 
12438                 args = args ? ',' + args : "";
12439                  
12440                 if(format.substr(0, 5) != "this."){
12441                     format = "fm." + format + '(';
12442                 }else{
12443                     format = 'this.call("'+ format.substr(5) + '", ';
12444                     args = ", values";
12445                 }
12446                 
12447                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12448             }
12449              
12450             if (args && args.length) {
12451                 // called with xxyx.yuu:(test,test)
12452                 // change to ()
12453                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12454             }
12455             // raw.. - :raw modifier..
12456             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12457             
12458         };
12459         var body;
12460         // branched to use + in gecko and [].join() in others
12461         if(Roo.isGecko){
12462             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12463                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12464                     "';};};";
12465         }else{
12466             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12467             body.push(tpl.body.replace(/(\r\n|\n)/g,
12468                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12469             body.push("'].join('');};};");
12470             body = body.join('');
12471         }
12472         
12473         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12474        
12475         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12476         eval(body);
12477         
12478         return this;
12479     },
12480      
12481     /**
12482      * same as applyTemplate, except it's done to one of the subTemplates
12483      * when using named templates, you can do:
12484      *
12485      * var str = pl.applySubTemplate('your-name', values);
12486      *
12487      * 
12488      * @param {Number} id of the template
12489      * @param {Object} values to apply to template
12490      * @param {Object} parent (normaly the instance of this object)
12491      */
12492     applySubTemplate : function(id, values, parent)
12493     {
12494         
12495         
12496         var t = this.tpls[id];
12497         
12498         
12499         try { 
12500             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12501                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507           
12508             return '';
12509         }
12510         try { 
12511             
12512             if(t.execCall && t.execCall.call(this, values, parent)){
12513                 return '';
12514             }
12515         } catch(e) {
12516             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12517             Roo.log(values);
12518             return '';
12519         }
12520         
12521         try {
12522             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12523             parent = t.target ? values : parent;
12524             if(t.forCall && vs instanceof Array){
12525                 var buf = [];
12526                 for(var i = 0, len = vs.length; i < len; i++){
12527                     try {
12528                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12529                     } catch (e) {
12530                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12531                         Roo.log(e.body);
12532                         //Roo.log(t.compiled);
12533                         Roo.log(vs[i]);
12534                     }   
12535                 }
12536                 return buf.join('');
12537             }
12538         } catch (e) {
12539             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12540             Roo.log(values);
12541             return '';
12542         }
12543         try {
12544             return t.compiled.call(this, vs, parent);
12545         } catch (e) {
12546             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12547             Roo.log(e.body);
12548             //Roo.log(t.compiled);
12549             Roo.log(values);
12550             return '';
12551         }
12552     },
12553
12554    
12555
12556     applyTemplate : function(values){
12557         return this.master.compiled.call(this, values, {});
12558         //var s = this.subs;
12559     },
12560
12561     apply : function(){
12562         return this.applyTemplate.apply(this, arguments);
12563     }
12564
12565  });
12566
12567 Roo.DomTemplate.from = function(el){
12568     el = Roo.getDom(el);
12569     return new Roo.Domtemplate(el.value || el.innerHTML);
12570 };/*
12571  * Based on:
12572  * Ext JS Library 1.1.1
12573  * Copyright(c) 2006-2007, Ext JS, LLC.
12574  *
12575  * Originally Released Under LGPL - original licence link has changed is not relivant.
12576  *
12577  * Fork - LGPL
12578  * <script type="text/javascript">
12579  */
12580
12581 /**
12582  * @class Roo.util.DelayedTask
12583  * Provides a convenient method of performing setTimeout where a new
12584  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12585  * You can use this class to buffer
12586  * the keypress events for a certain number of milliseconds, and perform only if they stop
12587  * for that amount of time.
12588  * @constructor The parameters to this constructor serve as defaults and are not required.
12589  * @param {Function} fn (optional) The default function to timeout
12590  * @param {Object} scope (optional) The default scope of that timeout
12591  * @param {Array} args (optional) The default Array of arguments
12592  */
12593 Roo.util.DelayedTask = function(fn, scope, args){
12594     var id = null, d, t;
12595
12596     var call = function(){
12597         var now = new Date().getTime();
12598         if(now - t >= d){
12599             clearInterval(id);
12600             id = null;
12601             fn.apply(scope, args || []);
12602         }
12603     };
12604     /**
12605      * Cancels any pending timeout and queues a new one
12606      * @param {Number} delay The milliseconds to delay
12607      * @param {Function} newFn (optional) Overrides function passed to constructor
12608      * @param {Object} newScope (optional) Overrides scope passed to constructor
12609      * @param {Array} newArgs (optional) Overrides args passed to constructor
12610      */
12611     this.delay = function(delay, newFn, newScope, newArgs){
12612         if(id && delay != d){
12613             this.cancel();
12614         }
12615         d = delay;
12616         t = new Date().getTime();
12617         fn = newFn || fn;
12618         scope = newScope || scope;
12619         args = newArgs || args;
12620         if(!id){
12621             id = setInterval(call, d);
12622         }
12623     };
12624
12625     /**
12626      * Cancel the last queued timeout
12627      */
12628     this.cancel = function(){
12629         if(id){
12630             clearInterval(id);
12631             id = null;
12632         }
12633     };
12634 };/*
12635  * Based on:
12636  * Ext JS Library 1.1.1
12637  * Copyright(c) 2006-2007, Ext JS, LLC.
12638  *
12639  * Originally Released Under LGPL - original licence link has changed is not relivant.
12640  *
12641  * Fork - LGPL
12642  * <script type="text/javascript">
12643  */
12644  
12645  
12646 Roo.util.TaskRunner = function(interval){
12647     interval = interval || 10;
12648     var tasks = [], removeQueue = [];
12649     var id = 0;
12650     var running = false;
12651
12652     var stopThread = function(){
12653         running = false;
12654         clearInterval(id);
12655         id = 0;
12656     };
12657
12658     var startThread = function(){
12659         if(!running){
12660             running = true;
12661             id = setInterval(runTasks, interval);
12662         }
12663     };
12664
12665     var removeTask = function(task){
12666         removeQueue.push(task);
12667         if(task.onStop){
12668             task.onStop();
12669         }
12670     };
12671
12672     var runTasks = function(){
12673         if(removeQueue.length > 0){
12674             for(var i = 0, len = removeQueue.length; i < len; i++){
12675                 tasks.remove(removeQueue[i]);
12676             }
12677             removeQueue = [];
12678             if(tasks.length < 1){
12679                 stopThread();
12680                 return;
12681             }
12682         }
12683         var now = new Date().getTime();
12684         for(var i = 0, len = tasks.length; i < len; ++i){
12685             var t = tasks[i];
12686             var itime = now - t.taskRunTime;
12687             if(t.interval <= itime){
12688                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12689                 t.taskRunTime = now;
12690                 if(rt === false || t.taskRunCount === t.repeat){
12691                     removeTask(t);
12692                     return;
12693                 }
12694             }
12695             if(t.duration && t.duration <= (now - t.taskStartTime)){
12696                 removeTask(t);
12697             }
12698         }
12699     };
12700
12701     /**
12702      * Queues a new task.
12703      * @param {Object} task
12704      */
12705     this.start = function(task){
12706         tasks.push(task);
12707         task.taskStartTime = new Date().getTime();
12708         task.taskRunTime = 0;
12709         task.taskRunCount = 0;
12710         startThread();
12711         return task;
12712     };
12713
12714     this.stop = function(task){
12715         removeTask(task);
12716         return task;
12717     };
12718
12719     this.stopAll = function(){
12720         stopThread();
12721         for(var i = 0, len = tasks.length; i < len; i++){
12722             if(tasks[i].onStop){
12723                 tasks[i].onStop();
12724             }
12725         }
12726         tasks = [];
12727         removeQueue = [];
12728     };
12729 };
12730
12731 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12732  * Based on:
12733  * Ext JS Library 1.1.1
12734  * Copyright(c) 2006-2007, Ext JS, LLC.
12735  *
12736  * Originally Released Under LGPL - original licence link has changed is not relivant.
12737  *
12738  * Fork - LGPL
12739  * <script type="text/javascript">
12740  */
12741
12742  
12743 /**
12744  * @class Roo.util.MixedCollection
12745  * @extends Roo.util.Observable
12746  * A Collection class that maintains both numeric indexes and keys and exposes events.
12747  * @constructor
12748  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12749  * collection (defaults to false)
12750  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12751  * and return the key value for that item.  This is used when available to look up the key on items that
12752  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12753  * equivalent to providing an implementation for the {@link #getKey} method.
12754  */
12755 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12756     this.items = [];
12757     this.map = {};
12758     this.keys = [];
12759     this.length = 0;
12760     this.addEvents({
12761         /**
12762          * @event clear
12763          * Fires when the collection is cleared.
12764          */
12765         "clear" : true,
12766         /**
12767          * @event add
12768          * Fires when an item is added to the collection.
12769          * @param {Number} index The index at which the item was added.
12770          * @param {Object} o The item added.
12771          * @param {String} key The key associated with the added item.
12772          */
12773         "add" : true,
12774         /**
12775          * @event replace
12776          * Fires when an item is replaced in the collection.
12777          * @param {String} key he key associated with the new added.
12778          * @param {Object} old The item being replaced.
12779          * @param {Object} new The new item.
12780          */
12781         "replace" : true,
12782         /**
12783          * @event remove
12784          * Fires when an item is removed from the collection.
12785          * @param {Object} o The item being removed.
12786          * @param {String} key (optional) The key associated with the removed item.
12787          */
12788         "remove" : true,
12789         "sort" : true
12790     });
12791     this.allowFunctions = allowFunctions === true;
12792     if(keyFn){
12793         this.getKey = keyFn;
12794     }
12795     Roo.util.MixedCollection.superclass.constructor.call(this);
12796 };
12797
12798 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12799     allowFunctions : false,
12800     
12801 /**
12802  * Adds an item to the collection.
12803  * @param {String} key The key to associate with the item
12804  * @param {Object} o The item to add.
12805  * @return {Object} The item added.
12806  */
12807     add : function(key, o){
12808         if(arguments.length == 1){
12809             o = arguments[0];
12810             key = this.getKey(o);
12811         }
12812         if(typeof key == "undefined" || key === null){
12813             this.length++;
12814             this.items.push(o);
12815             this.keys.push(null);
12816         }else{
12817             var old = this.map[key];
12818             if(old){
12819                 return this.replace(key, o);
12820             }
12821             this.length++;
12822             this.items.push(o);
12823             this.map[key] = o;
12824             this.keys.push(key);
12825         }
12826         this.fireEvent("add", this.length-1, o, key);
12827         return o;
12828     },
12829        
12830 /**
12831   * MixedCollection has a generic way to fetch keys if you implement getKey.
12832 <pre><code>
12833 // normal way
12834 var mc = new Roo.util.MixedCollection();
12835 mc.add(someEl.dom.id, someEl);
12836 mc.add(otherEl.dom.id, otherEl);
12837 //and so on
12838
12839 // using getKey
12840 var mc = new Roo.util.MixedCollection();
12841 mc.getKey = function(el){
12842    return el.dom.id;
12843 };
12844 mc.add(someEl);
12845 mc.add(otherEl);
12846
12847 // or via the constructor
12848 var mc = new Roo.util.MixedCollection(false, function(el){
12849    return el.dom.id;
12850 });
12851 mc.add(someEl);
12852 mc.add(otherEl);
12853 </code></pre>
12854  * @param o {Object} The item for which to find the key.
12855  * @return {Object} The key for the passed item.
12856  */
12857     getKey : function(o){
12858          return o.id; 
12859     },
12860    
12861 /**
12862  * Replaces an item in the collection.
12863  * @param {String} key The key associated with the item to replace, or the item to replace.
12864  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12865  * @return {Object}  The new item.
12866  */
12867     replace : function(key, o){
12868         if(arguments.length == 1){
12869             o = arguments[0];
12870             key = this.getKey(o);
12871         }
12872         var old = this.item(key);
12873         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12874              return this.add(key, o);
12875         }
12876         var index = this.indexOfKey(key);
12877         this.items[index] = o;
12878         this.map[key] = o;
12879         this.fireEvent("replace", key, old, o);
12880         return o;
12881     },
12882    
12883 /**
12884  * Adds all elements of an Array or an Object to the collection.
12885  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12886  * an Array of values, each of which are added to the collection.
12887  */
12888     addAll : function(objs){
12889         if(arguments.length > 1 || objs instanceof Array){
12890             var args = arguments.length > 1 ? arguments : objs;
12891             for(var i = 0, len = args.length; i < len; i++){
12892                 this.add(args[i]);
12893             }
12894         }else{
12895             for(var key in objs){
12896                 if(this.allowFunctions || typeof objs[key] != "function"){
12897                     this.add(key, objs[key]);
12898                 }
12899             }
12900         }
12901     },
12902    
12903 /**
12904  * Executes the specified function once for every item in the collection, passing each
12905  * item as the first and only parameter. returning false from the function will stop the iteration.
12906  * @param {Function} fn The function to execute for each item.
12907  * @param {Object} scope (optional) The scope in which to execute the function.
12908  */
12909     each : function(fn, scope){
12910         var items = [].concat(this.items); // each safe for removal
12911         for(var i = 0, len = items.length; i < len; i++){
12912             if(fn.call(scope || items[i], items[i], i, len) === false){
12913                 break;
12914             }
12915         }
12916     },
12917    
12918 /**
12919  * Executes the specified function once for every key in the collection, passing each
12920  * key, and its associated item as the first two parameters.
12921  * @param {Function} fn The function to execute for each item.
12922  * @param {Object} scope (optional) The scope in which to execute the function.
12923  */
12924     eachKey : function(fn, scope){
12925         for(var i = 0, len = this.keys.length; i < len; i++){
12926             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12927         }
12928     },
12929    
12930 /**
12931  * Returns the first item in the collection which elicits a true return value from the
12932  * passed selection function.
12933  * @param {Function} fn The selection function to execute for each item.
12934  * @param {Object} scope (optional) The scope in which to execute the function.
12935  * @return {Object} The first item in the collection which returned true from the selection function.
12936  */
12937     find : function(fn, scope){
12938         for(var i = 0, len = this.items.length; i < len; i++){
12939             if(fn.call(scope || window, this.items[i], this.keys[i])){
12940                 return this.items[i];
12941             }
12942         }
12943         return null;
12944     },
12945    
12946 /**
12947  * Inserts an item at the specified index in the collection.
12948  * @param {Number} index The index to insert the item at.
12949  * @param {String} key The key to associate with the new item, or the item itself.
12950  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12951  * @return {Object} The item inserted.
12952  */
12953     insert : function(index, key, o){
12954         if(arguments.length == 2){
12955             o = arguments[1];
12956             key = this.getKey(o);
12957         }
12958         if(index >= this.length){
12959             return this.add(key, o);
12960         }
12961         this.length++;
12962         this.items.splice(index, 0, o);
12963         if(typeof key != "undefined" && key != null){
12964             this.map[key] = o;
12965         }
12966         this.keys.splice(index, 0, key);
12967         this.fireEvent("add", index, o, key);
12968         return o;
12969     },
12970    
12971 /**
12972  * Removed an item from the collection.
12973  * @param {Object} o The item to remove.
12974  * @return {Object} The item removed.
12975  */
12976     remove : function(o){
12977         return this.removeAt(this.indexOf(o));
12978     },
12979    
12980 /**
12981  * Remove an item from a specified index in the collection.
12982  * @param {Number} index The index within the collection of the item to remove.
12983  */
12984     removeAt : function(index){
12985         if(index < this.length && index >= 0){
12986             this.length--;
12987             var o = this.items[index];
12988             this.items.splice(index, 1);
12989             var key = this.keys[index];
12990             if(typeof key != "undefined"){
12991                 delete this.map[key];
12992             }
12993             this.keys.splice(index, 1);
12994             this.fireEvent("remove", o, key);
12995         }
12996     },
12997    
12998 /**
12999  * Removed an item associated with the passed key fom the collection.
13000  * @param {String} key The key of the item to remove.
13001  */
13002     removeKey : function(key){
13003         return this.removeAt(this.indexOfKey(key));
13004     },
13005    
13006 /**
13007  * Returns the number of items in the collection.
13008  * @return {Number} the number of items in the collection.
13009  */
13010     getCount : function(){
13011         return this.length; 
13012     },
13013    
13014 /**
13015  * Returns index within the collection of the passed Object.
13016  * @param {Object} o The item to find the index of.
13017  * @return {Number} index of the item.
13018  */
13019     indexOf : function(o){
13020         if(!this.items.indexOf){
13021             for(var i = 0, len = this.items.length; i < len; i++){
13022                 if(this.items[i] == o) return i;
13023             }
13024             return -1;
13025         }else{
13026             return this.items.indexOf(o);
13027         }
13028     },
13029    
13030 /**
13031  * Returns index within the collection of the passed key.
13032  * @param {String} key The key to find the index of.
13033  * @return {Number} index of the key.
13034  */
13035     indexOfKey : function(key){
13036         if(!this.keys.indexOf){
13037             for(var i = 0, len = this.keys.length; i < len; i++){
13038                 if(this.keys[i] == key) return i;
13039             }
13040             return -1;
13041         }else{
13042             return this.keys.indexOf(key);
13043         }
13044     },
13045    
13046 /**
13047  * Returns the item associated with the passed key OR index. Key has priority over index.
13048  * @param {String/Number} key The key or index of the item.
13049  * @return {Object} The item associated with the passed key.
13050  */
13051     item : function(key){
13052         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13053         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13054     },
13055     
13056 /**
13057  * Returns the item at the specified index.
13058  * @param {Number} index The index of the item.
13059  * @return {Object}
13060  */
13061     itemAt : function(index){
13062         return this.items[index];
13063     },
13064     
13065 /**
13066  * Returns the item associated with the passed key.
13067  * @param {String/Number} key The key of the item.
13068  * @return {Object} The item associated with the passed key.
13069  */
13070     key : function(key){
13071         return this.map[key];
13072     },
13073    
13074 /**
13075  * Returns true if the collection contains the passed Object as an item.
13076  * @param {Object} o  The Object to look for in the collection.
13077  * @return {Boolean} True if the collection contains the Object as an item.
13078  */
13079     contains : function(o){
13080         return this.indexOf(o) != -1;
13081     },
13082    
13083 /**
13084  * Returns true if the collection contains the passed Object as a key.
13085  * @param {String} key The key to look for in the collection.
13086  * @return {Boolean} True if the collection contains the Object as a key.
13087  */
13088     containsKey : function(key){
13089         return typeof this.map[key] != "undefined";
13090     },
13091    
13092 /**
13093  * Removes all items from the collection.
13094  */
13095     clear : function(){
13096         this.length = 0;
13097         this.items = [];
13098         this.keys = [];
13099         this.map = {};
13100         this.fireEvent("clear");
13101     },
13102    
13103 /**
13104  * Returns the first item in the collection.
13105  * @return {Object} the first item in the collection..
13106  */
13107     first : function(){
13108         return this.items[0]; 
13109     },
13110    
13111 /**
13112  * Returns the last item in the collection.
13113  * @return {Object} the last item in the collection..
13114  */
13115     last : function(){
13116         return this.items[this.length-1];   
13117     },
13118     
13119     _sort : function(property, dir, fn){
13120         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13121         fn = fn || function(a, b){
13122             return a-b;
13123         };
13124         var c = [], k = this.keys, items = this.items;
13125         for(var i = 0, len = items.length; i < len; i++){
13126             c[c.length] = {key: k[i], value: items[i], index: i};
13127         }
13128         c.sort(function(a, b){
13129             var v = fn(a[property], b[property]) * dsc;
13130             if(v == 0){
13131                 v = (a.index < b.index ? -1 : 1);
13132             }
13133             return v;
13134         });
13135         for(var i = 0, len = c.length; i < len; i++){
13136             items[i] = c[i].value;
13137             k[i] = c[i].key;
13138         }
13139         this.fireEvent("sort", this);
13140     },
13141     
13142     /**
13143      * Sorts this collection with the passed comparison function
13144      * @param {String} direction (optional) "ASC" or "DESC"
13145      * @param {Function} fn (optional) comparison function
13146      */
13147     sort : function(dir, fn){
13148         this._sort("value", dir, fn);
13149     },
13150     
13151     /**
13152      * Sorts this collection by keys
13153      * @param {String} direction (optional) "ASC" or "DESC"
13154      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13155      */
13156     keySort : function(dir, fn){
13157         this._sort("key", dir, fn || function(a, b){
13158             return String(a).toUpperCase()-String(b).toUpperCase();
13159         });
13160     },
13161     
13162     /**
13163      * Returns a range of items in this collection
13164      * @param {Number} startIndex (optional) defaults to 0
13165      * @param {Number} endIndex (optional) default to the last item
13166      * @return {Array} An array of items
13167      */
13168     getRange : function(start, end){
13169         var items = this.items;
13170         if(items.length < 1){
13171             return [];
13172         }
13173         start = start || 0;
13174         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13175         var r = [];
13176         if(start <= end){
13177             for(var i = start; i <= end; i++) {
13178                     r[r.length] = items[i];
13179             }
13180         }else{
13181             for(var i = start; i >= end; i--) {
13182                     r[r.length] = items[i];
13183             }
13184         }
13185         return r;
13186     },
13187         
13188     /**
13189      * Filter the <i>objects</i> in this collection by a specific property. 
13190      * Returns a new collection that has been filtered.
13191      * @param {String} property A property on your objects
13192      * @param {String/RegExp} value Either string that the property values 
13193      * should start with or a RegExp to test against the property
13194      * @return {MixedCollection} The new filtered collection
13195      */
13196     filter : function(property, value){
13197         if(!value.exec){ // not a regex
13198             value = String(value);
13199             if(value.length == 0){
13200                 return this.clone();
13201             }
13202             value = new RegExp("^" + Roo.escapeRe(value), "i");
13203         }
13204         return this.filterBy(function(o){
13205             return o && value.test(o[property]);
13206         });
13207         },
13208     
13209     /**
13210      * Filter by a function. * Returns a new collection that has been filtered.
13211      * The passed function will be called with each 
13212      * object in the collection. If the function returns true, the value is included 
13213      * otherwise it is filtered.
13214      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13215      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13216      * @return {MixedCollection} The new filtered collection
13217      */
13218     filterBy : function(fn, scope){
13219         var r = new Roo.util.MixedCollection();
13220         r.getKey = this.getKey;
13221         var k = this.keys, it = this.items;
13222         for(var i = 0, len = it.length; i < len; i++){
13223             if(fn.call(scope||this, it[i], k[i])){
13224                                 r.add(k[i], it[i]);
13225                         }
13226         }
13227         return r;
13228     },
13229     
13230     /**
13231      * Creates a duplicate of this collection
13232      * @return {MixedCollection}
13233      */
13234     clone : function(){
13235         var r = new Roo.util.MixedCollection();
13236         var k = this.keys, it = this.items;
13237         for(var i = 0, len = it.length; i < len; i++){
13238             r.add(k[i], it[i]);
13239         }
13240         r.getKey = this.getKey;
13241         return r;
13242     }
13243 });
13244 /**
13245  * Returns the item associated with the passed key or index.
13246  * @method
13247  * @param {String/Number} key The key or index of the item.
13248  * @return {Object} The item associated with the passed key.
13249  */
13250 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13251  * Based on:
13252  * Ext JS Library 1.1.1
13253  * Copyright(c) 2006-2007, Ext JS, LLC.
13254  *
13255  * Originally Released Under LGPL - original licence link has changed is not relivant.
13256  *
13257  * Fork - LGPL
13258  * <script type="text/javascript">
13259  */
13260 /**
13261  * @class Roo.util.JSON
13262  * Modified version of Douglas Crockford"s json.js that doesn"t
13263  * mess with the Object prototype 
13264  * http://www.json.org/js.html
13265  * @singleton
13266  */
13267 Roo.util.JSON = new (function(){
13268     var useHasOwn = {}.hasOwnProperty ? true : false;
13269     
13270     // crashes Safari in some instances
13271     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13272     
13273     var pad = function(n) {
13274         return n < 10 ? "0" + n : n;
13275     };
13276     
13277     var m = {
13278         "\b": '\\b',
13279         "\t": '\\t',
13280         "\n": '\\n',
13281         "\f": '\\f',
13282         "\r": '\\r',
13283         '"' : '\\"',
13284         "\\": '\\\\'
13285     };
13286
13287     var encodeString = function(s){
13288         if (/["\\\x00-\x1f]/.test(s)) {
13289             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13290                 var c = m[b];
13291                 if(c){
13292                     return c;
13293                 }
13294                 c = b.charCodeAt();
13295                 return "\\u00" +
13296                     Math.floor(c / 16).toString(16) +
13297                     (c % 16).toString(16);
13298             }) + '"';
13299         }
13300         return '"' + s + '"';
13301     };
13302     
13303     var encodeArray = function(o){
13304         var a = ["["], b, i, l = o.length, v;
13305             for (i = 0; i < l; i += 1) {
13306                 v = o[i];
13307                 switch (typeof v) {
13308                     case "undefined":
13309                     case "function":
13310                     case "unknown":
13311                         break;
13312                     default:
13313                         if (b) {
13314                             a.push(',');
13315                         }
13316                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13317                         b = true;
13318                 }
13319             }
13320             a.push("]");
13321             return a.join("");
13322     };
13323     
13324     var encodeDate = function(o){
13325         return '"' + o.getFullYear() + "-" +
13326                 pad(o.getMonth() + 1) + "-" +
13327                 pad(o.getDate()) + "T" +
13328                 pad(o.getHours()) + ":" +
13329                 pad(o.getMinutes()) + ":" +
13330                 pad(o.getSeconds()) + '"';
13331     };
13332     
13333     /**
13334      * Encodes an Object, Array or other value
13335      * @param {Mixed} o The variable to encode
13336      * @return {String} The JSON string
13337      */
13338     this.encode = function(o)
13339     {
13340         // should this be extended to fully wrap stringify..
13341         
13342         if(typeof o == "undefined" || o === null){
13343             return "null";
13344         }else if(o instanceof Array){
13345             return encodeArray(o);
13346         }else if(o instanceof Date){
13347             return encodeDate(o);
13348         }else if(typeof o == "string"){
13349             return encodeString(o);
13350         }else if(typeof o == "number"){
13351             return isFinite(o) ? String(o) : "null";
13352         }else if(typeof o == "boolean"){
13353             return String(o);
13354         }else {
13355             var a = ["{"], b, i, v;
13356             for (i in o) {
13357                 if(!useHasOwn || o.hasOwnProperty(i)) {
13358                     v = o[i];
13359                     switch (typeof v) {
13360                     case "undefined":
13361                     case "function":
13362                     case "unknown":
13363                         break;
13364                     default:
13365                         if(b){
13366                             a.push(',');
13367                         }
13368                         a.push(this.encode(i), ":",
13369                                 v === null ? "null" : this.encode(v));
13370                         b = true;
13371                     }
13372                 }
13373             }
13374             a.push("}");
13375             return a.join("");
13376         }
13377     };
13378     
13379     /**
13380      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13381      * @param {String} json The JSON string
13382      * @return {Object} The resulting object
13383      */
13384     this.decode = function(json){
13385         
13386         return  /** eval:var:json */ eval("(" + json + ')');
13387     };
13388 })();
13389 /** 
13390  * Shorthand for {@link Roo.util.JSON#encode}
13391  * @member Roo encode 
13392  * @method */
13393 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13394 /** 
13395  * Shorthand for {@link Roo.util.JSON#decode}
13396  * @member Roo decode 
13397  * @method */
13398 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13399 /*
13400  * Based on:
13401  * Ext JS Library 1.1.1
13402  * Copyright(c) 2006-2007, Ext JS, LLC.
13403  *
13404  * Originally Released Under LGPL - original licence link has changed is not relivant.
13405  *
13406  * Fork - LGPL
13407  * <script type="text/javascript">
13408  */
13409  
13410 /**
13411  * @class Roo.util.Format
13412  * Reusable data formatting functions
13413  * @singleton
13414  */
13415 Roo.util.Format = function(){
13416     var trimRe = /^\s+|\s+$/g;
13417     return {
13418         /**
13419          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13420          * @param {String} value The string to truncate
13421          * @param {Number} length The maximum length to allow before truncating
13422          * @return {String} The converted text
13423          */
13424         ellipsis : function(value, len){
13425             if(value && value.length > len){
13426                 return value.substr(0, len-3)+"...";
13427             }
13428             return value;
13429         },
13430
13431         /**
13432          * Checks a reference and converts it to empty string if it is undefined
13433          * @param {Mixed} value Reference to check
13434          * @return {Mixed} Empty string if converted, otherwise the original value
13435          */
13436         undef : function(value){
13437             return typeof value != "undefined" ? value : "";
13438         },
13439
13440         /**
13441          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13442          * @param {String} value The string to encode
13443          * @return {String} The encoded text
13444          */
13445         htmlEncode : function(value){
13446             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13447         },
13448
13449         /**
13450          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13451          * @param {String} value The string to decode
13452          * @return {String} The decoded text
13453          */
13454         htmlDecode : function(value){
13455             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13456         },
13457
13458         /**
13459          * Trims any whitespace from either side of a string
13460          * @param {String} value The text to trim
13461          * @return {String} The trimmed text
13462          */
13463         trim : function(value){
13464             return String(value).replace(trimRe, "");
13465         },
13466
13467         /**
13468          * Returns a substring from within an original string
13469          * @param {String} value The original text
13470          * @param {Number} start The start index of the substring
13471          * @param {Number} length The length of the substring
13472          * @return {String} The substring
13473          */
13474         substr : function(value, start, length){
13475             return String(value).substr(start, length);
13476         },
13477
13478         /**
13479          * Converts a string to all lower case letters
13480          * @param {String} value The text to convert
13481          * @return {String} The converted text
13482          */
13483         lowercase : function(value){
13484             return String(value).toLowerCase();
13485         },
13486
13487         /**
13488          * Converts a string to all upper case letters
13489          * @param {String} value The text to convert
13490          * @return {String} The converted text
13491          */
13492         uppercase : function(value){
13493             return String(value).toUpperCase();
13494         },
13495
13496         /**
13497          * Converts the first character only of a string to upper case
13498          * @param {String} value The text to convert
13499          * @return {String} The converted text
13500          */
13501         capitalize : function(value){
13502             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13503         },
13504
13505         // private
13506         call : function(value, fn){
13507             if(arguments.length > 2){
13508                 var args = Array.prototype.slice.call(arguments, 2);
13509                 args.unshift(value);
13510                  
13511                 return /** eval:var:value */  eval(fn).apply(window, args);
13512             }else{
13513                 /** eval:var:value */
13514                 return /** eval:var:value */ eval(fn).call(window, value);
13515             }
13516         },
13517
13518        
13519         /**
13520          * safer version of Math.toFixed..??/
13521          * @param {Number/String} value The numeric value to format
13522          * @param {Number/String} value Decimal places 
13523          * @return {String} The formatted currency string
13524          */
13525         toFixed : function(v, n)
13526         {
13527             // why not use to fixed - precision is buggered???
13528             if (!n) {
13529                 return Math.round(v-0);
13530             }
13531             var fact = Math.pow(10,n+1);
13532             v = (Math.round((v-0)*fact))/fact;
13533             var z = (''+fact).substring(2);
13534             if (v == Math.floor(v)) {
13535                 return Math.floor(v) + '.' + z;
13536             }
13537             
13538             // now just padd decimals..
13539             var ps = String(v).split('.');
13540             var fd = (ps[1] + z);
13541             var r = fd.substring(0,n); 
13542             var rm = fd.substring(n); 
13543             if (rm < 5) {
13544                 return ps[0] + '.' + r;
13545             }
13546             r*=1; // turn it into a number;
13547             r++;
13548             if (String(r).length != n) {
13549                 ps[0]*=1;
13550                 ps[0]++;
13551                 r = String(r).substring(1); // chop the end off.
13552             }
13553             
13554             return ps[0] + '.' + r;
13555              
13556         },
13557         
13558         /**
13559          * Format a number as US currency
13560          * @param {Number/String} value The numeric value to format
13561          * @return {String} The formatted currency string
13562          */
13563         usMoney : function(v){
13564             return '$' + Roo.util.Format.number(v);
13565         },
13566         
13567         /**
13568          * Format a number
13569          * eventually this should probably emulate php's number_format
13570          * @param {Number/String} value The numeric value to format
13571          * @param {Number} decimals number of decimal places
13572          * @return {String} The formatted currency string
13573          */
13574         number : function(v,decimals)
13575         {
13576             // multiply and round.
13577             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13578             var mul = Math.pow(10, decimals);
13579             var zero = String(mul).substring(1);
13580             v = (Math.round((v-0)*mul))/mul;
13581             
13582             // if it's '0' number.. then
13583             
13584             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13585             v = String(v);
13586             var ps = v.split('.');
13587             var whole = ps[0];
13588             
13589             
13590             var r = /(\d+)(\d{3})/;
13591             // add comma's
13592             while (r.test(whole)) {
13593                 whole = whole.replace(r, '$1' + ',' + '$2');
13594             }
13595             
13596             
13597             var sub = ps[1] ?
13598                     // has decimals..
13599                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13600                     // does not have decimals
13601                     (decimals ? ('.' + zero) : '');
13602             
13603             
13604             return whole + sub ;
13605         },
13606         
13607         /**
13608          * Parse a value into a formatted date using the specified format pattern.
13609          * @param {Mixed} value The value to format
13610          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13611          * @return {String} The formatted date string
13612          */
13613         date : function(v, format){
13614             if(!v){
13615                 return "";
13616             }
13617             if(!(v instanceof Date)){
13618                 v = new Date(Date.parse(v));
13619             }
13620             return v.dateFormat(format || Roo.util.Format.defaults.date);
13621         },
13622
13623         /**
13624          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13625          * @param {String} format Any valid date format string
13626          * @return {Function} The date formatting function
13627          */
13628         dateRenderer : function(format){
13629             return function(v){
13630                 return Roo.util.Format.date(v, format);  
13631             };
13632         },
13633
13634         // private
13635         stripTagsRE : /<\/?[^>]+>/gi,
13636         
13637         /**
13638          * Strips all HTML tags
13639          * @param {Mixed} value The text from which to strip tags
13640          * @return {String} The stripped text
13641          */
13642         stripTags : function(v){
13643             return !v ? v : String(v).replace(this.stripTagsRE, "");
13644         }
13645     };
13646 }();
13647 Roo.util.Format.defaults = {
13648     date : 'd/M/Y'
13649 };/*
13650  * Based on:
13651  * Ext JS Library 1.1.1
13652  * Copyright(c) 2006-2007, Ext JS, LLC.
13653  *
13654  * Originally Released Under LGPL - original licence link has changed is not relivant.
13655  *
13656  * Fork - LGPL
13657  * <script type="text/javascript">
13658  */
13659
13660
13661  
13662
13663 /**
13664  * @class Roo.MasterTemplate
13665  * @extends Roo.Template
13666  * Provides a template that can have child templates. The syntax is:
13667 <pre><code>
13668 var t = new Roo.MasterTemplate(
13669         '&lt;select name="{name}"&gt;',
13670                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13671         '&lt;/select&gt;'
13672 );
13673 t.add('options', {value: 'foo', text: 'bar'});
13674 // or you can add multiple child elements in one shot
13675 t.addAll('options', [
13676     {value: 'foo', text: 'bar'},
13677     {value: 'foo2', text: 'bar2'},
13678     {value: 'foo3', text: 'bar3'}
13679 ]);
13680 // then append, applying the master template values
13681 t.append('my-form', {name: 'my-select'});
13682 </code></pre>
13683 * A name attribute for the child template is not required if you have only one child
13684 * template or you want to refer to them by index.
13685  */
13686 Roo.MasterTemplate = function(){
13687     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13688     this.originalHtml = this.html;
13689     var st = {};
13690     var m, re = this.subTemplateRe;
13691     re.lastIndex = 0;
13692     var subIndex = 0;
13693     while(m = re.exec(this.html)){
13694         var name = m[1], content = m[2];
13695         st[subIndex] = {
13696             name: name,
13697             index: subIndex,
13698             buffer: [],
13699             tpl : new Roo.Template(content)
13700         };
13701         if(name){
13702             st[name] = st[subIndex];
13703         }
13704         st[subIndex].tpl.compile();
13705         st[subIndex].tpl.call = this.call.createDelegate(this);
13706         subIndex++;
13707     }
13708     this.subCount = subIndex;
13709     this.subs = st;
13710 };
13711 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13712     /**
13713     * The regular expression used to match sub templates
13714     * @type RegExp
13715     * @property
13716     */
13717     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13718
13719     /**
13720      * Applies the passed values to a child template.
13721      * @param {String/Number} name (optional) The name or index of the child template
13722      * @param {Array/Object} values The values to be applied to the template
13723      * @return {MasterTemplate} this
13724      */
13725      add : function(name, values){
13726         if(arguments.length == 1){
13727             values = arguments[0];
13728             name = 0;
13729         }
13730         var s = this.subs[name];
13731         s.buffer[s.buffer.length] = s.tpl.apply(values);
13732         return this;
13733     },
13734
13735     /**
13736      * Applies all the passed values to a child template.
13737      * @param {String/Number} name (optional) The name or index of the child template
13738      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13739      * @param {Boolean} reset (optional) True to reset the template first
13740      * @return {MasterTemplate} this
13741      */
13742     fill : function(name, values, reset){
13743         var a = arguments;
13744         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13745             values = a[0];
13746             name = 0;
13747             reset = a[1];
13748         }
13749         if(reset){
13750             this.reset();
13751         }
13752         for(var i = 0, len = values.length; i < len; i++){
13753             this.add(name, values[i]);
13754         }
13755         return this;
13756     },
13757
13758     /**
13759      * Resets the template for reuse
13760      * @return {MasterTemplate} this
13761      */
13762      reset : function(){
13763         var s = this.subs;
13764         for(var i = 0; i < this.subCount; i++){
13765             s[i].buffer = [];
13766         }
13767         return this;
13768     },
13769
13770     applyTemplate : function(values){
13771         var s = this.subs;
13772         var replaceIndex = -1;
13773         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13774             return s[++replaceIndex].buffer.join("");
13775         });
13776         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13777     },
13778
13779     apply : function(){
13780         return this.applyTemplate.apply(this, arguments);
13781     },
13782
13783     compile : function(){return this;}
13784 });
13785
13786 /**
13787  * Alias for fill().
13788  * @method
13789  */
13790 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13791  /**
13792  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13793  * var tpl = Roo.MasterTemplate.from('element-id');
13794  * @param {String/HTMLElement} el
13795  * @param {Object} config
13796  * @static
13797  */
13798 Roo.MasterTemplate.from = function(el, config){
13799     el = Roo.getDom(el);
13800     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13801 };/*
13802  * Based on:
13803  * Ext JS Library 1.1.1
13804  * Copyright(c) 2006-2007, Ext JS, LLC.
13805  *
13806  * Originally Released Under LGPL - original licence link has changed is not relivant.
13807  *
13808  * Fork - LGPL
13809  * <script type="text/javascript">
13810  */
13811
13812  
13813 /**
13814  * @class Roo.util.CSS
13815  * Utility class for manipulating CSS rules
13816  * @singleton
13817  */
13818 Roo.util.CSS = function(){
13819         var rules = null;
13820         var doc = document;
13821
13822     var camelRe = /(-[a-z])/gi;
13823     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13824
13825    return {
13826    /**
13827     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13828     * tag and appended to the HEAD of the document.
13829     * @param {String|Object} cssText The text containing the css rules
13830     * @param {String} id An id to add to the stylesheet for later removal
13831     * @return {StyleSheet}
13832     */
13833     createStyleSheet : function(cssText, id){
13834         var ss;
13835         var head = doc.getElementsByTagName("head")[0];
13836         var nrules = doc.createElement("style");
13837         nrules.setAttribute("type", "text/css");
13838         if(id){
13839             nrules.setAttribute("id", id);
13840         }
13841         if (typeof(cssText) != 'string') {
13842             // support object maps..
13843             // not sure if this a good idea.. 
13844             // perhaps it should be merged with the general css handling
13845             // and handle js style props.
13846             var cssTextNew = [];
13847             for(var n in cssText) {
13848                 var citems = [];
13849                 for(var k in cssText[n]) {
13850                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13851                 }
13852                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13853                 
13854             }
13855             cssText = cssTextNew.join("\n");
13856             
13857         }
13858        
13859        
13860        if(Roo.isIE){
13861            head.appendChild(nrules);
13862            ss = nrules.styleSheet;
13863            ss.cssText = cssText;
13864        }else{
13865            try{
13866                 nrules.appendChild(doc.createTextNode(cssText));
13867            }catch(e){
13868                nrules.cssText = cssText; 
13869            }
13870            head.appendChild(nrules);
13871            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13872        }
13873        this.cacheStyleSheet(ss);
13874        return ss;
13875    },
13876
13877    /**
13878     * Removes a style or link tag by id
13879     * @param {String} id The id of the tag
13880     */
13881    removeStyleSheet : function(id){
13882        var existing = doc.getElementById(id);
13883        if(existing){
13884            existing.parentNode.removeChild(existing);
13885        }
13886    },
13887
13888    /**
13889     * Dynamically swaps an existing stylesheet reference for a new one
13890     * @param {String} id The id of an existing link tag to remove
13891     * @param {String} url The href of the new stylesheet to include
13892     */
13893    swapStyleSheet : function(id, url){
13894        this.removeStyleSheet(id);
13895        var ss = doc.createElement("link");
13896        ss.setAttribute("rel", "stylesheet");
13897        ss.setAttribute("type", "text/css");
13898        ss.setAttribute("id", id);
13899        ss.setAttribute("href", url);
13900        doc.getElementsByTagName("head")[0].appendChild(ss);
13901    },
13902    
13903    /**
13904     * Refresh the rule cache if you have dynamically added stylesheets
13905     * @return {Object} An object (hash) of rules indexed by selector
13906     */
13907    refreshCache : function(){
13908        return this.getRules(true);
13909    },
13910
13911    // private
13912    cacheStyleSheet : function(stylesheet){
13913        if(!rules){
13914            rules = {};
13915        }
13916        try{// try catch for cross domain access issue
13917            var ssRules = stylesheet.cssRules || stylesheet.rules;
13918            for(var j = ssRules.length-1; j >= 0; --j){
13919                rules[ssRules[j].selectorText] = ssRules[j];
13920            }
13921        }catch(e){}
13922    },
13923    
13924    /**
13925     * Gets all css rules for the document
13926     * @param {Boolean} refreshCache true to refresh the internal cache
13927     * @return {Object} An object (hash) of rules indexed by selector
13928     */
13929    getRules : function(refreshCache){
13930                 if(rules == null || refreshCache){
13931                         rules = {};
13932                         var ds = doc.styleSheets;
13933                         for(var i =0, len = ds.length; i < len; i++){
13934                             try{
13935                         this.cacheStyleSheet(ds[i]);
13936                     }catch(e){} 
13937                 }
13938                 }
13939                 return rules;
13940         },
13941         
13942         /**
13943     * Gets an an individual CSS rule by selector(s)
13944     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13945     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13946     * @return {CSSRule} The CSS rule or null if one is not found
13947     */
13948    getRule : function(selector, refreshCache){
13949                 var rs = this.getRules(refreshCache);
13950                 if(!(selector instanceof Array)){
13951                     return rs[selector];
13952                 }
13953                 for(var i = 0; i < selector.length; i++){
13954                         if(rs[selector[i]]){
13955                                 return rs[selector[i]];
13956                         }
13957                 }
13958                 return null;
13959         },
13960         
13961         
13962         /**
13963     * Updates a rule property
13964     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13965     * @param {String} property The css property
13966     * @param {String} value The new value for the property
13967     * @return {Boolean} true If a rule was found and updated
13968     */
13969    updateRule : function(selector, property, value){
13970                 if(!(selector instanceof Array)){
13971                         var rule = this.getRule(selector);
13972                         if(rule){
13973                                 rule.style[property.replace(camelRe, camelFn)] = value;
13974                                 return true;
13975                         }
13976                 }else{
13977                         for(var i = 0; i < selector.length; i++){
13978                                 if(this.updateRule(selector[i], property, value)){
13979                                         return true;
13980                                 }
13981                         }
13982                 }
13983                 return false;
13984         }
13985    };   
13986 }();/*
13987  * Based on:
13988  * Ext JS Library 1.1.1
13989  * Copyright(c) 2006-2007, Ext JS, LLC.
13990  *
13991  * Originally Released Under LGPL - original licence link has changed is not relivant.
13992  *
13993  * Fork - LGPL
13994  * <script type="text/javascript">
13995  */
13996
13997  
13998
13999 /**
14000  * @class Roo.util.ClickRepeater
14001  * @extends Roo.util.Observable
14002  * 
14003  * A wrapper class which can be applied to any element. Fires a "click" event while the
14004  * mouse is pressed. The interval between firings may be specified in the config but
14005  * defaults to 10 milliseconds.
14006  * 
14007  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14008  * 
14009  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14010  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14011  * Similar to an autorepeat key delay.
14012  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14013  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14014  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14015  *           "interval" and "delay" are ignored. "immediate" is honored.
14016  * @cfg {Boolean} preventDefault True to prevent the default click event
14017  * @cfg {Boolean} stopDefault True to stop the default click event
14018  * 
14019  * @history
14020  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14021  *     2007-02-02 jvs Renamed to ClickRepeater
14022  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14023  *
14024  *  @constructor
14025  * @param {String/HTMLElement/Element} el The element to listen on
14026  * @param {Object} config
14027  **/
14028 Roo.util.ClickRepeater = function(el, config)
14029 {
14030     this.el = Roo.get(el);
14031     this.el.unselectable();
14032
14033     Roo.apply(this, config);
14034
14035     this.addEvents({
14036     /**
14037      * @event mousedown
14038      * Fires when the mouse button is depressed.
14039      * @param {Roo.util.ClickRepeater} this
14040      */
14041         "mousedown" : true,
14042     /**
14043      * @event click
14044      * Fires on a specified interval during the time the element is pressed.
14045      * @param {Roo.util.ClickRepeater} this
14046      */
14047         "click" : true,
14048     /**
14049      * @event mouseup
14050      * Fires when the mouse key is released.
14051      * @param {Roo.util.ClickRepeater} this
14052      */
14053         "mouseup" : true
14054     });
14055
14056     this.el.on("mousedown", this.handleMouseDown, this);
14057     if(this.preventDefault || this.stopDefault){
14058         this.el.on("click", function(e){
14059             if(this.preventDefault){
14060                 e.preventDefault();
14061             }
14062             if(this.stopDefault){
14063                 e.stopEvent();
14064             }
14065         }, this);
14066     }
14067
14068     // allow inline handler
14069     if(this.handler){
14070         this.on("click", this.handler,  this.scope || this);
14071     }
14072
14073     Roo.util.ClickRepeater.superclass.constructor.call(this);
14074 };
14075
14076 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14077     interval : 20,
14078     delay: 250,
14079     preventDefault : true,
14080     stopDefault : false,
14081     timer : 0,
14082
14083     // private
14084     handleMouseDown : function(){
14085         clearTimeout(this.timer);
14086         this.el.blur();
14087         if(this.pressClass){
14088             this.el.addClass(this.pressClass);
14089         }
14090         this.mousedownTime = new Date();
14091
14092         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14093         this.el.on("mouseout", this.handleMouseOut, this);
14094
14095         this.fireEvent("mousedown", this);
14096         this.fireEvent("click", this);
14097         
14098         this.timer = this.click.defer(this.delay || this.interval, this);
14099     },
14100
14101     // private
14102     click : function(){
14103         this.fireEvent("click", this);
14104         this.timer = this.click.defer(this.getInterval(), this);
14105     },
14106
14107     // private
14108     getInterval: function(){
14109         if(!this.accelerate){
14110             return this.interval;
14111         }
14112         var pressTime = this.mousedownTime.getElapsed();
14113         if(pressTime < 500){
14114             return 400;
14115         }else if(pressTime < 1700){
14116             return 320;
14117         }else if(pressTime < 2600){
14118             return 250;
14119         }else if(pressTime < 3500){
14120             return 180;
14121         }else if(pressTime < 4400){
14122             return 140;
14123         }else if(pressTime < 5300){
14124             return 80;
14125         }else if(pressTime < 6200){
14126             return 50;
14127         }else{
14128             return 10;
14129         }
14130     },
14131
14132     // private
14133     handleMouseOut : function(){
14134         clearTimeout(this.timer);
14135         if(this.pressClass){
14136             this.el.removeClass(this.pressClass);
14137         }
14138         this.el.on("mouseover", this.handleMouseReturn, this);
14139     },
14140
14141     // private
14142     handleMouseReturn : function(){
14143         this.el.un("mouseover", this.handleMouseReturn);
14144         if(this.pressClass){
14145             this.el.addClass(this.pressClass);
14146         }
14147         this.click();
14148     },
14149
14150     // private
14151     handleMouseUp : function(){
14152         clearTimeout(this.timer);
14153         this.el.un("mouseover", this.handleMouseReturn);
14154         this.el.un("mouseout", this.handleMouseOut);
14155         Roo.get(document).un("mouseup", this.handleMouseUp);
14156         this.el.removeClass(this.pressClass);
14157         this.fireEvent("mouseup", this);
14158     }
14159 });/*
14160  * Based on:
14161  * Ext JS Library 1.1.1
14162  * Copyright(c) 2006-2007, Ext JS, LLC.
14163  *
14164  * Originally Released Under LGPL - original licence link has changed is not relivant.
14165  *
14166  * Fork - LGPL
14167  * <script type="text/javascript">
14168  */
14169
14170  
14171 /**
14172  * @class Roo.KeyNav
14173  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14174  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14175  * way to implement custom navigation schemes for any UI component.</p>
14176  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14177  * pageUp, pageDown, del, home, end.  Usage:</p>
14178  <pre><code>
14179 var nav = new Roo.KeyNav("my-element", {
14180     "left" : function(e){
14181         this.moveLeft(e.ctrlKey);
14182     },
14183     "right" : function(e){
14184         this.moveRight(e.ctrlKey);
14185     },
14186     "enter" : function(e){
14187         this.save();
14188     },
14189     scope : this
14190 });
14191 </code></pre>
14192  * @constructor
14193  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14194  * @param {Object} config The config
14195  */
14196 Roo.KeyNav = function(el, config){
14197     this.el = Roo.get(el);
14198     Roo.apply(this, config);
14199     if(!this.disabled){
14200         this.disabled = true;
14201         this.enable();
14202     }
14203 };
14204
14205 Roo.KeyNav.prototype = {
14206     /**
14207      * @cfg {Boolean} disabled
14208      * True to disable this KeyNav instance (defaults to false)
14209      */
14210     disabled : false,
14211     /**
14212      * @cfg {String} defaultEventAction
14213      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14214      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14215      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14216      */
14217     defaultEventAction: "stopEvent",
14218     /**
14219      * @cfg {Boolean} forceKeyDown
14220      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14221      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14222      * handle keydown instead of keypress.
14223      */
14224     forceKeyDown : false,
14225
14226     // private
14227     prepareEvent : function(e){
14228         var k = e.getKey();
14229         var h = this.keyToHandler[k];
14230         //if(h && this[h]){
14231         //    e.stopPropagation();
14232         //}
14233         if(Roo.isSafari && h && k >= 37 && k <= 40){
14234             e.stopEvent();
14235         }
14236     },
14237
14238     // private
14239     relay : function(e){
14240         var k = e.getKey();
14241         var h = this.keyToHandler[k];
14242         if(h && this[h]){
14243             if(this.doRelay(e, this[h], h) !== true){
14244                 e[this.defaultEventAction]();
14245             }
14246         }
14247     },
14248
14249     // private
14250     doRelay : function(e, h, hname){
14251         return h.call(this.scope || this, e);
14252     },
14253
14254     // possible handlers
14255     enter : false,
14256     left : false,
14257     right : false,
14258     up : false,
14259     down : false,
14260     tab : false,
14261     esc : false,
14262     pageUp : false,
14263     pageDown : false,
14264     del : false,
14265     home : false,
14266     end : false,
14267
14268     // quick lookup hash
14269     keyToHandler : {
14270         37 : "left",
14271         39 : "right",
14272         38 : "up",
14273         40 : "down",
14274         33 : "pageUp",
14275         34 : "pageDown",
14276         46 : "del",
14277         36 : "home",
14278         35 : "end",
14279         13 : "enter",
14280         27 : "esc",
14281         9  : "tab"
14282     },
14283
14284         /**
14285          * Enable this KeyNav
14286          */
14287         enable: function(){
14288                 if(this.disabled){
14289             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14290             // the EventObject will normalize Safari automatically
14291             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14292                 this.el.on("keydown", this.relay,  this);
14293             }else{
14294                 this.el.on("keydown", this.prepareEvent,  this);
14295                 this.el.on("keypress", this.relay,  this);
14296             }
14297                     this.disabled = false;
14298                 }
14299         },
14300
14301         /**
14302          * Disable this KeyNav
14303          */
14304         disable: function(){
14305                 if(!this.disabled){
14306                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14307                 this.el.un("keydown", this.relay);
14308             }else{
14309                 this.el.un("keydown", this.prepareEvent);
14310                 this.el.un("keypress", this.relay);
14311             }
14312                     this.disabled = true;
14313                 }
14314         }
14315 };/*
14316  * Based on:
14317  * Ext JS Library 1.1.1
14318  * Copyright(c) 2006-2007, Ext JS, LLC.
14319  *
14320  * Originally Released Under LGPL - original licence link has changed is not relivant.
14321  *
14322  * Fork - LGPL
14323  * <script type="text/javascript">
14324  */
14325
14326  
14327 /**
14328  * @class Roo.KeyMap
14329  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14330  * The constructor accepts the same config object as defined by {@link #addBinding}.
14331  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14332  * combination it will call the function with this signature (if the match is a multi-key
14333  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14334  * A KeyMap can also handle a string representation of keys.<br />
14335  * Usage:
14336  <pre><code>
14337 // map one key by key code
14338 var map = new Roo.KeyMap("my-element", {
14339     key: 13, // or Roo.EventObject.ENTER
14340     fn: myHandler,
14341     scope: myObject
14342 });
14343
14344 // map multiple keys to one action by string
14345 var map = new Roo.KeyMap("my-element", {
14346     key: "a\r\n\t",
14347     fn: myHandler,
14348     scope: myObject
14349 });
14350
14351 // map multiple keys to multiple actions by strings and array of codes
14352 var map = new Roo.KeyMap("my-element", [
14353     {
14354         key: [10,13],
14355         fn: function(){ alert("Return was pressed"); }
14356     }, {
14357         key: "abc",
14358         fn: function(){ alert('a, b or c was pressed'); }
14359     }, {
14360         key: "\t",
14361         ctrl:true,
14362         shift:true,
14363         fn: function(){ alert('Control + shift + tab was pressed.'); }
14364     }
14365 ]);
14366 </code></pre>
14367  * <b>Note: A KeyMap starts enabled</b>
14368  * @constructor
14369  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14370  * @param {Object} config The config (see {@link #addBinding})
14371  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14372  */
14373 Roo.KeyMap = function(el, config, eventName){
14374     this.el  = Roo.get(el);
14375     this.eventName = eventName || "keydown";
14376     this.bindings = [];
14377     if(config){
14378         this.addBinding(config);
14379     }
14380     this.enable();
14381 };
14382
14383 Roo.KeyMap.prototype = {
14384     /**
14385      * True to stop the event from bubbling and prevent the default browser action if the
14386      * key was handled by the KeyMap (defaults to false)
14387      * @type Boolean
14388      */
14389     stopEvent : false,
14390
14391     /**
14392      * Add a new binding to this KeyMap. The following config object properties are supported:
14393      * <pre>
14394 Property    Type             Description
14395 ----------  ---------------  ----------------------------------------------------------------------
14396 key         String/Array     A single keycode or an array of keycodes to handle
14397 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14398 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14399 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14400 fn          Function         The function to call when KeyMap finds the expected key combination
14401 scope       Object           The scope of the callback function
14402 </pre>
14403      *
14404      * Usage:
14405      * <pre><code>
14406 // Create a KeyMap
14407 var map = new Roo.KeyMap(document, {
14408     key: Roo.EventObject.ENTER,
14409     fn: handleKey,
14410     scope: this
14411 });
14412
14413 //Add a new binding to the existing KeyMap later
14414 map.addBinding({
14415     key: 'abc',
14416     shift: true,
14417     fn: handleKey,
14418     scope: this
14419 });
14420 </code></pre>
14421      * @param {Object/Array} config A single KeyMap config or an array of configs
14422      */
14423         addBinding : function(config){
14424         if(config instanceof Array){
14425             for(var i = 0, len = config.length; i < len; i++){
14426                 this.addBinding(config[i]);
14427             }
14428             return;
14429         }
14430         var keyCode = config.key,
14431             shift = config.shift, 
14432             ctrl = config.ctrl, 
14433             alt = config.alt,
14434             fn = config.fn,
14435             scope = config.scope;
14436         if(typeof keyCode == "string"){
14437             var ks = [];
14438             var keyString = keyCode.toUpperCase();
14439             for(var j = 0, len = keyString.length; j < len; j++){
14440                 ks.push(keyString.charCodeAt(j));
14441             }
14442             keyCode = ks;
14443         }
14444         var keyArray = keyCode instanceof Array;
14445         var handler = function(e){
14446             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14447                 var k = e.getKey();
14448                 if(keyArray){
14449                     for(var i = 0, len = keyCode.length; i < len; i++){
14450                         if(keyCode[i] == k){
14451                           if(this.stopEvent){
14452                               e.stopEvent();
14453                           }
14454                           fn.call(scope || window, k, e);
14455                           return;
14456                         }
14457                     }
14458                 }else{
14459                     if(k == keyCode){
14460                         if(this.stopEvent){
14461                            e.stopEvent();
14462                         }
14463                         fn.call(scope || window, k, e);
14464                     }
14465                 }
14466             }
14467         };
14468         this.bindings.push(handler);  
14469         },
14470
14471     /**
14472      * Shorthand for adding a single key listener
14473      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14474      * following options:
14475      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14476      * @param {Function} fn The function to call
14477      * @param {Object} scope (optional) The scope of the function
14478      */
14479     on : function(key, fn, scope){
14480         var keyCode, shift, ctrl, alt;
14481         if(typeof key == "object" && !(key instanceof Array)){
14482             keyCode = key.key;
14483             shift = key.shift;
14484             ctrl = key.ctrl;
14485             alt = key.alt;
14486         }else{
14487             keyCode = key;
14488         }
14489         this.addBinding({
14490             key: keyCode,
14491             shift: shift,
14492             ctrl: ctrl,
14493             alt: alt,
14494             fn: fn,
14495             scope: scope
14496         })
14497     },
14498
14499     // private
14500     handleKeyDown : function(e){
14501             if(this.enabled){ //just in case
14502             var b = this.bindings;
14503             for(var i = 0, len = b.length; i < len; i++){
14504                 b[i].call(this, e);
14505             }
14506             }
14507         },
14508         
14509         /**
14510          * Returns true if this KeyMap is enabled
14511          * @return {Boolean} 
14512          */
14513         isEnabled : function(){
14514             return this.enabled;  
14515         },
14516         
14517         /**
14518          * Enables this KeyMap
14519          */
14520         enable: function(){
14521                 if(!this.enabled){
14522                     this.el.on(this.eventName, this.handleKeyDown, this);
14523                     this.enabled = true;
14524                 }
14525         },
14526
14527         /**
14528          * Disable this KeyMap
14529          */
14530         disable: function(){
14531                 if(this.enabled){
14532                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14533                     this.enabled = false;
14534                 }
14535         }
14536 };/*
14537  * Based on:
14538  * Ext JS Library 1.1.1
14539  * Copyright(c) 2006-2007, Ext JS, LLC.
14540  *
14541  * Originally Released Under LGPL - original licence link has changed is not relivant.
14542  *
14543  * Fork - LGPL
14544  * <script type="text/javascript">
14545  */
14546
14547  
14548 /**
14549  * @class Roo.util.TextMetrics
14550  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14551  * wide, in pixels, a given block of text will be.
14552  * @singleton
14553  */
14554 Roo.util.TextMetrics = function(){
14555     var shared;
14556     return {
14557         /**
14558          * Measures the size of the specified text
14559          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14560          * that can affect the size of the rendered text
14561          * @param {String} text The text to measure
14562          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14563          * in order to accurately measure the text height
14564          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14565          */
14566         measure : function(el, text, fixedWidth){
14567             if(!shared){
14568                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14569             }
14570             shared.bind(el);
14571             shared.setFixedWidth(fixedWidth || 'auto');
14572             return shared.getSize(text);
14573         },
14574
14575         /**
14576          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14577          * the overhead of multiple calls to initialize the style properties on each measurement.
14578          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14579          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14580          * in order to accurately measure the text height
14581          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14582          */
14583         createInstance : function(el, fixedWidth){
14584             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14585         }
14586     };
14587 }();
14588
14589  
14590
14591 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14592     var ml = new Roo.Element(document.createElement('div'));
14593     document.body.appendChild(ml.dom);
14594     ml.position('absolute');
14595     ml.setLeftTop(-1000, -1000);
14596     ml.hide();
14597
14598     if(fixedWidth){
14599         ml.setWidth(fixedWidth);
14600     }
14601      
14602     var instance = {
14603         /**
14604          * Returns the size of the specified text based on the internal element's style and width properties
14605          * @memberOf Roo.util.TextMetrics.Instance#
14606          * @param {String} text The text to measure
14607          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14608          */
14609         getSize : function(text){
14610             ml.update(text);
14611             var s = ml.getSize();
14612             ml.update('');
14613             return s;
14614         },
14615
14616         /**
14617          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14618          * that can affect the size of the rendered text
14619          * @memberOf Roo.util.TextMetrics.Instance#
14620          * @param {String/HTMLElement} el The element, dom node or id
14621          */
14622         bind : function(el){
14623             ml.setStyle(
14624                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14625             );
14626         },
14627
14628         /**
14629          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14630          * to set a fixed width in order to accurately measure the text height.
14631          * @memberOf Roo.util.TextMetrics.Instance#
14632          * @param {Number} width The width to set on the element
14633          */
14634         setFixedWidth : function(width){
14635             ml.setWidth(width);
14636         },
14637
14638         /**
14639          * Returns the measured width of the specified text
14640          * @memberOf Roo.util.TextMetrics.Instance#
14641          * @param {String} text The text to measure
14642          * @return {Number} width The width in pixels
14643          */
14644         getWidth : function(text){
14645             ml.dom.style.width = 'auto';
14646             return this.getSize(text).width;
14647         },
14648
14649         /**
14650          * Returns the measured height of the specified text.  For multiline text, be sure to call
14651          * {@link #setFixedWidth} if necessary.
14652          * @memberOf Roo.util.TextMetrics.Instance#
14653          * @param {String} text The text to measure
14654          * @return {Number} height The height in pixels
14655          */
14656         getHeight : function(text){
14657             return this.getSize(text).height;
14658         }
14659     };
14660
14661     instance.bind(bindTo);
14662
14663     return instance;
14664 };
14665
14666 // backwards compat
14667 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14668  * Based on:
14669  * Ext JS Library 1.1.1
14670  * Copyright(c) 2006-2007, Ext JS, LLC.
14671  *
14672  * Originally Released Under LGPL - original licence link has changed is not relivant.
14673  *
14674  * Fork - LGPL
14675  * <script type="text/javascript">
14676  */
14677
14678 /**
14679  * @class Roo.state.Provider
14680  * Abstract base class for state provider implementations. This class provides methods
14681  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14682  * Provider interface.
14683  */
14684 Roo.state.Provider = function(){
14685     /**
14686      * @event statechange
14687      * Fires when a state change occurs.
14688      * @param {Provider} this This state provider
14689      * @param {String} key The state key which was changed
14690      * @param {String} value The encoded value for the state
14691      */
14692     this.addEvents({
14693         "statechange": true
14694     });
14695     this.state = {};
14696     Roo.state.Provider.superclass.constructor.call(this);
14697 };
14698 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14699     /**
14700      * Returns the current value for a key
14701      * @param {String} name The key name
14702      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14703      * @return {Mixed} The state data
14704      */
14705     get : function(name, defaultValue){
14706         return typeof this.state[name] == "undefined" ?
14707             defaultValue : this.state[name];
14708     },
14709     
14710     /**
14711      * Clears a value from the state
14712      * @param {String} name The key name
14713      */
14714     clear : function(name){
14715         delete this.state[name];
14716         this.fireEvent("statechange", this, name, null);
14717     },
14718     
14719     /**
14720      * Sets the value for a key
14721      * @param {String} name The key name
14722      * @param {Mixed} value The value to set
14723      */
14724     set : function(name, value){
14725         this.state[name] = value;
14726         this.fireEvent("statechange", this, name, value);
14727     },
14728     
14729     /**
14730      * Decodes a string previously encoded with {@link #encodeValue}.
14731      * @param {String} value The value to decode
14732      * @return {Mixed} The decoded value
14733      */
14734     decodeValue : function(cookie){
14735         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14736         var matches = re.exec(unescape(cookie));
14737         if(!matches || !matches[1]) return; // non state cookie
14738         var type = matches[1];
14739         var v = matches[2];
14740         switch(type){
14741             case "n":
14742                 return parseFloat(v);
14743             case "d":
14744                 return new Date(Date.parse(v));
14745             case "b":
14746                 return (v == "1");
14747             case "a":
14748                 var all = [];
14749                 var values = v.split("^");
14750                 for(var i = 0, len = values.length; i < len; i++){
14751                     all.push(this.decodeValue(values[i]));
14752                 }
14753                 return all;
14754            case "o":
14755                 var all = {};
14756                 var values = v.split("^");
14757                 for(var i = 0, len = values.length; i < len; i++){
14758                     var kv = values[i].split("=");
14759                     all[kv[0]] = this.decodeValue(kv[1]);
14760                 }
14761                 return all;
14762            default:
14763                 return v;
14764         }
14765     },
14766     
14767     /**
14768      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14769      * @param {Mixed} value The value to encode
14770      * @return {String} The encoded value
14771      */
14772     encodeValue : function(v){
14773         var enc;
14774         if(typeof v == "number"){
14775             enc = "n:" + v;
14776         }else if(typeof v == "boolean"){
14777             enc = "b:" + (v ? "1" : "0");
14778         }else if(v instanceof Date){
14779             enc = "d:" + v.toGMTString();
14780         }else if(v instanceof Array){
14781             var flat = "";
14782             for(var i = 0, len = v.length; i < len; i++){
14783                 flat += this.encodeValue(v[i]);
14784                 if(i != len-1) flat += "^";
14785             }
14786             enc = "a:" + flat;
14787         }else if(typeof v == "object"){
14788             var flat = "";
14789             for(var key in v){
14790                 if(typeof v[key] != "function"){
14791                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14792                 }
14793             }
14794             enc = "o:" + flat.substring(0, flat.length-1);
14795         }else{
14796             enc = "s:" + v;
14797         }
14798         return escape(enc);        
14799     }
14800 });
14801
14802 /*
14803  * Based on:
14804  * Ext JS Library 1.1.1
14805  * Copyright(c) 2006-2007, Ext JS, LLC.
14806  *
14807  * Originally Released Under LGPL - original licence link has changed is not relivant.
14808  *
14809  * Fork - LGPL
14810  * <script type="text/javascript">
14811  */
14812 /**
14813  * @class Roo.state.Manager
14814  * This is the global state manager. By default all components that are "state aware" check this class
14815  * for state information if you don't pass them a custom state provider. In order for this class
14816  * to be useful, it must be initialized with a provider when your application initializes.
14817  <pre><code>
14818 // in your initialization function
14819 init : function(){
14820    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14821    ...
14822    // supposed you have a {@link Roo.BorderLayout}
14823    var layout = new Roo.BorderLayout(...);
14824    layout.restoreState();
14825    // or a {Roo.BasicDialog}
14826    var dialog = new Roo.BasicDialog(...);
14827    dialog.restoreState();
14828  </code></pre>
14829  * @singleton
14830  */
14831 Roo.state.Manager = function(){
14832     var provider = new Roo.state.Provider();
14833     
14834     return {
14835         /**
14836          * Configures the default state provider for your application
14837          * @param {Provider} stateProvider The state provider to set
14838          */
14839         setProvider : function(stateProvider){
14840             provider = stateProvider;
14841         },
14842         
14843         /**
14844          * Returns the current value for a key
14845          * @param {String} name The key name
14846          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14847          * @return {Mixed} The state data
14848          */
14849         get : function(key, defaultValue){
14850             return provider.get(key, defaultValue);
14851         },
14852         
14853         /**
14854          * Sets the value for a key
14855          * @param {String} name The key name
14856          * @param {Mixed} value The state data
14857          */
14858          set : function(key, value){
14859             provider.set(key, value);
14860         },
14861         
14862         /**
14863          * Clears a value from the state
14864          * @param {String} name The key name
14865          */
14866         clear : function(key){
14867             provider.clear(key);
14868         },
14869         
14870         /**
14871          * Gets the currently configured state provider
14872          * @return {Provider} The state provider
14873          */
14874         getProvider : function(){
14875             return provider;
14876         }
14877     };
14878 }();
14879 /*
14880  * Based on:
14881  * Ext JS Library 1.1.1
14882  * Copyright(c) 2006-2007, Ext JS, LLC.
14883  *
14884  * Originally Released Under LGPL - original licence link has changed is not relivant.
14885  *
14886  * Fork - LGPL
14887  * <script type="text/javascript">
14888  */
14889 /**
14890  * @class Roo.state.CookieProvider
14891  * @extends Roo.state.Provider
14892  * The default Provider implementation which saves state via cookies.
14893  * <br />Usage:
14894  <pre><code>
14895    var cp = new Roo.state.CookieProvider({
14896        path: "/cgi-bin/",
14897        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14898        domain: "roojs.com"
14899    })
14900    Roo.state.Manager.setProvider(cp);
14901  </code></pre>
14902  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14903  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14904  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14905  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14906  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14907  * domain the page is running on including the 'www' like 'www.roojs.com')
14908  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14909  * @constructor
14910  * Create a new CookieProvider
14911  * @param {Object} config The configuration object
14912  */
14913 Roo.state.CookieProvider = function(config){
14914     Roo.state.CookieProvider.superclass.constructor.call(this);
14915     this.path = "/";
14916     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14917     this.domain = null;
14918     this.secure = false;
14919     Roo.apply(this, config);
14920     this.state = this.readCookies();
14921 };
14922
14923 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14924     // private
14925     set : function(name, value){
14926         if(typeof value == "undefined" || value === null){
14927             this.clear(name);
14928             return;
14929         }
14930         this.setCookie(name, value);
14931         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14932     },
14933
14934     // private
14935     clear : function(name){
14936         this.clearCookie(name);
14937         Roo.state.CookieProvider.superclass.clear.call(this, name);
14938     },
14939
14940     // private
14941     readCookies : function(){
14942         var cookies = {};
14943         var c = document.cookie + ";";
14944         var re = /\s?(.*?)=(.*?);/g;
14945         var matches;
14946         while((matches = re.exec(c)) != null){
14947             var name = matches[1];
14948             var value = matches[2];
14949             if(name && name.substring(0,3) == "ys-"){
14950                 cookies[name.substr(3)] = this.decodeValue(value);
14951             }
14952         }
14953         return cookies;
14954     },
14955
14956     // private
14957     setCookie : function(name, value){
14958         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14959            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14960            ((this.path == null) ? "" : ("; path=" + this.path)) +
14961            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14962            ((this.secure == true) ? "; secure" : "");
14963     },
14964
14965     // private
14966     clearCookie : function(name){
14967         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14968            ((this.path == null) ? "" : ("; path=" + this.path)) +
14969            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14970            ((this.secure == true) ? "; secure" : "");
14971     }
14972 });/*
14973  * Based on:
14974  * Ext JS Library 1.1.1
14975  * Copyright(c) 2006-2007, Ext JS, LLC.
14976  *
14977  * Originally Released Under LGPL - original licence link has changed is not relivant.
14978  *
14979  * Fork - LGPL
14980  * <script type="text/javascript">
14981  */
14982  
14983
14984 /**
14985  * @class Roo.ComponentMgr
14986  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14987  * @singleton
14988  */
14989 Roo.ComponentMgr = function(){
14990     var all = new Roo.util.MixedCollection();
14991
14992     return {
14993         /**
14994          * Registers a component.
14995          * @param {Roo.Component} c The component
14996          */
14997         register : function(c){
14998             all.add(c);
14999         },
15000
15001         /**
15002          * Unregisters a component.
15003          * @param {Roo.Component} c The component
15004          */
15005         unregister : function(c){
15006             all.remove(c);
15007         },
15008
15009         /**
15010          * Returns a component by id
15011          * @param {String} id The component id
15012          */
15013         get : function(id){
15014             return all.get(id);
15015         },
15016
15017         /**
15018          * Registers a function that will be called when a specified component is added to ComponentMgr
15019          * @param {String} id The component id
15020          * @param {Funtction} fn The callback function
15021          * @param {Object} scope The scope of the callback
15022          */
15023         onAvailable : function(id, fn, scope){
15024             all.on("add", function(index, o){
15025                 if(o.id == id){
15026                     fn.call(scope || o, o);
15027                     all.un("add", fn, scope);
15028                 }
15029             });
15030         }
15031     };
15032 }();/*
15033  * Based on:
15034  * Ext JS Library 1.1.1
15035  * Copyright(c) 2006-2007, Ext JS, LLC.
15036  *
15037  * Originally Released Under LGPL - original licence link has changed is not relivant.
15038  *
15039  * Fork - LGPL
15040  * <script type="text/javascript">
15041  */
15042  
15043 /**
15044  * @class Roo.Component
15045  * @extends Roo.util.Observable
15046  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15047  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15048  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15049  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15050  * All visual components (widgets) that require rendering into a layout should subclass Component.
15051  * @constructor
15052  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15053  * 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
15054  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15055  */
15056 Roo.Component = function(config){
15057     config = config || {};
15058     if(config.tagName || config.dom || typeof config == "string"){ // element object
15059         config = {el: config, id: config.id || config};
15060     }
15061     this.initialConfig = config;
15062
15063     Roo.apply(this, config);
15064     this.addEvents({
15065         /**
15066          * @event disable
15067          * Fires after the component is disabled.
15068              * @param {Roo.Component} this
15069              */
15070         disable : true,
15071         /**
15072          * @event enable
15073          * Fires after the component is enabled.
15074              * @param {Roo.Component} this
15075              */
15076         enable : true,
15077         /**
15078          * @event beforeshow
15079          * Fires before the component is shown.  Return false to stop the show.
15080              * @param {Roo.Component} this
15081              */
15082         beforeshow : true,
15083         /**
15084          * @event show
15085          * Fires after the component is shown.
15086              * @param {Roo.Component} this
15087              */
15088         show : true,
15089         /**
15090          * @event beforehide
15091          * Fires before the component is hidden. Return false to stop the hide.
15092              * @param {Roo.Component} this
15093              */
15094         beforehide : true,
15095         /**
15096          * @event hide
15097          * Fires after the component is hidden.
15098              * @param {Roo.Component} this
15099              */
15100         hide : true,
15101         /**
15102          * @event beforerender
15103          * Fires before the component is rendered. Return false to stop the render.
15104              * @param {Roo.Component} this
15105              */
15106         beforerender : true,
15107         /**
15108          * @event render
15109          * Fires after the component is rendered.
15110              * @param {Roo.Component} this
15111              */
15112         render : true,
15113         /**
15114          * @event beforedestroy
15115          * Fires before the component is destroyed. Return false to stop the destroy.
15116              * @param {Roo.Component} this
15117              */
15118         beforedestroy : true,
15119         /**
15120          * @event destroy
15121          * Fires after the component is destroyed.
15122              * @param {Roo.Component} this
15123              */
15124         destroy : true
15125     });
15126     if(!this.id){
15127         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15128     }
15129     Roo.ComponentMgr.register(this);
15130     Roo.Component.superclass.constructor.call(this);
15131     this.initComponent();
15132     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15133         this.render(this.renderTo);
15134         delete this.renderTo;
15135     }
15136 };
15137
15138 /** @private */
15139 Roo.Component.AUTO_ID = 1000;
15140
15141 Roo.extend(Roo.Component, Roo.util.Observable, {
15142     /**
15143      * @scope Roo.Component.prototype
15144      * @type {Boolean}
15145      * true if this component is hidden. Read-only.
15146      */
15147     hidden : false,
15148     /**
15149      * @type {Boolean}
15150      * true if this component is disabled. Read-only.
15151      */
15152     disabled : false,
15153     /**
15154      * @type {Boolean}
15155      * true if this component has been rendered. Read-only.
15156      */
15157     rendered : false,
15158     
15159     /** @cfg {String} disableClass
15160      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15161      */
15162     disabledClass : "x-item-disabled",
15163         /** @cfg {Boolean} allowDomMove
15164          * Whether the component can move the Dom node when rendering (defaults to true).
15165          */
15166     allowDomMove : true,
15167     /** @cfg {String} hideMode
15168      * How this component should hidden. Supported values are
15169      * "visibility" (css visibility), "offsets" (negative offset position) and
15170      * "display" (css display) - defaults to "display".
15171      */
15172     hideMode: 'display',
15173
15174     /** @private */
15175     ctype : "Roo.Component",
15176
15177     /**
15178      * @cfg {String} actionMode 
15179      * which property holds the element that used for  hide() / show() / disable() / enable()
15180      * default is 'el' 
15181      */
15182     actionMode : "el",
15183
15184     /** @private */
15185     getActionEl : function(){
15186         return this[this.actionMode];
15187     },
15188
15189     initComponent : Roo.emptyFn,
15190     /**
15191      * If this is a lazy rendering component, render it to its container element.
15192      * @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.
15193      */
15194     render : function(container, position){
15195         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15196             if(!container && this.el){
15197                 this.el = Roo.get(this.el);
15198                 container = this.el.dom.parentNode;
15199                 this.allowDomMove = false;
15200             }
15201             this.container = Roo.get(container);
15202             this.rendered = true;
15203             if(position !== undefined){
15204                 if(typeof position == 'number'){
15205                     position = this.container.dom.childNodes[position];
15206                 }else{
15207                     position = Roo.getDom(position);
15208                 }
15209             }
15210             this.onRender(this.container, position || null);
15211             if(this.cls){
15212                 this.el.addClass(this.cls);
15213                 delete this.cls;
15214             }
15215             if(this.style){
15216                 this.el.applyStyles(this.style);
15217                 delete this.style;
15218             }
15219             this.fireEvent("render", this);
15220             this.afterRender(this.container);
15221             if(this.hidden){
15222                 this.hide();
15223             }
15224             if(this.disabled){
15225                 this.disable();
15226             }
15227         }
15228         return this;
15229     },
15230
15231     /** @private */
15232     // default function is not really useful
15233     onRender : function(ct, position){
15234         if(this.el){
15235             this.el = Roo.get(this.el);
15236             if(this.allowDomMove !== false){
15237                 ct.dom.insertBefore(this.el.dom, position);
15238             }
15239         }
15240     },
15241
15242     /** @private */
15243     getAutoCreate : function(){
15244         var cfg = typeof this.autoCreate == "object" ?
15245                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15246         if(this.id && !cfg.id){
15247             cfg.id = this.id;
15248         }
15249         return cfg;
15250     },
15251
15252     /** @private */
15253     afterRender : Roo.emptyFn,
15254
15255     /**
15256      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15257      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15258      */
15259     destroy : function(){
15260         if(this.fireEvent("beforedestroy", this) !== false){
15261             this.purgeListeners();
15262             this.beforeDestroy();
15263             if(this.rendered){
15264                 this.el.removeAllListeners();
15265                 this.el.remove();
15266                 if(this.actionMode == "container"){
15267                     this.container.remove();
15268                 }
15269             }
15270             this.onDestroy();
15271             Roo.ComponentMgr.unregister(this);
15272             this.fireEvent("destroy", this);
15273         }
15274     },
15275
15276         /** @private */
15277     beforeDestroy : function(){
15278
15279     },
15280
15281         /** @private */
15282         onDestroy : function(){
15283
15284     },
15285
15286     /**
15287      * Returns the underlying {@link Roo.Element}.
15288      * @return {Roo.Element} The element
15289      */
15290     getEl : function(){
15291         return this.el;
15292     },
15293
15294     /**
15295      * Returns the id of this component.
15296      * @return {String}
15297      */
15298     getId : function(){
15299         return this.id;
15300     },
15301
15302     /**
15303      * Try to focus this component.
15304      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15305      * @return {Roo.Component} this
15306      */
15307     focus : function(selectText){
15308         if(this.rendered){
15309             this.el.focus();
15310             if(selectText === true){
15311                 this.el.dom.select();
15312             }
15313         }
15314         return this;
15315     },
15316
15317     /** @private */
15318     blur : function(){
15319         if(this.rendered){
15320             this.el.blur();
15321         }
15322         return this;
15323     },
15324
15325     /**
15326      * Disable this component.
15327      * @return {Roo.Component} this
15328      */
15329     disable : function(){
15330         if(this.rendered){
15331             this.onDisable();
15332         }
15333         this.disabled = true;
15334         this.fireEvent("disable", this);
15335         return this;
15336     },
15337
15338         // private
15339     onDisable : function(){
15340         this.getActionEl().addClass(this.disabledClass);
15341         this.el.dom.disabled = true;
15342     },
15343
15344     /**
15345      * Enable this component.
15346      * @return {Roo.Component} this
15347      */
15348     enable : function(){
15349         if(this.rendered){
15350             this.onEnable();
15351         }
15352         this.disabled = false;
15353         this.fireEvent("enable", this);
15354         return this;
15355     },
15356
15357         // private
15358     onEnable : function(){
15359         this.getActionEl().removeClass(this.disabledClass);
15360         this.el.dom.disabled = false;
15361     },
15362
15363     /**
15364      * Convenience function for setting disabled/enabled by boolean.
15365      * @param {Boolean} disabled
15366      */
15367     setDisabled : function(disabled){
15368         this[disabled ? "disable" : "enable"]();
15369     },
15370
15371     /**
15372      * Show this component.
15373      * @return {Roo.Component} this
15374      */
15375     show: function(){
15376         if(this.fireEvent("beforeshow", this) !== false){
15377             this.hidden = false;
15378             if(this.rendered){
15379                 this.onShow();
15380             }
15381             this.fireEvent("show", this);
15382         }
15383         return this;
15384     },
15385
15386     // private
15387     onShow : function(){
15388         var ae = this.getActionEl();
15389         if(this.hideMode == 'visibility'){
15390             ae.dom.style.visibility = "visible";
15391         }else if(this.hideMode == 'offsets'){
15392             ae.removeClass('x-hidden');
15393         }else{
15394             ae.dom.style.display = "";
15395         }
15396     },
15397
15398     /**
15399      * Hide this component.
15400      * @return {Roo.Component} this
15401      */
15402     hide: function(){
15403         if(this.fireEvent("beforehide", this) !== false){
15404             this.hidden = true;
15405             if(this.rendered){
15406                 this.onHide();
15407             }
15408             this.fireEvent("hide", this);
15409         }
15410         return this;
15411     },
15412
15413     // private
15414     onHide : function(){
15415         var ae = this.getActionEl();
15416         if(this.hideMode == 'visibility'){
15417             ae.dom.style.visibility = "hidden";
15418         }else if(this.hideMode == 'offsets'){
15419             ae.addClass('x-hidden');
15420         }else{
15421             ae.dom.style.display = "none";
15422         }
15423     },
15424
15425     /**
15426      * Convenience function to hide or show this component by boolean.
15427      * @param {Boolean} visible True to show, false to hide
15428      * @return {Roo.Component} this
15429      */
15430     setVisible: function(visible){
15431         if(visible) {
15432             this.show();
15433         }else{
15434             this.hide();
15435         }
15436         return this;
15437     },
15438
15439     /**
15440      * Returns true if this component is visible.
15441      */
15442     isVisible : function(){
15443         return this.getActionEl().isVisible();
15444     },
15445
15446     cloneConfig : function(overrides){
15447         overrides = overrides || {};
15448         var id = overrides.id || Roo.id();
15449         var cfg = Roo.applyIf(overrides, this.initialConfig);
15450         cfg.id = id; // prevent dup id
15451         return new this.constructor(cfg);
15452     }
15453 });/*
15454  * Based on:
15455  * Ext JS Library 1.1.1
15456  * Copyright(c) 2006-2007, Ext JS, LLC.
15457  *
15458  * Originally Released Under LGPL - original licence link has changed is not relivant.
15459  *
15460  * Fork - LGPL
15461  * <script type="text/javascript">
15462  */
15463
15464 /**
15465  * @class Roo.BoxComponent
15466  * @extends Roo.Component
15467  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15468  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15469  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15470  * layout containers.
15471  * @constructor
15472  * @param {Roo.Element/String/Object} config The configuration options.
15473  */
15474 Roo.BoxComponent = function(config){
15475     Roo.Component.call(this, config);
15476     this.addEvents({
15477         /**
15478          * @event resize
15479          * Fires after the component is resized.
15480              * @param {Roo.Component} this
15481              * @param {Number} adjWidth The box-adjusted width that was set
15482              * @param {Number} adjHeight The box-adjusted height that was set
15483              * @param {Number} rawWidth The width that was originally specified
15484              * @param {Number} rawHeight The height that was originally specified
15485              */
15486         resize : true,
15487         /**
15488          * @event move
15489          * Fires after the component is moved.
15490              * @param {Roo.Component} this
15491              * @param {Number} x The new x position
15492              * @param {Number} y The new y position
15493              */
15494         move : true
15495     });
15496 };
15497
15498 Roo.extend(Roo.BoxComponent, Roo.Component, {
15499     // private, set in afterRender to signify that the component has been rendered
15500     boxReady : false,
15501     // private, used to defer height settings to subclasses
15502     deferHeight: false,
15503     /** @cfg {Number} width
15504      * width (optional) size of component
15505      */
15506      /** @cfg {Number} height
15507      * height (optional) size of component
15508      */
15509      
15510     /**
15511      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15512      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15513      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15514      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15515      * @return {Roo.BoxComponent} this
15516      */
15517     setSize : function(w, h){
15518         // support for standard size objects
15519         if(typeof w == 'object'){
15520             h = w.height;
15521             w = w.width;
15522         }
15523         // not rendered
15524         if(!this.boxReady){
15525             this.width = w;
15526             this.height = h;
15527             return this;
15528         }
15529
15530         // prevent recalcs when not needed
15531         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15532             return this;
15533         }
15534         this.lastSize = {width: w, height: h};
15535
15536         var adj = this.adjustSize(w, h);
15537         var aw = adj.width, ah = adj.height;
15538         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15539             var rz = this.getResizeEl();
15540             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15541                 rz.setSize(aw, ah);
15542             }else if(!this.deferHeight && ah !== undefined){
15543                 rz.setHeight(ah);
15544             }else if(aw !== undefined){
15545                 rz.setWidth(aw);
15546             }
15547             this.onResize(aw, ah, w, h);
15548             this.fireEvent('resize', this, aw, ah, w, h);
15549         }
15550         return this;
15551     },
15552
15553     /**
15554      * Gets the current size of the component's underlying element.
15555      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15556      */
15557     getSize : function(){
15558         return this.el.getSize();
15559     },
15560
15561     /**
15562      * Gets the current XY position of the component's underlying element.
15563      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15564      * @return {Array} The XY position of the element (e.g., [100, 200])
15565      */
15566     getPosition : function(local){
15567         if(local === true){
15568             return [this.el.getLeft(true), this.el.getTop(true)];
15569         }
15570         return this.xy || this.el.getXY();
15571     },
15572
15573     /**
15574      * Gets the current box measurements of the component's underlying element.
15575      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15576      * @returns {Object} box An object in the format {x, y, width, height}
15577      */
15578     getBox : function(local){
15579         var s = this.el.getSize();
15580         if(local){
15581             s.x = this.el.getLeft(true);
15582             s.y = this.el.getTop(true);
15583         }else{
15584             var xy = this.xy || this.el.getXY();
15585             s.x = xy[0];
15586             s.y = xy[1];
15587         }
15588         return s;
15589     },
15590
15591     /**
15592      * Sets the current box measurements of the component's underlying element.
15593      * @param {Object} box An object in the format {x, y, width, height}
15594      * @returns {Roo.BoxComponent} this
15595      */
15596     updateBox : function(box){
15597         this.setSize(box.width, box.height);
15598         this.setPagePosition(box.x, box.y);
15599         return this;
15600     },
15601
15602     // protected
15603     getResizeEl : function(){
15604         return this.resizeEl || this.el;
15605     },
15606
15607     // protected
15608     getPositionEl : function(){
15609         return this.positionEl || this.el;
15610     },
15611
15612     /**
15613      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15614      * This method fires the move event.
15615      * @param {Number} left The new left
15616      * @param {Number} top The new top
15617      * @returns {Roo.BoxComponent} this
15618      */
15619     setPosition : function(x, y){
15620         this.x = x;
15621         this.y = y;
15622         if(!this.boxReady){
15623             return this;
15624         }
15625         var adj = this.adjustPosition(x, y);
15626         var ax = adj.x, ay = adj.y;
15627
15628         var el = this.getPositionEl();
15629         if(ax !== undefined || ay !== undefined){
15630             if(ax !== undefined && ay !== undefined){
15631                 el.setLeftTop(ax, ay);
15632             }else if(ax !== undefined){
15633                 el.setLeft(ax);
15634             }else if(ay !== undefined){
15635                 el.setTop(ay);
15636             }
15637             this.onPosition(ax, ay);
15638             this.fireEvent('move', this, ax, ay);
15639         }
15640         return this;
15641     },
15642
15643     /**
15644      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15645      * This method fires the move event.
15646      * @param {Number} x The new x position
15647      * @param {Number} y The new y position
15648      * @returns {Roo.BoxComponent} this
15649      */
15650     setPagePosition : function(x, y){
15651         this.pageX = x;
15652         this.pageY = y;
15653         if(!this.boxReady){
15654             return;
15655         }
15656         if(x === undefined || y === undefined){ // cannot translate undefined points
15657             return;
15658         }
15659         var p = this.el.translatePoints(x, y);
15660         this.setPosition(p.left, p.top);
15661         return this;
15662     },
15663
15664     // private
15665     onRender : function(ct, position){
15666         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15667         if(this.resizeEl){
15668             this.resizeEl = Roo.get(this.resizeEl);
15669         }
15670         if(this.positionEl){
15671             this.positionEl = Roo.get(this.positionEl);
15672         }
15673     },
15674
15675     // private
15676     afterRender : function(){
15677         Roo.BoxComponent.superclass.afterRender.call(this);
15678         this.boxReady = true;
15679         this.setSize(this.width, this.height);
15680         if(this.x || this.y){
15681             this.setPosition(this.x, this.y);
15682         }
15683         if(this.pageX || this.pageY){
15684             this.setPagePosition(this.pageX, this.pageY);
15685         }
15686     },
15687
15688     /**
15689      * Force the component's size to recalculate based on the underlying element's current height and width.
15690      * @returns {Roo.BoxComponent} this
15691      */
15692     syncSize : function(){
15693         delete this.lastSize;
15694         this.setSize(this.el.getWidth(), this.el.getHeight());
15695         return this;
15696     },
15697
15698     /**
15699      * Called after the component is resized, this method is empty by default but can be implemented by any
15700      * subclass that needs to perform custom logic after a resize occurs.
15701      * @param {Number} adjWidth The box-adjusted width that was set
15702      * @param {Number} adjHeight The box-adjusted height that was set
15703      * @param {Number} rawWidth The width that was originally specified
15704      * @param {Number} rawHeight The height that was originally specified
15705      */
15706     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15707
15708     },
15709
15710     /**
15711      * Called after the component is moved, this method is empty by default but can be implemented by any
15712      * subclass that needs to perform custom logic after a move occurs.
15713      * @param {Number} x The new x position
15714      * @param {Number} y The new y position
15715      */
15716     onPosition : function(x, y){
15717
15718     },
15719
15720     // private
15721     adjustSize : function(w, h){
15722         if(this.autoWidth){
15723             w = 'auto';
15724         }
15725         if(this.autoHeight){
15726             h = 'auto';
15727         }
15728         return {width : w, height: h};
15729     },
15730
15731     // private
15732     adjustPosition : function(x, y){
15733         return {x : x, y: y};
15734     }
15735 });/*
15736  * Original code for Roojs - LGPL
15737  * <script type="text/javascript">
15738  */
15739  
15740 /**
15741  * @class Roo.XComponent
15742  * A delayed Element creator...
15743  * Or a way to group chunks of interface together.
15744  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15745  *  used in conjunction with XComponent.build() it will create an instance of each element,
15746  *  then call addxtype() to build the User interface.
15747  * 
15748  * Mypart.xyx = new Roo.XComponent({
15749
15750     parent : 'Mypart.xyz', // empty == document.element.!!
15751     order : '001',
15752     name : 'xxxx'
15753     region : 'xxxx'
15754     disabled : function() {} 
15755      
15756     tree : function() { // return an tree of xtype declared components
15757         var MODULE = this;
15758         return 
15759         {
15760             xtype : 'NestedLayoutPanel',
15761             // technicall
15762         }
15763      ]
15764  *})
15765  *
15766  *
15767  * It can be used to build a big heiracy, with parent etc.
15768  * or you can just use this to render a single compoent to a dom element
15769  * MYPART.render(Roo.Element | String(id) | dom_element )
15770  *
15771  *
15772  * Usage patterns.
15773  *
15774  * Classic Roo
15775  *
15776  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15777  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15778  *
15779  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15780  *
15781  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15782  * - if mulitple topModules exist, the last one is defined as the top module.
15783  *
15784  * Embeded Roo
15785  * 
15786  * When the top level or multiple modules are to embedded into a existing HTML page,
15787  * the parent element can container '#id' of the element where the module will be drawn.
15788  *
15789  * Bootstrap Roo
15790  *
15791  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15792  * it relies more on a include mechanism, where sub modules are included into an outer page.
15793  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15794  * 
15795  * Bootstrap Roo Included elements
15796  *
15797  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15798  * hence confusing the component builder as it thinks there are multiple top level elements. 
15799  *
15800  * 
15801  * 
15802  * @extends Roo.util.Observable
15803  * @constructor
15804  * @param cfg {Object} configuration of component
15805  * 
15806  */
15807 Roo.XComponent = function(cfg) {
15808     Roo.apply(this, cfg);
15809     this.addEvents({ 
15810         /**
15811              * @event built
15812              * Fires when this the componnt is built
15813              * @param {Roo.XComponent} c the component
15814              */
15815         'built' : true
15816         
15817     });
15818     this.region = this.region || 'center'; // default..
15819     Roo.XComponent.register(this);
15820     this.modules = false;
15821     this.el = false; // where the layout goes..
15822     
15823     
15824 }
15825 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15826     /**
15827      * @property el
15828      * The created element (with Roo.factory())
15829      * @type {Roo.Layout}
15830      */
15831     el  : false,
15832     
15833     /**
15834      * @property el
15835      * for BC  - use el in new code
15836      * @type {Roo.Layout}
15837      */
15838     panel : false,
15839     
15840     /**
15841      * @property layout
15842      * for BC  - use el in new code
15843      * @type {Roo.Layout}
15844      */
15845     layout : false,
15846     
15847      /**
15848      * @cfg {Function|boolean} disabled
15849      * If this module is disabled by some rule, return true from the funtion
15850      */
15851     disabled : false,
15852     
15853     /**
15854      * @cfg {String} parent 
15855      * Name of parent element which it get xtype added to..
15856      */
15857     parent: false,
15858     
15859     /**
15860      * @cfg {String} order
15861      * Used to set the order in which elements are created (usefull for multiple tabs)
15862      */
15863     
15864     order : false,
15865     /**
15866      * @cfg {String} name
15867      * String to display while loading.
15868      */
15869     name : false,
15870     /**
15871      * @cfg {String} region
15872      * Region to render component to (defaults to center)
15873      */
15874     region : 'center',
15875     
15876     /**
15877      * @cfg {Array} items
15878      * A single item array - the first element is the root of the tree..
15879      * It's done this way to stay compatible with the Xtype system...
15880      */
15881     items : false,
15882     
15883     /**
15884      * @property _tree
15885      * The method that retuns the tree of parts that make up this compoennt 
15886      * @type {function}
15887      */
15888     _tree  : false,
15889     
15890      /**
15891      * render
15892      * render element to dom or tree
15893      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15894      */
15895     
15896     render : function(el)
15897     {
15898         
15899         el = el || false;
15900         var hp = this.parent ? 1 : 0;
15901         Roo.log(this);
15902         
15903         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15904             // if parent is a '#.....' string, then let's use that..
15905             var ename = this.parent.substr(1);
15906             this.parent = false;
15907             Roo.log(ename);
15908             switch (ename) {
15909                 case 'bootstrap-body' :
15910                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15911                         this.parent = { el :  new  Roo.bootstrap.Body() };
15912                         Roo.log("setting el to doc body");
15913                          
15914                     } else {
15915                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15916                     }
15917                     break;
15918                 case 'bootstrap':
15919                     this.parent = { el : true};
15920                     // fall through
15921                 default:
15922                     el = Roo.get(ename);
15923                     break;
15924             }
15925                 
15926             
15927             if (!el && !this.parent) {
15928                 Roo.log("Warning - element can not be found :#" + ename );
15929                 return;
15930             }
15931         }
15932         Roo.log("EL:");Roo.log(el);
15933         Roo.log("this.parent.el:");Roo.log(this.parent.el);
15934         
15935         var tree = this._tree ? this._tree() : this.tree();
15936
15937         // altertive root elements ??? - we need a better way to indicate these.
15938         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15939                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15940         
15941         if (!this.parent && is_alt) {
15942             //el = Roo.get(document.body);
15943             this.parent = { el : true };
15944         }
15945             
15946             
15947         
15948         if (!this.parent) {
15949             
15950             Roo.log("no parent - creating one");
15951             
15952             el = el ? Roo.get(el) : false;      
15953             
15954             // it's a top level one..
15955             this.parent =  {
15956                 el : new Roo.BorderLayout(el || document.body, {
15957                 
15958                      center: {
15959                          titlebar: false,
15960                          autoScroll:false,
15961                          closeOnTab: true,
15962                          tabPosition: 'top',
15963                           //resizeTabs: true,
15964                          alwaysShowTabs: el && hp? false :  true,
15965                          hideTabs: el || !hp ? true :  false,
15966                          minTabWidth: 140
15967                      }
15968                  })
15969             }
15970         }
15971         
15972                 if (!this.parent.el) {
15973                         // probably an old style ctor, which has been disabled.
15974                         return;
15975                         
15976                 }
15977                 // The 'tree' method is  '_tree now' 
15978             
15979         tree.region = tree.region || this.region;
15980         
15981         if (this.parent.el === true) {
15982             // bootstrap... - body..
15983             this.parent.el = Roo.factory(tree);
15984         }
15985         
15986         this.el = this.parent.el.addxtype(tree);
15987         this.fireEvent('built', this);
15988         
15989         this.panel = this.el;
15990         this.layout = this.panel.layout;
15991                 this.parentLayout = this.parent.layout  || false;  
15992          
15993     }
15994     
15995 });
15996
15997 Roo.apply(Roo.XComponent, {
15998     /**
15999      * @property  hideProgress
16000      * true to disable the building progress bar.. usefull on single page renders.
16001      * @type Boolean
16002      */
16003     hideProgress : false,
16004     /**
16005      * @property  buildCompleted
16006      * True when the builder has completed building the interface.
16007      * @type Boolean
16008      */
16009     buildCompleted : false,
16010      
16011     /**
16012      * @property  topModule
16013      * the upper most module - uses document.element as it's constructor.
16014      * @type Object
16015      */
16016      
16017     topModule  : false,
16018       
16019     /**
16020      * @property  modules
16021      * array of modules to be created by registration system.
16022      * @type {Array} of Roo.XComponent
16023      */
16024     
16025     modules : [],
16026     /**
16027      * @property  elmodules
16028      * array of modules to be created by which use #ID 
16029      * @type {Array} of Roo.XComponent
16030      */
16031      
16032     elmodules : [],
16033
16034      /**
16035      * @property  build_from_html
16036      * Build elements from html - used by bootstrap HTML stuff 
16037      *    - this is cleared after build is completed
16038      * @type {boolean} true  (default false)
16039      */
16040      
16041     build_from_html : false,
16042
16043     /**
16044      * Register components to be built later.
16045      *
16046      * This solves the following issues
16047      * - Building is not done on page load, but after an authentication process has occured.
16048      * - Interface elements are registered on page load
16049      * - Parent Interface elements may not be loaded before child, so this handles that..
16050      * 
16051      *
16052      * example:
16053      * 
16054      * MyApp.register({
16055           order : '000001',
16056           module : 'Pman.Tab.projectMgr',
16057           region : 'center',
16058           parent : 'Pman.layout',
16059           disabled : false,  // or use a function..
16060         })
16061      
16062      * * @param {Object} details about module
16063      */
16064     register : function(obj) {
16065                 
16066         Roo.XComponent.event.fireEvent('register', obj);
16067         switch(typeof(obj.disabled) ) {
16068                 
16069             case 'undefined':
16070                 break;
16071             
16072             case 'function':
16073                 if ( obj.disabled() ) {
16074                         return;
16075                 }
16076                 break;
16077             
16078             default:
16079                 if (obj.disabled) {
16080                         return;
16081                 }
16082                 break;
16083         }
16084                 
16085         this.modules.push(obj);
16086          
16087     },
16088     /**
16089      * convert a string to an object..
16090      * eg. 'AAA.BBB' -> finds AAA.BBB
16091
16092      */
16093     
16094     toObject : function(str)
16095     {
16096         if (!str || typeof(str) == 'object') {
16097             return str;
16098         }
16099         if (str.substring(0,1) == '#') {
16100             return str;
16101         }
16102
16103         var ar = str.split('.');
16104         var rt, o;
16105         rt = ar.shift();
16106             /** eval:var:o */
16107         try {
16108             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16109         } catch (e) {
16110             throw "Module not found : " + str;
16111         }
16112         
16113         if (o === false) {
16114             throw "Module not found : " + str;
16115         }
16116         Roo.each(ar, function(e) {
16117             if (typeof(o[e]) == 'undefined') {
16118                 throw "Module not found : " + str;
16119             }
16120             o = o[e];
16121         });
16122         
16123         return o;
16124         
16125     },
16126     
16127     
16128     /**
16129      * move modules into their correct place in the tree..
16130      * 
16131      */
16132     preBuild : function ()
16133     {
16134         var _t = this;
16135         Roo.each(this.modules , function (obj)
16136         {
16137             Roo.XComponent.event.fireEvent('beforebuild', obj);
16138             
16139             var opar = obj.parent;
16140             try { 
16141                 obj.parent = this.toObject(opar);
16142             } catch(e) {
16143                 Roo.log("parent:toObject failed: " + e.toString());
16144                 return;
16145             }
16146             
16147             if (!obj.parent) {
16148                 Roo.debug && Roo.log("GOT top level module");
16149                 Roo.debug && Roo.log(obj);
16150                 obj.modules = new Roo.util.MixedCollection(false, 
16151                     function(o) { return o.order + '' }
16152                 );
16153                 this.topModule = obj;
16154                 return;
16155             }
16156                         // parent is a string (usually a dom element name..)
16157             if (typeof(obj.parent) == 'string') {
16158                 this.elmodules.push(obj);
16159                 return;
16160             }
16161             if (obj.parent.constructor != Roo.XComponent) {
16162                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16163             }
16164             if (!obj.parent.modules) {
16165                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16166                     function(o) { return o.order + '' }
16167                 );
16168             }
16169             if (obj.parent.disabled) {
16170                 obj.disabled = true;
16171             }
16172             obj.parent.modules.add(obj);
16173         }, this);
16174     },
16175     
16176      /**
16177      * make a list of modules to build.
16178      * @return {Array} list of modules. 
16179      */ 
16180     
16181     buildOrder : function()
16182     {
16183         var _this = this;
16184         var cmp = function(a,b) {   
16185             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16186         };
16187         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16188             throw "No top level modules to build";
16189         }
16190         
16191         // make a flat list in order of modules to build.
16192         var mods = this.topModule ? [ this.topModule ] : [];
16193                 
16194         
16195         // elmodules (is a list of DOM based modules )
16196         Roo.each(this.elmodules, function(e) {
16197             mods.push(e);
16198             if (!this.topModule &&
16199                 typeof(e.parent) == 'string' &&
16200                 e.parent.substring(0,1) == '#' &&
16201                 Roo.get(e.parent.substr(1))
16202                ) {
16203                 
16204                 _this.topModule = e;
16205             }
16206             
16207         });
16208
16209         
16210         // add modules to their parents..
16211         var addMod = function(m) {
16212             Roo.debug && Roo.log("build Order: add: " + m.name);
16213                 
16214             mods.push(m);
16215             if (m.modules && !m.disabled) {
16216                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16217                 m.modules.keySort('ASC',  cmp );
16218                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16219     
16220                 m.modules.each(addMod);
16221             } else {
16222                 Roo.debug && Roo.log("build Order: no child modules");
16223             }
16224             // not sure if this is used any more..
16225             if (m.finalize) {
16226                 m.finalize.name = m.name + " (clean up) ";
16227                 mods.push(m.finalize);
16228             }
16229             
16230         }
16231         if (this.topModule && this.topModule.modules) { 
16232             this.topModule.modules.keySort('ASC',  cmp );
16233             this.topModule.modules.each(addMod);
16234         } 
16235         return mods;
16236     },
16237     
16238      /**
16239      * Build the registered modules.
16240      * @param {Object} parent element.
16241      * @param {Function} optional method to call after module has been added.
16242      * 
16243      */ 
16244    
16245     build : function(opts) 
16246     {
16247         
16248         if (typeof(opts) != 'undefined') {
16249             Roo.apply(this,opts);
16250         }
16251         
16252         this.preBuild();
16253         var mods = this.buildOrder();
16254       
16255         //this.allmods = mods;
16256         //Roo.debug && Roo.log(mods);
16257         //return;
16258         if (!mods.length) { // should not happen
16259             throw "NO modules!!!";
16260         }
16261         
16262         
16263         var msg = "Building Interface...";
16264         // flash it up as modal - so we store the mask!?
16265         if (!this.hideProgress && Roo.MessageBox) {
16266             Roo.MessageBox.show({ title: 'loading' });
16267             Roo.MessageBox.show({
16268                title: "Please wait...",
16269                msg: msg,
16270                width:450,
16271                progress:true,
16272                closable:false,
16273                modal: false
16274               
16275             });
16276         }
16277         var total = mods.length;
16278         
16279         var _this = this;
16280         var progressRun = function() {
16281             if (!mods.length) {
16282                 Roo.debug && Roo.log('hide?');
16283                 if (!this.hideProgress && Roo.MessageBox) {
16284                     Roo.MessageBox.hide();
16285                 }
16286                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16287                 
16288                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16289                 
16290                 // THE END...
16291                 return false;   
16292             }
16293             
16294             var m = mods.shift();
16295             
16296             
16297             Roo.debug && Roo.log(m);
16298             // not sure if this is supported any more.. - modules that are are just function
16299             if (typeof(m) == 'function') { 
16300                 m.call(this);
16301                 return progressRun.defer(10, _this);
16302             } 
16303             
16304             
16305             msg = "Building Interface " + (total  - mods.length) + 
16306                     " of " + total + 
16307                     (m.name ? (' - ' + m.name) : '');
16308                         Roo.debug && Roo.log(msg);
16309             if (!this.hideProgress &&  Roo.MessageBox) { 
16310                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16311             }
16312             
16313          
16314             // is the module disabled?
16315             var disabled = (typeof(m.disabled) == 'function') ?
16316                 m.disabled.call(m.module.disabled) : m.disabled;    
16317             
16318             
16319             if (disabled) {
16320                 return progressRun(); // we do not update the display!
16321             }
16322             
16323             // now build 
16324             
16325                         
16326                         
16327             m.render();
16328             // it's 10 on top level, and 1 on others??? why...
16329             return progressRun.defer(10, _this);
16330              
16331         }
16332         progressRun.defer(1, _this);
16333      
16334         
16335         
16336     },
16337         
16338         
16339         /**
16340          * Event Object.
16341          *
16342          *
16343          */
16344         event: false, 
16345     /**
16346          * wrapper for event.on - aliased later..  
16347          * Typically use to register a event handler for register:
16348          *
16349          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16350          *
16351          */
16352     on : false
16353    
16354     
16355     
16356 });
16357
16358 Roo.XComponent.event = new Roo.util.Observable({
16359                 events : { 
16360                         /**
16361                          * @event register
16362                          * Fires when an Component is registered,
16363                          * set the disable property on the Component to stop registration.
16364                          * @param {Roo.XComponent} c the component being registerd.
16365                          * 
16366                          */
16367                         'register' : true,
16368             /**
16369                          * @event beforebuild
16370                          * Fires before each Component is built
16371                          * can be used to apply permissions.
16372                          * @param {Roo.XComponent} c the component being registerd.
16373                          * 
16374                          */
16375                         'beforebuild' : true,
16376                         /**
16377                          * @event buildcomplete
16378                          * Fires on the top level element when all elements have been built
16379                          * @param {Roo.XComponent} the top level component.
16380                          */
16381                         'buildcomplete' : true
16382                         
16383                 }
16384 });
16385
16386 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16387  /*
16388  * Based on:
16389  * Ext JS Library 1.1.1
16390  * Copyright(c) 2006-2007, Ext JS, LLC.
16391  *
16392  * Originally Released Under LGPL - original licence link has changed is not relivant.
16393  *
16394  * Fork - LGPL
16395  * <script type="text/javascript">
16396  */
16397
16398
16399
16400 /*
16401  * These classes are derivatives of the similarly named classes in the YUI Library.
16402  * The original license:
16403  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16404  * Code licensed under the BSD License:
16405  * http://developer.yahoo.net/yui/license.txt
16406  */
16407
16408 (function() {
16409
16410 var Event=Roo.EventManager;
16411 var Dom=Roo.lib.Dom;
16412
16413 /**
16414  * @class Roo.dd.DragDrop
16415  * @extends Roo.util.Observable
16416  * Defines the interface and base operation of items that that can be
16417  * dragged or can be drop targets.  It was designed to be extended, overriding
16418  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16419  * Up to three html elements can be associated with a DragDrop instance:
16420  * <ul>
16421  * <li>linked element: the element that is passed into the constructor.
16422  * This is the element which defines the boundaries for interaction with
16423  * other DragDrop objects.</li>
16424  * <li>handle element(s): The drag operation only occurs if the element that
16425  * was clicked matches a handle element.  By default this is the linked
16426  * element, but there are times that you will want only a portion of the
16427  * linked element to initiate the drag operation, and the setHandleElId()
16428  * method provides a way to define this.</li>
16429  * <li>drag element: this represents the element that would be moved along
16430  * with the cursor during a drag operation.  By default, this is the linked
16431  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16432  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16433  * </li>
16434  * </ul>
16435  * This class should not be instantiated until the onload event to ensure that
16436  * the associated elements are available.
16437  * The following would define a DragDrop obj that would interact with any
16438  * other DragDrop obj in the "group1" group:
16439  * <pre>
16440  *  dd = new Roo.dd.DragDrop("div1", "group1");
16441  * </pre>
16442  * Since none of the event handlers have been implemented, nothing would
16443  * actually happen if you were to run the code above.  Normally you would
16444  * override this class or one of the default implementations, but you can
16445  * also override the methods you want on an instance of the class...
16446  * <pre>
16447  *  dd.onDragDrop = function(e, id) {
16448  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16449  *  }
16450  * </pre>
16451  * @constructor
16452  * @param {String} id of the element that is linked to this instance
16453  * @param {String} sGroup the group of related DragDrop objects
16454  * @param {object} config an object containing configurable attributes
16455  *                Valid properties for DragDrop:
16456  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16457  */
16458 Roo.dd.DragDrop = function(id, sGroup, config) {
16459     if (id) {
16460         this.init(id, sGroup, config);
16461     }
16462     
16463 };
16464
16465 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16466
16467     /**
16468      * The id of the element associated with this object.  This is what we
16469      * refer to as the "linked element" because the size and position of
16470      * this element is used to determine when the drag and drop objects have
16471      * interacted.
16472      * @property id
16473      * @type String
16474      */
16475     id: null,
16476
16477     /**
16478      * Configuration attributes passed into the constructor
16479      * @property config
16480      * @type object
16481      */
16482     config: null,
16483
16484     /**
16485      * The id of the element that will be dragged.  By default this is same
16486      * as the linked element , but could be changed to another element. Ex:
16487      * Roo.dd.DDProxy
16488      * @property dragElId
16489      * @type String
16490      * @private
16491      */
16492     dragElId: null,
16493
16494     /**
16495      * the id of the element that initiates the drag operation.  By default
16496      * this is the linked element, but could be changed to be a child of this
16497      * element.  This lets us do things like only starting the drag when the
16498      * header element within the linked html element is clicked.
16499      * @property handleElId
16500      * @type String
16501      * @private
16502      */
16503     handleElId: null,
16504
16505     /**
16506      * An associative array of HTML tags that will be ignored if clicked.
16507      * @property invalidHandleTypes
16508      * @type {string: string}
16509      */
16510     invalidHandleTypes: null,
16511
16512     /**
16513      * An associative array of ids for elements that will be ignored if clicked
16514      * @property invalidHandleIds
16515      * @type {string: string}
16516      */
16517     invalidHandleIds: null,
16518
16519     /**
16520      * An indexted array of css class names for elements that will be ignored
16521      * if clicked.
16522      * @property invalidHandleClasses
16523      * @type string[]
16524      */
16525     invalidHandleClasses: null,
16526
16527     /**
16528      * The linked element's absolute X position at the time the drag was
16529      * started
16530      * @property startPageX
16531      * @type int
16532      * @private
16533      */
16534     startPageX: 0,
16535
16536     /**
16537      * The linked element's absolute X position at the time the drag was
16538      * started
16539      * @property startPageY
16540      * @type int
16541      * @private
16542      */
16543     startPageY: 0,
16544
16545     /**
16546      * The group defines a logical collection of DragDrop objects that are
16547      * related.  Instances only get events when interacting with other
16548      * DragDrop object in the same group.  This lets us define multiple
16549      * groups using a single DragDrop subclass if we want.
16550      * @property groups
16551      * @type {string: string}
16552      */
16553     groups: null,
16554
16555     /**
16556      * Individual drag/drop instances can be locked.  This will prevent
16557      * onmousedown start drag.
16558      * @property locked
16559      * @type boolean
16560      * @private
16561      */
16562     locked: false,
16563
16564     /**
16565      * Lock this instance
16566      * @method lock
16567      */
16568     lock: function() { this.locked = true; },
16569
16570     /**
16571      * Unlock this instace
16572      * @method unlock
16573      */
16574     unlock: function() { this.locked = false; },
16575
16576     /**
16577      * By default, all insances can be a drop target.  This can be disabled by
16578      * setting isTarget to false.
16579      * @method isTarget
16580      * @type boolean
16581      */
16582     isTarget: true,
16583
16584     /**
16585      * The padding configured for this drag and drop object for calculating
16586      * the drop zone intersection with this object.
16587      * @method padding
16588      * @type int[]
16589      */
16590     padding: null,
16591
16592     /**
16593      * Cached reference to the linked element
16594      * @property _domRef
16595      * @private
16596      */
16597     _domRef: null,
16598
16599     /**
16600      * Internal typeof flag
16601      * @property __ygDragDrop
16602      * @private
16603      */
16604     __ygDragDrop: true,
16605
16606     /**
16607      * Set to true when horizontal contraints are applied
16608      * @property constrainX
16609      * @type boolean
16610      * @private
16611      */
16612     constrainX: false,
16613
16614     /**
16615      * Set to true when vertical contraints are applied
16616      * @property constrainY
16617      * @type boolean
16618      * @private
16619      */
16620     constrainY: false,
16621
16622     /**
16623      * The left constraint
16624      * @property minX
16625      * @type int
16626      * @private
16627      */
16628     minX: 0,
16629
16630     /**
16631      * The right constraint
16632      * @property maxX
16633      * @type int
16634      * @private
16635      */
16636     maxX: 0,
16637
16638     /**
16639      * The up constraint
16640      * @property minY
16641      * @type int
16642      * @type int
16643      * @private
16644      */
16645     minY: 0,
16646
16647     /**
16648      * The down constraint
16649      * @property maxY
16650      * @type int
16651      * @private
16652      */
16653     maxY: 0,
16654
16655     /**
16656      * Maintain offsets when we resetconstraints.  Set to true when you want
16657      * the position of the element relative to its parent to stay the same
16658      * when the page changes
16659      *
16660      * @property maintainOffset
16661      * @type boolean
16662      */
16663     maintainOffset: false,
16664
16665     /**
16666      * Array of pixel locations the element will snap to if we specified a
16667      * horizontal graduation/interval.  This array is generated automatically
16668      * when you define a tick interval.
16669      * @property xTicks
16670      * @type int[]
16671      */
16672     xTicks: null,
16673
16674     /**
16675      * Array of pixel locations the element will snap to if we specified a
16676      * vertical graduation/interval.  This array is generated automatically
16677      * when you define a tick interval.
16678      * @property yTicks
16679      * @type int[]
16680      */
16681     yTicks: null,
16682
16683     /**
16684      * By default the drag and drop instance will only respond to the primary
16685      * button click (left button for a right-handed mouse).  Set to true to
16686      * allow drag and drop to start with any mouse click that is propogated
16687      * by the browser
16688      * @property primaryButtonOnly
16689      * @type boolean
16690      */
16691     primaryButtonOnly: true,
16692
16693     /**
16694      * The availabe property is false until the linked dom element is accessible.
16695      * @property available
16696      * @type boolean
16697      */
16698     available: false,
16699
16700     /**
16701      * By default, drags can only be initiated if the mousedown occurs in the
16702      * region the linked element is.  This is done in part to work around a
16703      * bug in some browsers that mis-report the mousedown if the previous
16704      * mouseup happened outside of the window.  This property is set to true
16705      * if outer handles are defined.
16706      *
16707      * @property hasOuterHandles
16708      * @type boolean
16709      * @default false
16710      */
16711     hasOuterHandles: false,
16712
16713     /**
16714      * Code that executes immediately before the startDrag event
16715      * @method b4StartDrag
16716      * @private
16717      */
16718     b4StartDrag: function(x, y) { },
16719
16720     /**
16721      * Abstract method called after a drag/drop object is clicked
16722      * and the drag or mousedown time thresholds have beeen met.
16723      * @method startDrag
16724      * @param {int} X click location
16725      * @param {int} Y click location
16726      */
16727     startDrag: function(x, y) { /* override this */ },
16728
16729     /**
16730      * Code that executes immediately before the onDrag event
16731      * @method b4Drag
16732      * @private
16733      */
16734     b4Drag: function(e) { },
16735
16736     /**
16737      * Abstract method called during the onMouseMove event while dragging an
16738      * object.
16739      * @method onDrag
16740      * @param {Event} e the mousemove event
16741      */
16742     onDrag: function(e) { /* override this */ },
16743
16744     /**
16745      * Abstract method called when this element fist begins hovering over
16746      * another DragDrop obj
16747      * @method onDragEnter
16748      * @param {Event} e the mousemove event
16749      * @param {String|DragDrop[]} id In POINT mode, the element
16750      * id this is hovering over.  In INTERSECT mode, an array of one or more
16751      * dragdrop items being hovered over.
16752      */
16753     onDragEnter: function(e, id) { /* override this */ },
16754
16755     /**
16756      * Code that executes immediately before the onDragOver event
16757      * @method b4DragOver
16758      * @private
16759      */
16760     b4DragOver: function(e) { },
16761
16762     /**
16763      * Abstract method called when this element is hovering over another
16764      * DragDrop obj
16765      * @method onDragOver
16766      * @param {Event} e the mousemove event
16767      * @param {String|DragDrop[]} id In POINT mode, the element
16768      * id this is hovering over.  In INTERSECT mode, an array of dd items
16769      * being hovered over.
16770      */
16771     onDragOver: function(e, id) { /* override this */ },
16772
16773     /**
16774      * Code that executes immediately before the onDragOut event
16775      * @method b4DragOut
16776      * @private
16777      */
16778     b4DragOut: function(e) { },
16779
16780     /**
16781      * Abstract method called when we are no longer hovering over an element
16782      * @method onDragOut
16783      * @param {Event} e the mousemove event
16784      * @param {String|DragDrop[]} id In POINT mode, the element
16785      * id this was hovering over.  In INTERSECT mode, an array of dd items
16786      * that the mouse is no longer over.
16787      */
16788     onDragOut: function(e, id) { /* override this */ },
16789
16790     /**
16791      * Code that executes immediately before the onDragDrop event
16792      * @method b4DragDrop
16793      * @private
16794      */
16795     b4DragDrop: function(e) { },
16796
16797     /**
16798      * Abstract method called when this item is dropped on another DragDrop
16799      * obj
16800      * @method onDragDrop
16801      * @param {Event} e the mouseup event
16802      * @param {String|DragDrop[]} id In POINT mode, the element
16803      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16804      * was dropped on.
16805      */
16806     onDragDrop: function(e, id) { /* override this */ },
16807
16808     /**
16809      * Abstract method called when this item is dropped on an area with no
16810      * drop target
16811      * @method onInvalidDrop
16812      * @param {Event} e the mouseup event
16813      */
16814     onInvalidDrop: function(e) { /* override this */ },
16815
16816     /**
16817      * Code that executes immediately before the endDrag event
16818      * @method b4EndDrag
16819      * @private
16820      */
16821     b4EndDrag: function(e) { },
16822
16823     /**
16824      * Fired when we are done dragging the object
16825      * @method endDrag
16826      * @param {Event} e the mouseup event
16827      */
16828     endDrag: function(e) { /* override this */ },
16829
16830     /**
16831      * Code executed immediately before the onMouseDown event
16832      * @method b4MouseDown
16833      * @param {Event} e the mousedown event
16834      * @private
16835      */
16836     b4MouseDown: function(e) {  },
16837
16838     /**
16839      * Event handler that fires when a drag/drop obj gets a mousedown
16840      * @method onMouseDown
16841      * @param {Event} e the mousedown event
16842      */
16843     onMouseDown: function(e) { /* override this */ },
16844
16845     /**
16846      * Event handler that fires when a drag/drop obj gets a mouseup
16847      * @method onMouseUp
16848      * @param {Event} e the mouseup event
16849      */
16850     onMouseUp: function(e) { /* override this */ },
16851
16852     /**
16853      * Override the onAvailable method to do what is needed after the initial
16854      * position was determined.
16855      * @method onAvailable
16856      */
16857     onAvailable: function () {
16858     },
16859
16860     /*
16861      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16862      * @type Object
16863      */
16864     defaultPadding : {left:0, right:0, top:0, bottom:0},
16865
16866     /*
16867      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16868  *
16869  * Usage:
16870  <pre><code>
16871  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16872                 { dragElId: "existingProxyDiv" });
16873  dd.startDrag = function(){
16874      this.constrainTo("parent-id");
16875  };
16876  </code></pre>
16877  * Or you can initalize it using the {@link Roo.Element} object:
16878  <pre><code>
16879  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16880      startDrag : function(){
16881          this.constrainTo("parent-id");
16882      }
16883  });
16884  </code></pre>
16885      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16886      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16887      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16888      * an object containing the sides to pad. For example: {right:10, bottom:10}
16889      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16890      */
16891     constrainTo : function(constrainTo, pad, inContent){
16892         if(typeof pad == "number"){
16893             pad = {left: pad, right:pad, top:pad, bottom:pad};
16894         }
16895         pad = pad || this.defaultPadding;
16896         var b = Roo.get(this.getEl()).getBox();
16897         var ce = Roo.get(constrainTo);
16898         var s = ce.getScroll();
16899         var c, cd = ce.dom;
16900         if(cd == document.body){
16901             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16902         }else{
16903             xy = ce.getXY();
16904             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16905         }
16906
16907
16908         var topSpace = b.y - c.y;
16909         var leftSpace = b.x - c.x;
16910
16911         this.resetConstraints();
16912         this.setXConstraint(leftSpace - (pad.left||0), // left
16913                 c.width - leftSpace - b.width - (pad.right||0) //right
16914         );
16915         this.setYConstraint(topSpace - (pad.top||0), //top
16916                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16917         );
16918     },
16919
16920     /**
16921      * Returns a reference to the linked element
16922      * @method getEl
16923      * @return {HTMLElement} the html element
16924      */
16925     getEl: function() {
16926         if (!this._domRef) {
16927             this._domRef = Roo.getDom(this.id);
16928         }
16929
16930         return this._domRef;
16931     },
16932
16933     /**
16934      * Returns a reference to the actual element to drag.  By default this is
16935      * the same as the html element, but it can be assigned to another
16936      * element. An example of this can be found in Roo.dd.DDProxy
16937      * @method getDragEl
16938      * @return {HTMLElement} the html element
16939      */
16940     getDragEl: function() {
16941         return Roo.getDom(this.dragElId);
16942     },
16943
16944     /**
16945      * Sets up the DragDrop object.  Must be called in the constructor of any
16946      * Roo.dd.DragDrop subclass
16947      * @method init
16948      * @param id the id of the linked element
16949      * @param {String} sGroup the group of related items
16950      * @param {object} config configuration attributes
16951      */
16952     init: function(id, sGroup, config) {
16953         this.initTarget(id, sGroup, config);
16954         if (!Roo.isTouch) {
16955             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16956         }
16957         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16958         // Event.on(this.id, "selectstart", Event.preventDefault);
16959     },
16960
16961     /**
16962      * Initializes Targeting functionality only... the object does not
16963      * get a mousedown handler.
16964      * @method initTarget
16965      * @param id the id of the linked element
16966      * @param {String} sGroup the group of related items
16967      * @param {object} config configuration attributes
16968      */
16969     initTarget: function(id, sGroup, config) {
16970
16971         // configuration attributes
16972         this.config = config || {};
16973
16974         // create a local reference to the drag and drop manager
16975         this.DDM = Roo.dd.DDM;
16976         // initialize the groups array
16977         this.groups = {};
16978
16979         // assume that we have an element reference instead of an id if the
16980         // parameter is not a string
16981         if (typeof id !== "string") {
16982             id = Roo.id(id);
16983         }
16984
16985         // set the id
16986         this.id = id;
16987
16988         // add to an interaction group
16989         this.addToGroup((sGroup) ? sGroup : "default");
16990
16991         // We don't want to register this as the handle with the manager
16992         // so we just set the id rather than calling the setter.
16993         this.handleElId = id;
16994
16995         // the linked element is the element that gets dragged by default
16996         this.setDragElId(id);
16997
16998         // by default, clicked anchors will not start drag operations.
16999         this.invalidHandleTypes = { A: "A" };
17000         this.invalidHandleIds = {};
17001         this.invalidHandleClasses = [];
17002
17003         this.applyConfig();
17004
17005         this.handleOnAvailable();
17006     },
17007
17008     /**
17009      * Applies the configuration parameters that were passed into the constructor.
17010      * This is supposed to happen at each level through the inheritance chain.  So
17011      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17012      * DragDrop in order to get all of the parameters that are available in
17013      * each object.
17014      * @method applyConfig
17015      */
17016     applyConfig: function() {
17017
17018         // configurable properties:
17019         //    padding, isTarget, maintainOffset, primaryButtonOnly
17020         this.padding           = this.config.padding || [0, 0, 0, 0];
17021         this.isTarget          = (this.config.isTarget !== false);
17022         this.maintainOffset    = (this.config.maintainOffset);
17023         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17024
17025     },
17026
17027     /**
17028      * Executed when the linked element is available
17029      * @method handleOnAvailable
17030      * @private
17031      */
17032     handleOnAvailable: function() {
17033         this.available = true;
17034         this.resetConstraints();
17035         this.onAvailable();
17036     },
17037
17038      /**
17039      * Configures the padding for the target zone in px.  Effectively expands
17040      * (or reduces) the virtual object size for targeting calculations.
17041      * Supports css-style shorthand; if only one parameter is passed, all sides
17042      * will have that padding, and if only two are passed, the top and bottom
17043      * will have the first param, the left and right the second.
17044      * @method setPadding
17045      * @param {int} iTop    Top pad
17046      * @param {int} iRight  Right pad
17047      * @param {int} iBot    Bot pad
17048      * @param {int} iLeft   Left pad
17049      */
17050     setPadding: function(iTop, iRight, iBot, iLeft) {
17051         // this.padding = [iLeft, iRight, iTop, iBot];
17052         if (!iRight && 0 !== iRight) {
17053             this.padding = [iTop, iTop, iTop, iTop];
17054         } else if (!iBot && 0 !== iBot) {
17055             this.padding = [iTop, iRight, iTop, iRight];
17056         } else {
17057             this.padding = [iTop, iRight, iBot, iLeft];
17058         }
17059     },
17060
17061     /**
17062      * Stores the initial placement of the linked element.
17063      * @method setInitialPosition
17064      * @param {int} diffX   the X offset, default 0
17065      * @param {int} diffY   the Y offset, default 0
17066      */
17067     setInitPosition: function(diffX, diffY) {
17068         var el = this.getEl();
17069
17070         if (!this.DDM.verifyEl(el)) {
17071             return;
17072         }
17073
17074         var dx = diffX || 0;
17075         var dy = diffY || 0;
17076
17077         var p = Dom.getXY( el );
17078
17079         this.initPageX = p[0] - dx;
17080         this.initPageY = p[1] - dy;
17081
17082         this.lastPageX = p[0];
17083         this.lastPageY = p[1];
17084
17085
17086         this.setStartPosition(p);
17087     },
17088
17089     /**
17090      * Sets the start position of the element.  This is set when the obj
17091      * is initialized, the reset when a drag is started.
17092      * @method setStartPosition
17093      * @param pos current position (from previous lookup)
17094      * @private
17095      */
17096     setStartPosition: function(pos) {
17097         var p = pos || Dom.getXY( this.getEl() );
17098         this.deltaSetXY = null;
17099
17100         this.startPageX = p[0];
17101         this.startPageY = p[1];
17102     },
17103
17104     /**
17105      * Add this instance to a group of related drag/drop objects.  All
17106      * instances belong to at least one group, and can belong to as many
17107      * groups as needed.
17108      * @method addToGroup
17109      * @param sGroup {string} the name of the group
17110      */
17111     addToGroup: function(sGroup) {
17112         this.groups[sGroup] = true;
17113         this.DDM.regDragDrop(this, sGroup);
17114     },
17115
17116     /**
17117      * Remove's this instance from the supplied interaction group
17118      * @method removeFromGroup
17119      * @param {string}  sGroup  The group to drop
17120      */
17121     removeFromGroup: function(sGroup) {
17122         if (this.groups[sGroup]) {
17123             delete this.groups[sGroup];
17124         }
17125
17126         this.DDM.removeDDFromGroup(this, sGroup);
17127     },
17128
17129     /**
17130      * Allows you to specify that an element other than the linked element
17131      * will be moved with the cursor during a drag
17132      * @method setDragElId
17133      * @param id {string} the id of the element that will be used to initiate the drag
17134      */
17135     setDragElId: function(id) {
17136         this.dragElId = id;
17137     },
17138
17139     /**
17140      * Allows you to specify a child of the linked element that should be
17141      * used to initiate the drag operation.  An example of this would be if
17142      * you have a content div with text and links.  Clicking anywhere in the
17143      * content area would normally start the drag operation.  Use this method
17144      * to specify that an element inside of the content div is the element
17145      * that starts the drag operation.
17146      * @method setHandleElId
17147      * @param id {string} the id of the element that will be used to
17148      * initiate the drag.
17149      */
17150     setHandleElId: function(id) {
17151         if (typeof id !== "string") {
17152             id = Roo.id(id);
17153         }
17154         this.handleElId = id;
17155         this.DDM.regHandle(this.id, id);
17156     },
17157
17158     /**
17159      * Allows you to set an element outside of the linked element as a drag
17160      * handle
17161      * @method setOuterHandleElId
17162      * @param id the id of the element that will be used to initiate the drag
17163      */
17164     setOuterHandleElId: function(id) {
17165         if (typeof id !== "string") {
17166             id = Roo.id(id);
17167         }
17168         Event.on(id, "mousedown",
17169                 this.handleMouseDown, this);
17170         this.setHandleElId(id);
17171
17172         this.hasOuterHandles = true;
17173     },
17174
17175     /**
17176      * Remove all drag and drop hooks for this element
17177      * @method unreg
17178      */
17179     unreg: function() {
17180         Event.un(this.id, "mousedown",
17181                 this.handleMouseDown);
17182         Event.un(this.id, "touchstart",
17183                 this.handleMouseDown);
17184         this._domRef = null;
17185         this.DDM._remove(this);
17186     },
17187
17188     destroy : function(){
17189         this.unreg();
17190     },
17191
17192     /**
17193      * Returns true if this instance is locked, or the drag drop mgr is locked
17194      * (meaning that all drag/drop is disabled on the page.)
17195      * @method isLocked
17196      * @return {boolean} true if this obj or all drag/drop is locked, else
17197      * false
17198      */
17199     isLocked: function() {
17200         return (this.DDM.isLocked() || this.locked);
17201     },
17202
17203     /**
17204      * Fired when this object is clicked
17205      * @method handleMouseDown
17206      * @param {Event} e
17207      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17208      * @private
17209      */
17210     handleMouseDown: function(e, oDD){
17211      
17212         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17213             //Roo.log('not touch/ button !=0');
17214             return;
17215         }
17216         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17217             return; // double touch..
17218         }
17219         
17220
17221         if (this.isLocked()) {
17222             //Roo.log('locked');
17223             return;
17224         }
17225
17226         this.DDM.refreshCache(this.groups);
17227 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17228         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17229         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17230             //Roo.log('no outer handes or not over target');
17231                 // do nothing.
17232         } else {
17233 //            Roo.log('check validator');
17234             if (this.clickValidator(e)) {
17235 //                Roo.log('validate success');
17236                 // set the initial element position
17237                 this.setStartPosition();
17238
17239
17240                 this.b4MouseDown(e);
17241                 this.onMouseDown(e);
17242
17243                 this.DDM.handleMouseDown(e, this);
17244
17245                 this.DDM.stopEvent(e);
17246             } else {
17247
17248
17249             }
17250         }
17251     },
17252
17253     clickValidator: function(e) {
17254         var target = e.getTarget();
17255         return ( this.isValidHandleChild(target) &&
17256                     (this.id == this.handleElId ||
17257                         this.DDM.handleWasClicked(target, this.id)) );
17258     },
17259
17260     /**
17261      * Allows you to specify a tag name that should not start a drag operation
17262      * when clicked.  This is designed to facilitate embedding links within a
17263      * drag handle that do something other than start the drag.
17264      * @method addInvalidHandleType
17265      * @param {string} tagName the type of element to exclude
17266      */
17267     addInvalidHandleType: function(tagName) {
17268         var type = tagName.toUpperCase();
17269         this.invalidHandleTypes[type] = type;
17270     },
17271
17272     /**
17273      * Lets you to specify an element id for a child of a drag handle
17274      * that should not initiate a drag
17275      * @method addInvalidHandleId
17276      * @param {string} id the element id of the element you wish to ignore
17277      */
17278     addInvalidHandleId: function(id) {
17279         if (typeof id !== "string") {
17280             id = Roo.id(id);
17281         }
17282         this.invalidHandleIds[id] = id;
17283     },
17284
17285     /**
17286      * Lets you specify a css class of elements that will not initiate a drag
17287      * @method addInvalidHandleClass
17288      * @param {string} cssClass the class of the elements you wish to ignore
17289      */
17290     addInvalidHandleClass: function(cssClass) {
17291         this.invalidHandleClasses.push(cssClass);
17292     },
17293
17294     /**
17295      * Unsets an excluded tag name set by addInvalidHandleType
17296      * @method removeInvalidHandleType
17297      * @param {string} tagName the type of element to unexclude
17298      */
17299     removeInvalidHandleType: function(tagName) {
17300         var type = tagName.toUpperCase();
17301         // this.invalidHandleTypes[type] = null;
17302         delete this.invalidHandleTypes[type];
17303     },
17304
17305     /**
17306      * Unsets an invalid handle id
17307      * @method removeInvalidHandleId
17308      * @param {string} id the id of the element to re-enable
17309      */
17310     removeInvalidHandleId: function(id) {
17311         if (typeof id !== "string") {
17312             id = Roo.id(id);
17313         }
17314         delete this.invalidHandleIds[id];
17315     },
17316
17317     /**
17318      * Unsets an invalid css class
17319      * @method removeInvalidHandleClass
17320      * @param {string} cssClass the class of the element(s) you wish to
17321      * re-enable
17322      */
17323     removeInvalidHandleClass: function(cssClass) {
17324         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17325             if (this.invalidHandleClasses[i] == cssClass) {
17326                 delete this.invalidHandleClasses[i];
17327             }
17328         }
17329     },
17330
17331     /**
17332      * Checks the tag exclusion list to see if this click should be ignored
17333      * @method isValidHandleChild
17334      * @param {HTMLElement} node the HTMLElement to evaluate
17335      * @return {boolean} true if this is a valid tag type, false if not
17336      */
17337     isValidHandleChild: function(node) {
17338
17339         var valid = true;
17340         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17341         var nodeName;
17342         try {
17343             nodeName = node.nodeName.toUpperCase();
17344         } catch(e) {
17345             nodeName = node.nodeName;
17346         }
17347         valid = valid && !this.invalidHandleTypes[nodeName];
17348         valid = valid && !this.invalidHandleIds[node.id];
17349
17350         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17351             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17352         }
17353
17354
17355         return valid;
17356
17357     },
17358
17359     /**
17360      * Create the array of horizontal tick marks if an interval was specified
17361      * in setXConstraint().
17362      * @method setXTicks
17363      * @private
17364      */
17365     setXTicks: function(iStartX, iTickSize) {
17366         this.xTicks = [];
17367         this.xTickSize = iTickSize;
17368
17369         var tickMap = {};
17370
17371         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17372             if (!tickMap[i]) {
17373                 this.xTicks[this.xTicks.length] = i;
17374                 tickMap[i] = true;
17375             }
17376         }
17377
17378         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17379             if (!tickMap[i]) {
17380                 this.xTicks[this.xTicks.length] = i;
17381                 tickMap[i] = true;
17382             }
17383         }
17384
17385         this.xTicks.sort(this.DDM.numericSort) ;
17386     },
17387
17388     /**
17389      * Create the array of vertical tick marks if an interval was specified in
17390      * setYConstraint().
17391      * @method setYTicks
17392      * @private
17393      */
17394     setYTicks: function(iStartY, iTickSize) {
17395         this.yTicks = [];
17396         this.yTickSize = iTickSize;
17397
17398         var tickMap = {};
17399
17400         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17401             if (!tickMap[i]) {
17402                 this.yTicks[this.yTicks.length] = i;
17403                 tickMap[i] = true;
17404             }
17405         }
17406
17407         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17408             if (!tickMap[i]) {
17409                 this.yTicks[this.yTicks.length] = i;
17410                 tickMap[i] = true;
17411             }
17412         }
17413
17414         this.yTicks.sort(this.DDM.numericSort) ;
17415     },
17416
17417     /**
17418      * By default, the element can be dragged any place on the screen.  Use
17419      * this method to limit the horizontal travel of the element.  Pass in
17420      * 0,0 for the parameters if you want to lock the drag to the y axis.
17421      * @method setXConstraint
17422      * @param {int} iLeft the number of pixels the element can move to the left
17423      * @param {int} iRight the number of pixels the element can move to the
17424      * right
17425      * @param {int} iTickSize optional parameter for specifying that the
17426      * element
17427      * should move iTickSize pixels at a time.
17428      */
17429     setXConstraint: function(iLeft, iRight, iTickSize) {
17430         this.leftConstraint = iLeft;
17431         this.rightConstraint = iRight;
17432
17433         this.minX = this.initPageX - iLeft;
17434         this.maxX = this.initPageX + iRight;
17435         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17436
17437         this.constrainX = true;
17438     },
17439
17440     /**
17441      * Clears any constraints applied to this instance.  Also clears ticks
17442      * since they can't exist independent of a constraint at this time.
17443      * @method clearConstraints
17444      */
17445     clearConstraints: function() {
17446         this.constrainX = false;
17447         this.constrainY = false;
17448         this.clearTicks();
17449     },
17450
17451     /**
17452      * Clears any tick interval defined for this instance
17453      * @method clearTicks
17454      */
17455     clearTicks: function() {
17456         this.xTicks = null;
17457         this.yTicks = null;
17458         this.xTickSize = 0;
17459         this.yTickSize = 0;
17460     },
17461
17462     /**
17463      * By default, the element can be dragged any place on the screen.  Set
17464      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17465      * parameters if you want to lock the drag to the x axis.
17466      * @method setYConstraint
17467      * @param {int} iUp the number of pixels the element can move up
17468      * @param {int} iDown the number of pixels the element can move down
17469      * @param {int} iTickSize optional parameter for specifying that the
17470      * element should move iTickSize pixels at a time.
17471      */
17472     setYConstraint: function(iUp, iDown, iTickSize) {
17473         this.topConstraint = iUp;
17474         this.bottomConstraint = iDown;
17475
17476         this.minY = this.initPageY - iUp;
17477         this.maxY = this.initPageY + iDown;
17478         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17479
17480         this.constrainY = true;
17481
17482     },
17483
17484     /**
17485      * resetConstraints must be called if you manually reposition a dd element.
17486      * @method resetConstraints
17487      * @param {boolean} maintainOffset
17488      */
17489     resetConstraints: function() {
17490
17491
17492         // Maintain offsets if necessary
17493         if (this.initPageX || this.initPageX === 0) {
17494             // figure out how much this thing has moved
17495             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17496             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17497
17498             this.setInitPosition(dx, dy);
17499
17500         // This is the first time we have detected the element's position
17501         } else {
17502             this.setInitPosition();
17503         }
17504
17505         if (this.constrainX) {
17506             this.setXConstraint( this.leftConstraint,
17507                                  this.rightConstraint,
17508                                  this.xTickSize        );
17509         }
17510
17511         if (this.constrainY) {
17512             this.setYConstraint( this.topConstraint,
17513                                  this.bottomConstraint,
17514                                  this.yTickSize         );
17515         }
17516     },
17517
17518     /**
17519      * Normally the drag element is moved pixel by pixel, but we can specify
17520      * that it move a number of pixels at a time.  This method resolves the
17521      * location when we have it set up like this.
17522      * @method getTick
17523      * @param {int} val where we want to place the object
17524      * @param {int[]} tickArray sorted array of valid points
17525      * @return {int} the closest tick
17526      * @private
17527      */
17528     getTick: function(val, tickArray) {
17529
17530         if (!tickArray) {
17531             // If tick interval is not defined, it is effectively 1 pixel,
17532             // so we return the value passed to us.
17533             return val;
17534         } else if (tickArray[0] >= val) {
17535             // The value is lower than the first tick, so we return the first
17536             // tick.
17537             return tickArray[0];
17538         } else {
17539             for (var i=0, len=tickArray.length; i<len; ++i) {
17540                 var next = i + 1;
17541                 if (tickArray[next] && tickArray[next] >= val) {
17542                     var diff1 = val - tickArray[i];
17543                     var diff2 = tickArray[next] - val;
17544                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17545                 }
17546             }
17547
17548             // The value is larger than the last tick, so we return the last
17549             // tick.
17550             return tickArray[tickArray.length - 1];
17551         }
17552     },
17553
17554     /**
17555      * toString method
17556      * @method toString
17557      * @return {string} string representation of the dd obj
17558      */
17559     toString: function() {
17560         return ("DragDrop " + this.id);
17561     }
17562
17563 });
17564
17565 })();
17566 /*
17567  * Based on:
17568  * Ext JS Library 1.1.1
17569  * Copyright(c) 2006-2007, Ext JS, LLC.
17570  *
17571  * Originally Released Under LGPL - original licence link has changed is not relivant.
17572  *
17573  * Fork - LGPL
17574  * <script type="text/javascript">
17575  */
17576
17577
17578 /**
17579  * The drag and drop utility provides a framework for building drag and drop
17580  * applications.  In addition to enabling drag and drop for specific elements,
17581  * the drag and drop elements are tracked by the manager class, and the
17582  * interactions between the various elements are tracked during the drag and
17583  * the implementing code is notified about these important moments.
17584  */
17585
17586 // Only load the library once.  Rewriting the manager class would orphan
17587 // existing drag and drop instances.
17588 if (!Roo.dd.DragDropMgr) {
17589
17590 /**
17591  * @class Roo.dd.DragDropMgr
17592  * DragDropMgr is a singleton that tracks the element interaction for
17593  * all DragDrop items in the window.  Generally, you will not call
17594  * this class directly, but it does have helper methods that could
17595  * be useful in your DragDrop implementations.
17596  * @singleton
17597  */
17598 Roo.dd.DragDropMgr = function() {
17599
17600     var Event = Roo.EventManager;
17601
17602     return {
17603
17604         /**
17605          * Two dimensional Array of registered DragDrop objects.  The first
17606          * dimension is the DragDrop item group, the second the DragDrop
17607          * object.
17608          * @property ids
17609          * @type {string: string}
17610          * @private
17611          * @static
17612          */
17613         ids: {},
17614
17615         /**
17616          * Array of element ids defined as drag handles.  Used to determine
17617          * if the element that generated the mousedown event is actually the
17618          * handle and not the html element itself.
17619          * @property handleIds
17620          * @type {string: string}
17621          * @private
17622          * @static
17623          */
17624         handleIds: {},
17625
17626         /**
17627          * the DragDrop object that is currently being dragged
17628          * @property dragCurrent
17629          * @type DragDrop
17630          * @private
17631          * @static
17632          **/
17633         dragCurrent: null,
17634
17635         /**
17636          * the DragDrop object(s) that are being hovered over
17637          * @property dragOvers
17638          * @type Array
17639          * @private
17640          * @static
17641          */
17642         dragOvers: {},
17643
17644         /**
17645          * the X distance between the cursor and the object being dragged
17646          * @property deltaX
17647          * @type int
17648          * @private
17649          * @static
17650          */
17651         deltaX: 0,
17652
17653         /**
17654          * the Y distance between the cursor and the object being dragged
17655          * @property deltaY
17656          * @type int
17657          * @private
17658          * @static
17659          */
17660         deltaY: 0,
17661
17662         /**
17663          * Flag to determine if we should prevent the default behavior of the
17664          * events we define. By default this is true, but this can be set to
17665          * false if you need the default behavior (not recommended)
17666          * @property preventDefault
17667          * @type boolean
17668          * @static
17669          */
17670         preventDefault: true,
17671
17672         /**
17673          * Flag to determine if we should stop the propagation of the events
17674          * we generate. This is true by default but you may want to set it to
17675          * false if the html element contains other features that require the
17676          * mouse click.
17677          * @property stopPropagation
17678          * @type boolean
17679          * @static
17680          */
17681         stopPropagation: true,
17682
17683         /**
17684          * Internal flag that is set to true when drag and drop has been
17685          * intialized
17686          * @property initialized
17687          * @private
17688          * @static
17689          */
17690         initalized: false,
17691
17692         /**
17693          * All drag and drop can be disabled.
17694          * @property locked
17695          * @private
17696          * @static
17697          */
17698         locked: false,
17699
17700         /**
17701          * Called the first time an element is registered.
17702          * @method init
17703          * @private
17704          * @static
17705          */
17706         init: function() {
17707             this.initialized = true;
17708         },
17709
17710         /**
17711          * In point mode, drag and drop interaction is defined by the
17712          * location of the cursor during the drag/drop
17713          * @property POINT
17714          * @type int
17715          * @static
17716          */
17717         POINT: 0,
17718
17719         /**
17720          * In intersect mode, drag and drop interactio nis defined by the
17721          * overlap of two or more drag and drop objects.
17722          * @property INTERSECT
17723          * @type int
17724          * @static
17725          */
17726         INTERSECT: 1,
17727
17728         /**
17729          * The current drag and drop mode.  Default: POINT
17730          * @property mode
17731          * @type int
17732          * @static
17733          */
17734         mode: 0,
17735
17736         /**
17737          * Runs method on all drag and drop objects
17738          * @method _execOnAll
17739          * @private
17740          * @static
17741          */
17742         _execOnAll: function(sMethod, args) {
17743             for (var i in this.ids) {
17744                 for (var j in this.ids[i]) {
17745                     var oDD = this.ids[i][j];
17746                     if (! this.isTypeOfDD(oDD)) {
17747                         continue;
17748                     }
17749                     oDD[sMethod].apply(oDD, args);
17750                 }
17751             }
17752         },
17753
17754         /**
17755          * Drag and drop initialization.  Sets up the global event handlers
17756          * @method _onLoad
17757          * @private
17758          * @static
17759          */
17760         _onLoad: function() {
17761
17762             this.init();
17763
17764             if (!Roo.isTouch) {
17765                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17766                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17767             }
17768             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17769             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17770             
17771             Event.on(window,   "unload",    this._onUnload, this, true);
17772             Event.on(window,   "resize",    this._onResize, this, true);
17773             // Event.on(window,   "mouseout",    this._test);
17774
17775         },
17776
17777         /**
17778          * Reset constraints on all drag and drop objs
17779          * @method _onResize
17780          * @private
17781          * @static
17782          */
17783         _onResize: function(e) {
17784             this._execOnAll("resetConstraints", []);
17785         },
17786
17787         /**
17788          * Lock all drag and drop functionality
17789          * @method lock
17790          * @static
17791          */
17792         lock: function() { this.locked = true; },
17793
17794         /**
17795          * Unlock all drag and drop functionality
17796          * @method unlock
17797          * @static
17798          */
17799         unlock: function() { this.locked = false; },
17800
17801         /**
17802          * Is drag and drop locked?
17803          * @method isLocked
17804          * @return {boolean} True if drag and drop is locked, false otherwise.
17805          * @static
17806          */
17807         isLocked: function() { return this.locked; },
17808
17809         /**
17810          * Location cache that is set for all drag drop objects when a drag is
17811          * initiated, cleared when the drag is finished.
17812          * @property locationCache
17813          * @private
17814          * @static
17815          */
17816         locationCache: {},
17817
17818         /**
17819          * Set useCache to false if you want to force object the lookup of each
17820          * drag and drop linked element constantly during a drag.
17821          * @property useCache
17822          * @type boolean
17823          * @static
17824          */
17825         useCache: true,
17826
17827         /**
17828          * The number of pixels that the mouse needs to move after the
17829          * mousedown before the drag is initiated.  Default=3;
17830          * @property clickPixelThresh
17831          * @type int
17832          * @static
17833          */
17834         clickPixelThresh: 3,
17835
17836         /**
17837          * The number of milliseconds after the mousedown event to initiate the
17838          * drag if we don't get a mouseup event. Default=1000
17839          * @property clickTimeThresh
17840          * @type int
17841          * @static
17842          */
17843         clickTimeThresh: 350,
17844
17845         /**
17846          * Flag that indicates that either the drag pixel threshold or the
17847          * mousdown time threshold has been met
17848          * @property dragThreshMet
17849          * @type boolean
17850          * @private
17851          * @static
17852          */
17853         dragThreshMet: false,
17854
17855         /**
17856          * Timeout used for the click time threshold
17857          * @property clickTimeout
17858          * @type Object
17859          * @private
17860          * @static
17861          */
17862         clickTimeout: null,
17863
17864         /**
17865          * The X position of the mousedown event stored for later use when a
17866          * drag threshold is met.
17867          * @property startX
17868          * @type int
17869          * @private
17870          * @static
17871          */
17872         startX: 0,
17873
17874         /**
17875          * The Y position of the mousedown event stored for later use when a
17876          * drag threshold is met.
17877          * @property startY
17878          * @type int
17879          * @private
17880          * @static
17881          */
17882         startY: 0,
17883
17884         /**
17885          * Each DragDrop instance must be registered with the DragDropMgr.
17886          * This is executed in DragDrop.init()
17887          * @method regDragDrop
17888          * @param {DragDrop} oDD the DragDrop object to register
17889          * @param {String} sGroup the name of the group this element belongs to
17890          * @static
17891          */
17892         regDragDrop: function(oDD, sGroup) {
17893             if (!this.initialized) { this.init(); }
17894
17895             if (!this.ids[sGroup]) {
17896                 this.ids[sGroup] = {};
17897             }
17898             this.ids[sGroup][oDD.id] = oDD;
17899         },
17900
17901         /**
17902          * Removes the supplied dd instance from the supplied group. Executed
17903          * by DragDrop.removeFromGroup, so don't call this function directly.
17904          * @method removeDDFromGroup
17905          * @private
17906          * @static
17907          */
17908         removeDDFromGroup: function(oDD, sGroup) {
17909             if (!this.ids[sGroup]) {
17910                 this.ids[sGroup] = {};
17911             }
17912
17913             var obj = this.ids[sGroup];
17914             if (obj && obj[oDD.id]) {
17915                 delete obj[oDD.id];
17916             }
17917         },
17918
17919         /**
17920          * Unregisters a drag and drop item.  This is executed in
17921          * DragDrop.unreg, use that method instead of calling this directly.
17922          * @method _remove
17923          * @private
17924          * @static
17925          */
17926         _remove: function(oDD) {
17927             for (var g in oDD.groups) {
17928                 if (g && this.ids[g][oDD.id]) {
17929                     delete this.ids[g][oDD.id];
17930                 }
17931             }
17932             delete this.handleIds[oDD.id];
17933         },
17934
17935         /**
17936          * Each DragDrop handle element must be registered.  This is done
17937          * automatically when executing DragDrop.setHandleElId()
17938          * @method regHandle
17939          * @param {String} sDDId the DragDrop id this element is a handle for
17940          * @param {String} sHandleId the id of the element that is the drag
17941          * handle
17942          * @static
17943          */
17944         regHandle: function(sDDId, sHandleId) {
17945             if (!this.handleIds[sDDId]) {
17946                 this.handleIds[sDDId] = {};
17947             }
17948             this.handleIds[sDDId][sHandleId] = sHandleId;
17949         },
17950
17951         /**
17952          * Utility function to determine if a given element has been
17953          * registered as a drag drop item.
17954          * @method isDragDrop
17955          * @param {String} id the element id to check
17956          * @return {boolean} true if this element is a DragDrop item,
17957          * false otherwise
17958          * @static
17959          */
17960         isDragDrop: function(id) {
17961             return ( this.getDDById(id) ) ? true : false;
17962         },
17963
17964         /**
17965          * Returns the drag and drop instances that are in all groups the
17966          * passed in instance belongs to.
17967          * @method getRelated
17968          * @param {DragDrop} p_oDD the obj to get related data for
17969          * @param {boolean} bTargetsOnly if true, only return targetable objs
17970          * @return {DragDrop[]} the related instances
17971          * @static
17972          */
17973         getRelated: function(p_oDD, bTargetsOnly) {
17974             var oDDs = [];
17975             for (var i in p_oDD.groups) {
17976                 for (j in this.ids[i]) {
17977                     var dd = this.ids[i][j];
17978                     if (! this.isTypeOfDD(dd)) {
17979                         continue;
17980                     }
17981                     if (!bTargetsOnly || dd.isTarget) {
17982                         oDDs[oDDs.length] = dd;
17983                     }
17984                 }
17985             }
17986
17987             return oDDs;
17988         },
17989
17990         /**
17991          * Returns true if the specified dd target is a legal target for
17992          * the specifice drag obj
17993          * @method isLegalTarget
17994          * @param {DragDrop} the drag obj
17995          * @param {DragDrop} the target
17996          * @return {boolean} true if the target is a legal target for the
17997          * dd obj
17998          * @static
17999          */
18000         isLegalTarget: function (oDD, oTargetDD) {
18001             var targets = this.getRelated(oDD, true);
18002             for (var i=0, len=targets.length;i<len;++i) {
18003                 if (targets[i].id == oTargetDD.id) {
18004                     return true;
18005                 }
18006             }
18007
18008             return false;
18009         },
18010
18011         /**
18012          * My goal is to be able to transparently determine if an object is
18013          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18014          * returns "object", oDD.constructor.toString() always returns
18015          * "DragDrop" and not the name of the subclass.  So for now it just
18016          * evaluates a well-known variable in DragDrop.
18017          * @method isTypeOfDD
18018          * @param {Object} the object to evaluate
18019          * @return {boolean} true if typeof oDD = DragDrop
18020          * @static
18021          */
18022         isTypeOfDD: function (oDD) {
18023             return (oDD && oDD.__ygDragDrop);
18024         },
18025
18026         /**
18027          * Utility function to determine if a given element has been
18028          * registered as a drag drop handle for the given Drag Drop object.
18029          * @method isHandle
18030          * @param {String} id the element id to check
18031          * @return {boolean} true if this element is a DragDrop handle, false
18032          * otherwise
18033          * @static
18034          */
18035         isHandle: function(sDDId, sHandleId) {
18036             return ( this.handleIds[sDDId] &&
18037                             this.handleIds[sDDId][sHandleId] );
18038         },
18039
18040         /**
18041          * Returns the DragDrop instance for a given id
18042          * @method getDDById
18043          * @param {String} id the id of the DragDrop object
18044          * @return {DragDrop} the drag drop object, null if it is not found
18045          * @static
18046          */
18047         getDDById: function(id) {
18048             for (var i in this.ids) {
18049                 if (this.ids[i][id]) {
18050                     return this.ids[i][id];
18051                 }
18052             }
18053             return null;
18054         },
18055
18056         /**
18057          * Fired after a registered DragDrop object gets the mousedown event.
18058          * Sets up the events required to track the object being dragged
18059          * @method handleMouseDown
18060          * @param {Event} e the event
18061          * @param oDD the DragDrop object being dragged
18062          * @private
18063          * @static
18064          */
18065         handleMouseDown: function(e, oDD) {
18066             if(Roo.QuickTips){
18067                 Roo.QuickTips.disable();
18068             }
18069             this.currentTarget = e.getTarget();
18070
18071             this.dragCurrent = oDD;
18072
18073             var el = oDD.getEl();
18074
18075             // track start position
18076             this.startX = e.getPageX();
18077             this.startY = e.getPageY();
18078
18079             this.deltaX = this.startX - el.offsetLeft;
18080             this.deltaY = this.startY - el.offsetTop;
18081
18082             this.dragThreshMet = false;
18083
18084             this.clickTimeout = setTimeout(
18085                     function() {
18086                         var DDM = Roo.dd.DDM;
18087                         DDM.startDrag(DDM.startX, DDM.startY);
18088                     },
18089                     this.clickTimeThresh );
18090         },
18091
18092         /**
18093          * Fired when either the drag pixel threshol or the mousedown hold
18094          * time threshold has been met.
18095          * @method startDrag
18096          * @param x {int} the X position of the original mousedown
18097          * @param y {int} the Y position of the original mousedown
18098          * @static
18099          */
18100         startDrag: function(x, y) {
18101             clearTimeout(this.clickTimeout);
18102             if (this.dragCurrent) {
18103                 this.dragCurrent.b4StartDrag(x, y);
18104                 this.dragCurrent.startDrag(x, y);
18105             }
18106             this.dragThreshMet = true;
18107         },
18108
18109         /**
18110          * Internal function to handle the mouseup event.  Will be invoked
18111          * from the context of the document.
18112          * @method handleMouseUp
18113          * @param {Event} e the event
18114          * @private
18115          * @static
18116          */
18117         handleMouseUp: function(e) {
18118
18119             if(Roo.QuickTips){
18120                 Roo.QuickTips.enable();
18121             }
18122             if (! this.dragCurrent) {
18123                 return;
18124             }
18125
18126             clearTimeout(this.clickTimeout);
18127
18128             if (this.dragThreshMet) {
18129                 this.fireEvents(e, true);
18130             } else {
18131             }
18132
18133             this.stopDrag(e);
18134
18135             this.stopEvent(e);
18136         },
18137
18138         /**
18139          * Utility to stop event propagation and event default, if these
18140          * features are turned on.
18141          * @method stopEvent
18142          * @param {Event} e the event as returned by this.getEvent()
18143          * @static
18144          */
18145         stopEvent: function(e){
18146             if(this.stopPropagation) {
18147                 e.stopPropagation();
18148             }
18149
18150             if (this.preventDefault) {
18151                 e.preventDefault();
18152             }
18153         },
18154
18155         /**
18156          * Internal function to clean up event handlers after the drag
18157          * operation is complete
18158          * @method stopDrag
18159          * @param {Event} e the event
18160          * @private
18161          * @static
18162          */
18163         stopDrag: function(e) {
18164             // Fire the drag end event for the item that was dragged
18165             if (this.dragCurrent) {
18166                 if (this.dragThreshMet) {
18167                     this.dragCurrent.b4EndDrag(e);
18168                     this.dragCurrent.endDrag(e);
18169                 }
18170
18171                 this.dragCurrent.onMouseUp(e);
18172             }
18173
18174             this.dragCurrent = null;
18175             this.dragOvers = {};
18176         },
18177
18178         /**
18179          * Internal function to handle the mousemove event.  Will be invoked
18180          * from the context of the html element.
18181          *
18182          * @TODO figure out what we can do about mouse events lost when the
18183          * user drags objects beyond the window boundary.  Currently we can
18184          * detect this in internet explorer by verifying that the mouse is
18185          * down during the mousemove event.  Firefox doesn't give us the
18186          * button state on the mousemove event.
18187          * @method handleMouseMove
18188          * @param {Event} e the event
18189          * @private
18190          * @static
18191          */
18192         handleMouseMove: function(e) {
18193             if (! this.dragCurrent) {
18194                 return true;
18195             }
18196
18197             // var button = e.which || e.button;
18198
18199             // check for IE mouseup outside of page boundary
18200             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18201                 this.stopEvent(e);
18202                 return this.handleMouseUp(e);
18203             }
18204
18205             if (!this.dragThreshMet) {
18206                 var diffX = Math.abs(this.startX - e.getPageX());
18207                 var diffY = Math.abs(this.startY - e.getPageY());
18208                 if (diffX > this.clickPixelThresh ||
18209                             diffY > this.clickPixelThresh) {
18210                     this.startDrag(this.startX, this.startY);
18211                 }
18212             }
18213
18214             if (this.dragThreshMet) {
18215                 this.dragCurrent.b4Drag(e);
18216                 this.dragCurrent.onDrag(e);
18217                 if(!this.dragCurrent.moveOnly){
18218                     this.fireEvents(e, false);
18219                 }
18220             }
18221
18222             this.stopEvent(e);
18223
18224             return true;
18225         },
18226
18227         /**
18228          * Iterates over all of the DragDrop elements to find ones we are
18229          * hovering over or dropping on
18230          * @method fireEvents
18231          * @param {Event} e the event
18232          * @param {boolean} isDrop is this a drop op or a mouseover op?
18233          * @private
18234          * @static
18235          */
18236         fireEvents: function(e, isDrop) {
18237             var dc = this.dragCurrent;
18238
18239             // If the user did the mouse up outside of the window, we could
18240             // get here even though we have ended the drag.
18241             if (!dc || dc.isLocked()) {
18242                 return;
18243             }
18244
18245             var pt = e.getPoint();
18246
18247             // cache the previous dragOver array
18248             var oldOvers = [];
18249
18250             var outEvts   = [];
18251             var overEvts  = [];
18252             var dropEvts  = [];
18253             var enterEvts = [];
18254
18255             // Check to see if the object(s) we were hovering over is no longer
18256             // being hovered over so we can fire the onDragOut event
18257             for (var i in this.dragOvers) {
18258
18259                 var ddo = this.dragOvers[i];
18260
18261                 if (! this.isTypeOfDD(ddo)) {
18262                     continue;
18263                 }
18264
18265                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18266                     outEvts.push( ddo );
18267                 }
18268
18269                 oldOvers[i] = true;
18270                 delete this.dragOvers[i];
18271             }
18272
18273             for (var sGroup in dc.groups) {
18274
18275                 if ("string" != typeof sGroup) {
18276                     continue;
18277                 }
18278
18279                 for (i in this.ids[sGroup]) {
18280                     var oDD = this.ids[sGroup][i];
18281                     if (! this.isTypeOfDD(oDD)) {
18282                         continue;
18283                     }
18284
18285                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18286                         if (this.isOverTarget(pt, oDD, this.mode)) {
18287                             // look for drop interactions
18288                             if (isDrop) {
18289                                 dropEvts.push( oDD );
18290                             // look for drag enter and drag over interactions
18291                             } else {
18292
18293                                 // initial drag over: dragEnter fires
18294                                 if (!oldOvers[oDD.id]) {
18295                                     enterEvts.push( oDD );
18296                                 // subsequent drag overs: dragOver fires
18297                                 } else {
18298                                     overEvts.push( oDD );
18299                                 }
18300
18301                                 this.dragOvers[oDD.id] = oDD;
18302                             }
18303                         }
18304                     }
18305                 }
18306             }
18307
18308             if (this.mode) {
18309                 if (outEvts.length) {
18310                     dc.b4DragOut(e, outEvts);
18311                     dc.onDragOut(e, outEvts);
18312                 }
18313
18314                 if (enterEvts.length) {
18315                     dc.onDragEnter(e, enterEvts);
18316                 }
18317
18318                 if (overEvts.length) {
18319                     dc.b4DragOver(e, overEvts);
18320                     dc.onDragOver(e, overEvts);
18321                 }
18322
18323                 if (dropEvts.length) {
18324                     dc.b4DragDrop(e, dropEvts);
18325                     dc.onDragDrop(e, dropEvts);
18326                 }
18327
18328             } else {
18329                 // fire dragout events
18330                 var len = 0;
18331                 for (i=0, len=outEvts.length; i<len; ++i) {
18332                     dc.b4DragOut(e, outEvts[i].id);
18333                     dc.onDragOut(e, outEvts[i].id);
18334                 }
18335
18336                 // fire enter events
18337                 for (i=0,len=enterEvts.length; i<len; ++i) {
18338                     // dc.b4DragEnter(e, oDD.id);
18339                     dc.onDragEnter(e, enterEvts[i].id);
18340                 }
18341
18342                 // fire over events
18343                 for (i=0,len=overEvts.length; i<len; ++i) {
18344                     dc.b4DragOver(e, overEvts[i].id);
18345                     dc.onDragOver(e, overEvts[i].id);
18346                 }
18347
18348                 // fire drop events
18349                 for (i=0, len=dropEvts.length; i<len; ++i) {
18350                     dc.b4DragDrop(e, dropEvts[i].id);
18351                     dc.onDragDrop(e, dropEvts[i].id);
18352                 }
18353
18354             }
18355
18356             // notify about a drop that did not find a target
18357             if (isDrop && !dropEvts.length) {
18358                 dc.onInvalidDrop(e);
18359             }
18360
18361         },
18362
18363         /**
18364          * Helper function for getting the best match from the list of drag
18365          * and drop objects returned by the drag and drop events when we are
18366          * in INTERSECT mode.  It returns either the first object that the
18367          * cursor is over, or the object that has the greatest overlap with
18368          * the dragged element.
18369          * @method getBestMatch
18370          * @param  {DragDrop[]} dds The array of drag and drop objects
18371          * targeted
18372          * @return {DragDrop}       The best single match
18373          * @static
18374          */
18375         getBestMatch: function(dds) {
18376             var winner = null;
18377             // Return null if the input is not what we expect
18378             //if (!dds || !dds.length || dds.length == 0) {
18379                // winner = null;
18380             // If there is only one item, it wins
18381             //} else if (dds.length == 1) {
18382
18383             var len = dds.length;
18384
18385             if (len == 1) {
18386                 winner = dds[0];
18387             } else {
18388                 // Loop through the targeted items
18389                 for (var i=0; i<len; ++i) {
18390                     var dd = dds[i];
18391                     // If the cursor is over the object, it wins.  If the
18392                     // cursor is over multiple matches, the first one we come
18393                     // to wins.
18394                     if (dd.cursorIsOver) {
18395                         winner = dd;
18396                         break;
18397                     // Otherwise the object with the most overlap wins
18398                     } else {
18399                         if (!winner ||
18400                             winner.overlap.getArea() < dd.overlap.getArea()) {
18401                             winner = dd;
18402                         }
18403                     }
18404                 }
18405             }
18406
18407             return winner;
18408         },
18409
18410         /**
18411          * Refreshes the cache of the top-left and bottom-right points of the
18412          * drag and drop objects in the specified group(s).  This is in the
18413          * format that is stored in the drag and drop instance, so typical
18414          * usage is:
18415          * <code>
18416          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18417          * </code>
18418          * Alternatively:
18419          * <code>
18420          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18421          * </code>
18422          * @TODO this really should be an indexed array.  Alternatively this
18423          * method could accept both.
18424          * @method refreshCache
18425          * @param {Object} groups an associative array of groups to refresh
18426          * @static
18427          */
18428         refreshCache: function(groups) {
18429             for (var sGroup in groups) {
18430                 if ("string" != typeof sGroup) {
18431                     continue;
18432                 }
18433                 for (var i in this.ids[sGroup]) {
18434                     var oDD = this.ids[sGroup][i];
18435
18436                     if (this.isTypeOfDD(oDD)) {
18437                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18438                         var loc = this.getLocation(oDD);
18439                         if (loc) {
18440                             this.locationCache[oDD.id] = loc;
18441                         } else {
18442                             delete this.locationCache[oDD.id];
18443                             // this will unregister the drag and drop object if
18444                             // the element is not in a usable state
18445                             // oDD.unreg();
18446                         }
18447                     }
18448                 }
18449             }
18450         },
18451
18452         /**
18453          * This checks to make sure an element exists and is in the DOM.  The
18454          * main purpose is to handle cases where innerHTML is used to remove
18455          * drag and drop objects from the DOM.  IE provides an 'unspecified
18456          * error' when trying to access the offsetParent of such an element
18457          * @method verifyEl
18458          * @param {HTMLElement} el the element to check
18459          * @return {boolean} true if the element looks usable
18460          * @static
18461          */
18462         verifyEl: function(el) {
18463             if (el) {
18464                 var parent;
18465                 if(Roo.isIE){
18466                     try{
18467                         parent = el.offsetParent;
18468                     }catch(e){}
18469                 }else{
18470                     parent = el.offsetParent;
18471                 }
18472                 if (parent) {
18473                     return true;
18474                 }
18475             }
18476
18477             return false;
18478         },
18479
18480         /**
18481          * Returns a Region object containing the drag and drop element's position
18482          * and size, including the padding configured for it
18483          * @method getLocation
18484          * @param {DragDrop} oDD the drag and drop object to get the
18485          *                       location for
18486          * @return {Roo.lib.Region} a Region object representing the total area
18487          *                             the element occupies, including any padding
18488          *                             the instance is configured for.
18489          * @static
18490          */
18491         getLocation: function(oDD) {
18492             if (! this.isTypeOfDD(oDD)) {
18493                 return null;
18494             }
18495
18496             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18497
18498             try {
18499                 pos= Roo.lib.Dom.getXY(el);
18500             } catch (e) { }
18501
18502             if (!pos) {
18503                 return null;
18504             }
18505
18506             x1 = pos[0];
18507             x2 = x1 + el.offsetWidth;
18508             y1 = pos[1];
18509             y2 = y1 + el.offsetHeight;
18510
18511             t = y1 - oDD.padding[0];
18512             r = x2 + oDD.padding[1];
18513             b = y2 + oDD.padding[2];
18514             l = x1 - oDD.padding[3];
18515
18516             return new Roo.lib.Region( t, r, b, l );
18517         },
18518
18519         /**
18520          * Checks the cursor location to see if it over the target
18521          * @method isOverTarget
18522          * @param {Roo.lib.Point} pt The point to evaluate
18523          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18524          * @return {boolean} true if the mouse is over the target
18525          * @private
18526          * @static
18527          */
18528         isOverTarget: function(pt, oTarget, intersect) {
18529             // use cache if available
18530             var loc = this.locationCache[oTarget.id];
18531             if (!loc || !this.useCache) {
18532                 loc = this.getLocation(oTarget);
18533                 this.locationCache[oTarget.id] = loc;
18534
18535             }
18536
18537             if (!loc) {
18538                 return false;
18539             }
18540
18541             oTarget.cursorIsOver = loc.contains( pt );
18542
18543             // DragDrop is using this as a sanity check for the initial mousedown
18544             // in this case we are done.  In POINT mode, if the drag obj has no
18545             // contraints, we are also done. Otherwise we need to evaluate the
18546             // location of the target as related to the actual location of the
18547             // dragged element.
18548             var dc = this.dragCurrent;
18549             if (!dc || !dc.getTargetCoord ||
18550                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18551                 return oTarget.cursorIsOver;
18552             }
18553
18554             oTarget.overlap = null;
18555
18556             // Get the current location of the drag element, this is the
18557             // location of the mouse event less the delta that represents
18558             // where the original mousedown happened on the element.  We
18559             // need to consider constraints and ticks as well.
18560             var pos = dc.getTargetCoord(pt.x, pt.y);
18561
18562             var el = dc.getDragEl();
18563             var curRegion = new Roo.lib.Region( pos.y,
18564                                                    pos.x + el.offsetWidth,
18565                                                    pos.y + el.offsetHeight,
18566                                                    pos.x );
18567
18568             var overlap = curRegion.intersect(loc);
18569
18570             if (overlap) {
18571                 oTarget.overlap = overlap;
18572                 return (intersect) ? true : oTarget.cursorIsOver;
18573             } else {
18574                 return false;
18575             }
18576         },
18577
18578         /**
18579          * unload event handler
18580          * @method _onUnload
18581          * @private
18582          * @static
18583          */
18584         _onUnload: function(e, me) {
18585             Roo.dd.DragDropMgr.unregAll();
18586         },
18587
18588         /**
18589          * Cleans up the drag and drop events and objects.
18590          * @method unregAll
18591          * @private
18592          * @static
18593          */
18594         unregAll: function() {
18595
18596             if (this.dragCurrent) {
18597                 this.stopDrag();
18598                 this.dragCurrent = null;
18599             }
18600
18601             this._execOnAll("unreg", []);
18602
18603             for (i in this.elementCache) {
18604                 delete this.elementCache[i];
18605             }
18606
18607             this.elementCache = {};
18608             this.ids = {};
18609         },
18610
18611         /**
18612          * A cache of DOM elements
18613          * @property elementCache
18614          * @private
18615          * @static
18616          */
18617         elementCache: {},
18618
18619         /**
18620          * Get the wrapper for the DOM element specified
18621          * @method getElWrapper
18622          * @param {String} id the id of the element to get
18623          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18624          * @private
18625          * @deprecated This wrapper isn't that useful
18626          * @static
18627          */
18628         getElWrapper: function(id) {
18629             var oWrapper = this.elementCache[id];
18630             if (!oWrapper || !oWrapper.el) {
18631                 oWrapper = this.elementCache[id] =
18632                     new this.ElementWrapper(Roo.getDom(id));
18633             }
18634             return oWrapper;
18635         },
18636
18637         /**
18638          * Returns the actual DOM element
18639          * @method getElement
18640          * @param {String} id the id of the elment to get
18641          * @return {Object} The element
18642          * @deprecated use Roo.getDom instead
18643          * @static
18644          */
18645         getElement: function(id) {
18646             return Roo.getDom(id);
18647         },
18648
18649         /**
18650          * Returns the style property for the DOM element (i.e.,
18651          * document.getElById(id).style)
18652          * @method getCss
18653          * @param {String} id the id of the elment to get
18654          * @return {Object} The style property of the element
18655          * @deprecated use Roo.getDom instead
18656          * @static
18657          */
18658         getCss: function(id) {
18659             var el = Roo.getDom(id);
18660             return (el) ? el.style : null;
18661         },
18662
18663         /**
18664          * Inner class for cached elements
18665          * @class DragDropMgr.ElementWrapper
18666          * @for DragDropMgr
18667          * @private
18668          * @deprecated
18669          */
18670         ElementWrapper: function(el) {
18671                 /**
18672                  * The element
18673                  * @property el
18674                  */
18675                 this.el = el || null;
18676                 /**
18677                  * The element id
18678                  * @property id
18679                  */
18680                 this.id = this.el && el.id;
18681                 /**
18682                  * A reference to the style property
18683                  * @property css
18684                  */
18685                 this.css = this.el && el.style;
18686             },
18687
18688         /**
18689          * Returns the X position of an html element
18690          * @method getPosX
18691          * @param el the element for which to get the position
18692          * @return {int} the X coordinate
18693          * @for DragDropMgr
18694          * @deprecated use Roo.lib.Dom.getX instead
18695          * @static
18696          */
18697         getPosX: function(el) {
18698             return Roo.lib.Dom.getX(el);
18699         },
18700
18701         /**
18702          * Returns the Y position of an html element
18703          * @method getPosY
18704          * @param el the element for which to get the position
18705          * @return {int} the Y coordinate
18706          * @deprecated use Roo.lib.Dom.getY instead
18707          * @static
18708          */
18709         getPosY: function(el) {
18710             return Roo.lib.Dom.getY(el);
18711         },
18712
18713         /**
18714          * Swap two nodes.  In IE, we use the native method, for others we
18715          * emulate the IE behavior
18716          * @method swapNode
18717          * @param n1 the first node to swap
18718          * @param n2 the other node to swap
18719          * @static
18720          */
18721         swapNode: function(n1, n2) {
18722             if (n1.swapNode) {
18723                 n1.swapNode(n2);
18724             } else {
18725                 var p = n2.parentNode;
18726                 var s = n2.nextSibling;
18727
18728                 if (s == n1) {
18729                     p.insertBefore(n1, n2);
18730                 } else if (n2 == n1.nextSibling) {
18731                     p.insertBefore(n2, n1);
18732                 } else {
18733                     n1.parentNode.replaceChild(n2, n1);
18734                     p.insertBefore(n1, s);
18735                 }
18736             }
18737         },
18738
18739         /**
18740          * Returns the current scroll position
18741          * @method getScroll
18742          * @private
18743          * @static
18744          */
18745         getScroll: function () {
18746             var t, l, dde=document.documentElement, db=document.body;
18747             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18748                 t = dde.scrollTop;
18749                 l = dde.scrollLeft;
18750             } else if (db) {
18751                 t = db.scrollTop;
18752                 l = db.scrollLeft;
18753             } else {
18754
18755             }
18756             return { top: t, left: l };
18757         },
18758
18759         /**
18760          * Returns the specified element style property
18761          * @method getStyle
18762          * @param {HTMLElement} el          the element
18763          * @param {string}      styleProp   the style property
18764          * @return {string} The value of the style property
18765          * @deprecated use Roo.lib.Dom.getStyle
18766          * @static
18767          */
18768         getStyle: function(el, styleProp) {
18769             return Roo.fly(el).getStyle(styleProp);
18770         },
18771
18772         /**
18773          * Gets the scrollTop
18774          * @method getScrollTop
18775          * @return {int} the document's scrollTop
18776          * @static
18777          */
18778         getScrollTop: function () { return this.getScroll().top; },
18779
18780         /**
18781          * Gets the scrollLeft
18782          * @method getScrollLeft
18783          * @return {int} the document's scrollTop
18784          * @static
18785          */
18786         getScrollLeft: function () { return this.getScroll().left; },
18787
18788         /**
18789          * Sets the x/y position of an element to the location of the
18790          * target element.
18791          * @method moveToEl
18792          * @param {HTMLElement} moveEl      The element to move
18793          * @param {HTMLElement} targetEl    The position reference element
18794          * @static
18795          */
18796         moveToEl: function (moveEl, targetEl) {
18797             var aCoord = Roo.lib.Dom.getXY(targetEl);
18798             Roo.lib.Dom.setXY(moveEl, aCoord);
18799         },
18800
18801         /**
18802          * Numeric array sort function
18803          * @method numericSort
18804          * @static
18805          */
18806         numericSort: function(a, b) { return (a - b); },
18807
18808         /**
18809          * Internal counter
18810          * @property _timeoutCount
18811          * @private
18812          * @static
18813          */
18814         _timeoutCount: 0,
18815
18816         /**
18817          * Trying to make the load order less important.  Without this we get
18818          * an error if this file is loaded before the Event Utility.
18819          * @method _addListeners
18820          * @private
18821          * @static
18822          */
18823         _addListeners: function() {
18824             var DDM = Roo.dd.DDM;
18825             if ( Roo.lib.Event && document ) {
18826                 DDM._onLoad();
18827             } else {
18828                 if (DDM._timeoutCount > 2000) {
18829                 } else {
18830                     setTimeout(DDM._addListeners, 10);
18831                     if (document && document.body) {
18832                         DDM._timeoutCount += 1;
18833                     }
18834                 }
18835             }
18836         },
18837
18838         /**
18839          * Recursively searches the immediate parent and all child nodes for
18840          * the handle element in order to determine wheter or not it was
18841          * clicked.
18842          * @method handleWasClicked
18843          * @param node the html element to inspect
18844          * @static
18845          */
18846         handleWasClicked: function(node, id) {
18847             if (this.isHandle(id, node.id)) {
18848                 return true;
18849             } else {
18850                 // check to see if this is a text node child of the one we want
18851                 var p = node.parentNode;
18852
18853                 while (p) {
18854                     if (this.isHandle(id, p.id)) {
18855                         return true;
18856                     } else {
18857                         p = p.parentNode;
18858                     }
18859                 }
18860             }
18861
18862             return false;
18863         }
18864
18865     };
18866
18867 }();
18868
18869 // shorter alias, save a few bytes
18870 Roo.dd.DDM = Roo.dd.DragDropMgr;
18871 Roo.dd.DDM._addListeners();
18872
18873 }/*
18874  * Based on:
18875  * Ext JS Library 1.1.1
18876  * Copyright(c) 2006-2007, Ext JS, LLC.
18877  *
18878  * Originally Released Under LGPL - original licence link has changed is not relivant.
18879  *
18880  * Fork - LGPL
18881  * <script type="text/javascript">
18882  */
18883
18884 /**
18885  * @class Roo.dd.DD
18886  * A DragDrop implementation where the linked element follows the
18887  * mouse cursor during a drag.
18888  * @extends Roo.dd.DragDrop
18889  * @constructor
18890  * @param {String} id the id of the linked element
18891  * @param {String} sGroup the group of related DragDrop items
18892  * @param {object} config an object containing configurable attributes
18893  *                Valid properties for DD:
18894  *                    scroll
18895  */
18896 Roo.dd.DD = function(id, sGroup, config) {
18897     if (id) {
18898         this.init(id, sGroup, config);
18899     }
18900 };
18901
18902 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18903
18904     /**
18905      * When set to true, the utility automatically tries to scroll the browser
18906      * window wehn a drag and drop element is dragged near the viewport boundary.
18907      * Defaults to true.
18908      * @property scroll
18909      * @type boolean
18910      */
18911     scroll: true,
18912
18913     /**
18914      * Sets the pointer offset to the distance between the linked element's top
18915      * left corner and the location the element was clicked
18916      * @method autoOffset
18917      * @param {int} iPageX the X coordinate of the click
18918      * @param {int} iPageY the Y coordinate of the click
18919      */
18920     autoOffset: function(iPageX, iPageY) {
18921         var x = iPageX - this.startPageX;
18922         var y = iPageY - this.startPageY;
18923         this.setDelta(x, y);
18924     },
18925
18926     /**
18927      * Sets the pointer offset.  You can call this directly to force the
18928      * offset to be in a particular location (e.g., pass in 0,0 to set it
18929      * to the center of the object)
18930      * @method setDelta
18931      * @param {int} iDeltaX the distance from the left
18932      * @param {int} iDeltaY the distance from the top
18933      */
18934     setDelta: function(iDeltaX, iDeltaY) {
18935         this.deltaX = iDeltaX;
18936         this.deltaY = iDeltaY;
18937     },
18938
18939     /**
18940      * Sets the drag element to the location of the mousedown or click event,
18941      * maintaining the cursor location relative to the location on the element
18942      * that was clicked.  Override this if you want to place the element in a
18943      * location other than where the cursor is.
18944      * @method setDragElPos
18945      * @param {int} iPageX the X coordinate of the mousedown or drag event
18946      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18947      */
18948     setDragElPos: function(iPageX, iPageY) {
18949         // the first time we do this, we are going to check to make sure
18950         // the element has css positioning
18951
18952         var el = this.getDragEl();
18953         this.alignElWithMouse(el, iPageX, iPageY);
18954     },
18955
18956     /**
18957      * Sets the element to the location of the mousedown or click event,
18958      * maintaining the cursor location relative to the location on the element
18959      * that was clicked.  Override this if you want to place the element in a
18960      * location other than where the cursor is.
18961      * @method alignElWithMouse
18962      * @param {HTMLElement} el the element to move
18963      * @param {int} iPageX the X coordinate of the mousedown or drag event
18964      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18965      */
18966     alignElWithMouse: function(el, iPageX, iPageY) {
18967         var oCoord = this.getTargetCoord(iPageX, iPageY);
18968         var fly = el.dom ? el : Roo.fly(el);
18969         if (!this.deltaSetXY) {
18970             var aCoord = [oCoord.x, oCoord.y];
18971             fly.setXY(aCoord);
18972             var newLeft = fly.getLeft(true);
18973             var newTop  = fly.getTop(true);
18974             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18975         } else {
18976             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18977         }
18978
18979         this.cachePosition(oCoord.x, oCoord.y);
18980         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18981         return oCoord;
18982     },
18983
18984     /**
18985      * Saves the most recent position so that we can reset the constraints and
18986      * tick marks on-demand.  We need to know this so that we can calculate the
18987      * number of pixels the element is offset from its original position.
18988      * @method cachePosition
18989      * @param iPageX the current x position (optional, this just makes it so we
18990      * don't have to look it up again)
18991      * @param iPageY the current y position (optional, this just makes it so we
18992      * don't have to look it up again)
18993      */
18994     cachePosition: function(iPageX, iPageY) {
18995         if (iPageX) {
18996             this.lastPageX = iPageX;
18997             this.lastPageY = iPageY;
18998         } else {
18999             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19000             this.lastPageX = aCoord[0];
19001             this.lastPageY = aCoord[1];
19002         }
19003     },
19004
19005     /**
19006      * Auto-scroll the window if the dragged object has been moved beyond the
19007      * visible window boundary.
19008      * @method autoScroll
19009      * @param {int} x the drag element's x position
19010      * @param {int} y the drag element's y position
19011      * @param {int} h the height of the drag element
19012      * @param {int} w the width of the drag element
19013      * @private
19014      */
19015     autoScroll: function(x, y, h, w) {
19016
19017         if (this.scroll) {
19018             // The client height
19019             var clientH = Roo.lib.Dom.getViewWidth();
19020
19021             // The client width
19022             var clientW = Roo.lib.Dom.getViewHeight();
19023
19024             // The amt scrolled down
19025             var st = this.DDM.getScrollTop();
19026
19027             // The amt scrolled right
19028             var sl = this.DDM.getScrollLeft();
19029
19030             // Location of the bottom of the element
19031             var bot = h + y;
19032
19033             // Location of the right of the element
19034             var right = w + x;
19035
19036             // The distance from the cursor to the bottom of the visible area,
19037             // adjusted so that we don't scroll if the cursor is beyond the
19038             // element drag constraints
19039             var toBot = (clientH + st - y - this.deltaY);
19040
19041             // The distance from the cursor to the right of the visible area
19042             var toRight = (clientW + sl - x - this.deltaX);
19043
19044
19045             // How close to the edge the cursor must be before we scroll
19046             // var thresh = (document.all) ? 100 : 40;
19047             var thresh = 40;
19048
19049             // How many pixels to scroll per autoscroll op.  This helps to reduce
19050             // clunky scrolling. IE is more sensitive about this ... it needs this
19051             // value to be higher.
19052             var scrAmt = (document.all) ? 80 : 30;
19053
19054             // Scroll down if we are near the bottom of the visible page and the
19055             // obj extends below the crease
19056             if ( bot > clientH && toBot < thresh ) {
19057                 window.scrollTo(sl, st + scrAmt);
19058             }
19059
19060             // Scroll up if the window is scrolled down and the top of the object
19061             // goes above the top border
19062             if ( y < st && st > 0 && y - st < thresh ) {
19063                 window.scrollTo(sl, st - scrAmt);
19064             }
19065
19066             // Scroll right if the obj is beyond the right border and the cursor is
19067             // near the border.
19068             if ( right > clientW && toRight < thresh ) {
19069                 window.scrollTo(sl + scrAmt, st);
19070             }
19071
19072             // Scroll left if the window has been scrolled to the right and the obj
19073             // extends past the left border
19074             if ( x < sl && sl > 0 && x - sl < thresh ) {
19075                 window.scrollTo(sl - scrAmt, st);
19076             }
19077         }
19078     },
19079
19080     /**
19081      * Finds the location the element should be placed if we want to move
19082      * it to where the mouse location less the click offset would place us.
19083      * @method getTargetCoord
19084      * @param {int} iPageX the X coordinate of the click
19085      * @param {int} iPageY the Y coordinate of the click
19086      * @return an object that contains the coordinates (Object.x and Object.y)
19087      * @private
19088      */
19089     getTargetCoord: function(iPageX, iPageY) {
19090
19091
19092         var x = iPageX - this.deltaX;
19093         var y = iPageY - this.deltaY;
19094
19095         if (this.constrainX) {
19096             if (x < this.minX) { x = this.minX; }
19097             if (x > this.maxX) { x = this.maxX; }
19098         }
19099
19100         if (this.constrainY) {
19101             if (y < this.minY) { y = this.minY; }
19102             if (y > this.maxY) { y = this.maxY; }
19103         }
19104
19105         x = this.getTick(x, this.xTicks);
19106         y = this.getTick(y, this.yTicks);
19107
19108
19109         return {x:x, y:y};
19110     },
19111
19112     /*
19113      * Sets up config options specific to this class. Overrides
19114      * Roo.dd.DragDrop, but all versions of this method through the
19115      * inheritance chain are called
19116      */
19117     applyConfig: function() {
19118         Roo.dd.DD.superclass.applyConfig.call(this);
19119         this.scroll = (this.config.scroll !== false);
19120     },
19121
19122     /*
19123      * Event that fires prior to the onMouseDown event.  Overrides
19124      * Roo.dd.DragDrop.
19125      */
19126     b4MouseDown: function(e) {
19127         // this.resetConstraints();
19128         this.autoOffset(e.getPageX(),
19129                             e.getPageY());
19130     },
19131
19132     /*
19133      * Event that fires prior to the onDrag event.  Overrides
19134      * Roo.dd.DragDrop.
19135      */
19136     b4Drag: function(e) {
19137         this.setDragElPos(e.getPageX(),
19138                             e.getPageY());
19139     },
19140
19141     toString: function() {
19142         return ("DD " + this.id);
19143     }
19144
19145     //////////////////////////////////////////////////////////////////////////
19146     // Debugging ygDragDrop events that can be overridden
19147     //////////////////////////////////////////////////////////////////////////
19148     /*
19149     startDrag: function(x, y) {
19150     },
19151
19152     onDrag: function(e) {
19153     },
19154
19155     onDragEnter: function(e, id) {
19156     },
19157
19158     onDragOver: function(e, id) {
19159     },
19160
19161     onDragOut: function(e, id) {
19162     },
19163
19164     onDragDrop: function(e, id) {
19165     },
19166
19167     endDrag: function(e) {
19168     }
19169
19170     */
19171
19172 });/*
19173  * Based on:
19174  * Ext JS Library 1.1.1
19175  * Copyright(c) 2006-2007, Ext JS, LLC.
19176  *
19177  * Originally Released Under LGPL - original licence link has changed is not relivant.
19178  *
19179  * Fork - LGPL
19180  * <script type="text/javascript">
19181  */
19182
19183 /**
19184  * @class Roo.dd.DDProxy
19185  * A DragDrop implementation that inserts an empty, bordered div into
19186  * the document that follows the cursor during drag operations.  At the time of
19187  * the click, the frame div is resized to the dimensions of the linked html
19188  * element, and moved to the exact location of the linked element.
19189  *
19190  * References to the "frame" element refer to the single proxy element that
19191  * was created to be dragged in place of all DDProxy elements on the
19192  * page.
19193  *
19194  * @extends Roo.dd.DD
19195  * @constructor
19196  * @param {String} id the id of the linked html element
19197  * @param {String} sGroup the group of related DragDrop objects
19198  * @param {object} config an object containing configurable attributes
19199  *                Valid properties for DDProxy in addition to those in DragDrop:
19200  *                   resizeFrame, centerFrame, dragElId
19201  */
19202 Roo.dd.DDProxy = function(id, sGroup, config) {
19203     if (id) {
19204         this.init(id, sGroup, config);
19205         this.initFrame();
19206     }
19207 };
19208
19209 /**
19210  * The default drag frame div id
19211  * @property Roo.dd.DDProxy.dragElId
19212  * @type String
19213  * @static
19214  */
19215 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19216
19217 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19218
19219     /**
19220      * By default we resize the drag frame to be the same size as the element
19221      * we want to drag (this is to get the frame effect).  We can turn it off
19222      * if we want a different behavior.
19223      * @property resizeFrame
19224      * @type boolean
19225      */
19226     resizeFrame: true,
19227
19228     /**
19229      * By default the frame is positioned exactly where the drag element is, so
19230      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19231      * you do not have constraints on the obj is to have the drag frame centered
19232      * around the cursor.  Set centerFrame to true for this effect.
19233      * @property centerFrame
19234      * @type boolean
19235      */
19236     centerFrame: false,
19237
19238     /**
19239      * Creates the proxy element if it does not yet exist
19240      * @method createFrame
19241      */
19242     createFrame: function() {
19243         var self = this;
19244         var body = document.body;
19245
19246         if (!body || !body.firstChild) {
19247             setTimeout( function() { self.createFrame(); }, 50 );
19248             return;
19249         }
19250
19251         var div = this.getDragEl();
19252
19253         if (!div) {
19254             div    = document.createElement("div");
19255             div.id = this.dragElId;
19256             var s  = div.style;
19257
19258             s.position   = "absolute";
19259             s.visibility = "hidden";
19260             s.cursor     = "move";
19261             s.border     = "2px solid #aaa";
19262             s.zIndex     = 999;
19263
19264             // appendChild can blow up IE if invoked prior to the window load event
19265             // while rendering a table.  It is possible there are other scenarios
19266             // that would cause this to happen as well.
19267             body.insertBefore(div, body.firstChild);
19268         }
19269     },
19270
19271     /**
19272      * Initialization for the drag frame element.  Must be called in the
19273      * constructor of all subclasses
19274      * @method initFrame
19275      */
19276     initFrame: function() {
19277         this.createFrame();
19278     },
19279
19280     applyConfig: function() {
19281         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19282
19283         this.resizeFrame = (this.config.resizeFrame !== false);
19284         this.centerFrame = (this.config.centerFrame);
19285         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19286     },
19287
19288     /**
19289      * Resizes the drag frame to the dimensions of the clicked object, positions
19290      * it over the object, and finally displays it
19291      * @method showFrame
19292      * @param {int} iPageX X click position
19293      * @param {int} iPageY Y click position
19294      * @private
19295      */
19296     showFrame: function(iPageX, iPageY) {
19297         var el = this.getEl();
19298         var dragEl = this.getDragEl();
19299         var s = dragEl.style;
19300
19301         this._resizeProxy();
19302
19303         if (this.centerFrame) {
19304             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19305                            Math.round(parseInt(s.height, 10)/2) );
19306         }
19307
19308         this.setDragElPos(iPageX, iPageY);
19309
19310         Roo.fly(dragEl).show();
19311     },
19312
19313     /**
19314      * The proxy is automatically resized to the dimensions of the linked
19315      * element when a drag is initiated, unless resizeFrame is set to false
19316      * @method _resizeProxy
19317      * @private
19318      */
19319     _resizeProxy: function() {
19320         if (this.resizeFrame) {
19321             var el = this.getEl();
19322             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19323         }
19324     },
19325
19326     // overrides Roo.dd.DragDrop
19327     b4MouseDown: function(e) {
19328         var x = e.getPageX();
19329         var y = e.getPageY();
19330         this.autoOffset(x, y);
19331         this.setDragElPos(x, y);
19332     },
19333
19334     // overrides Roo.dd.DragDrop
19335     b4StartDrag: function(x, y) {
19336         // show the drag frame
19337         this.showFrame(x, y);
19338     },
19339
19340     // overrides Roo.dd.DragDrop
19341     b4EndDrag: function(e) {
19342         Roo.fly(this.getDragEl()).hide();
19343     },
19344
19345     // overrides Roo.dd.DragDrop
19346     // By default we try to move the element to the last location of the frame.
19347     // This is so that the default behavior mirrors that of Roo.dd.DD.
19348     endDrag: function(e) {
19349
19350         var lel = this.getEl();
19351         var del = this.getDragEl();
19352
19353         // Show the drag frame briefly so we can get its position
19354         del.style.visibility = "";
19355
19356         this.beforeMove();
19357         // Hide the linked element before the move to get around a Safari
19358         // rendering bug.
19359         lel.style.visibility = "hidden";
19360         Roo.dd.DDM.moveToEl(lel, del);
19361         del.style.visibility = "hidden";
19362         lel.style.visibility = "";
19363
19364         this.afterDrag();
19365     },
19366
19367     beforeMove : function(){
19368
19369     },
19370
19371     afterDrag : function(){
19372
19373     },
19374
19375     toString: function() {
19376         return ("DDProxy " + this.id);
19377     }
19378
19379 });
19380 /*
19381  * Based on:
19382  * Ext JS Library 1.1.1
19383  * Copyright(c) 2006-2007, Ext JS, LLC.
19384  *
19385  * Originally Released Under LGPL - original licence link has changed is not relivant.
19386  *
19387  * Fork - LGPL
19388  * <script type="text/javascript">
19389  */
19390
19391  /**
19392  * @class Roo.dd.DDTarget
19393  * A DragDrop implementation that does not move, but can be a drop
19394  * target.  You would get the same result by simply omitting implementation
19395  * for the event callbacks, but this way we reduce the processing cost of the
19396  * event listener and the callbacks.
19397  * @extends Roo.dd.DragDrop
19398  * @constructor
19399  * @param {String} id the id of the element that is a drop target
19400  * @param {String} sGroup the group of related DragDrop objects
19401  * @param {object} config an object containing configurable attributes
19402  *                 Valid properties for DDTarget in addition to those in
19403  *                 DragDrop:
19404  *                    none
19405  */
19406 Roo.dd.DDTarget = function(id, sGroup, config) {
19407     if (id) {
19408         this.initTarget(id, sGroup, config);
19409     }
19410     if (config.listeners || config.events) { 
19411        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19412             listeners : config.listeners || {}, 
19413             events : config.events || {} 
19414         });    
19415     }
19416 };
19417
19418 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19419 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19420     toString: function() {
19421         return ("DDTarget " + this.id);
19422     }
19423 });
19424 /*
19425  * Based on:
19426  * Ext JS Library 1.1.1
19427  * Copyright(c) 2006-2007, Ext JS, LLC.
19428  *
19429  * Originally Released Under LGPL - original licence link has changed is not relivant.
19430  *
19431  * Fork - LGPL
19432  * <script type="text/javascript">
19433  */
19434  
19435
19436 /**
19437  * @class Roo.dd.ScrollManager
19438  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19439  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19440  * @singleton
19441  */
19442 Roo.dd.ScrollManager = function(){
19443     var ddm = Roo.dd.DragDropMgr;
19444     var els = {};
19445     var dragEl = null;
19446     var proc = {};
19447     
19448     
19449     
19450     var onStop = function(e){
19451         dragEl = null;
19452         clearProc();
19453     };
19454     
19455     var triggerRefresh = function(){
19456         if(ddm.dragCurrent){
19457              ddm.refreshCache(ddm.dragCurrent.groups);
19458         }
19459     };
19460     
19461     var doScroll = function(){
19462         if(ddm.dragCurrent){
19463             var dds = Roo.dd.ScrollManager;
19464             if(!dds.animate){
19465                 if(proc.el.scroll(proc.dir, dds.increment)){
19466                     triggerRefresh();
19467                 }
19468             }else{
19469                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19470             }
19471         }
19472     };
19473     
19474     var clearProc = function(){
19475         if(proc.id){
19476             clearInterval(proc.id);
19477         }
19478         proc.id = 0;
19479         proc.el = null;
19480         proc.dir = "";
19481     };
19482     
19483     var startProc = function(el, dir){
19484          Roo.log('scroll startproc');
19485         clearProc();
19486         proc.el = el;
19487         proc.dir = dir;
19488         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19489     };
19490     
19491     var onFire = function(e, isDrop){
19492        
19493         if(isDrop || !ddm.dragCurrent){ return; }
19494         var dds = Roo.dd.ScrollManager;
19495         if(!dragEl || dragEl != ddm.dragCurrent){
19496             dragEl = ddm.dragCurrent;
19497             // refresh regions on drag start
19498             dds.refreshCache();
19499         }
19500         
19501         var xy = Roo.lib.Event.getXY(e);
19502         var pt = new Roo.lib.Point(xy[0], xy[1]);
19503         for(var id in els){
19504             var el = els[id], r = el._region;
19505             if(r && r.contains(pt) && el.isScrollable()){
19506                 if(r.bottom - pt.y <= dds.thresh){
19507                     if(proc.el != el){
19508                         startProc(el, "down");
19509                     }
19510                     return;
19511                 }else if(r.right - pt.x <= dds.thresh){
19512                     if(proc.el != el){
19513                         startProc(el, "left");
19514                     }
19515                     return;
19516                 }else if(pt.y - r.top <= dds.thresh){
19517                     if(proc.el != el){
19518                         startProc(el, "up");
19519                     }
19520                     return;
19521                 }else if(pt.x - r.left <= dds.thresh){
19522                     if(proc.el != el){
19523                         startProc(el, "right");
19524                     }
19525                     return;
19526                 }
19527             }
19528         }
19529         clearProc();
19530     };
19531     
19532     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19533     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19534     
19535     return {
19536         /**
19537          * Registers new overflow element(s) to auto scroll
19538          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19539          */
19540         register : function(el){
19541             if(el instanceof Array){
19542                 for(var i = 0, len = el.length; i < len; i++) {
19543                         this.register(el[i]);
19544                 }
19545             }else{
19546                 el = Roo.get(el);
19547                 els[el.id] = el;
19548             }
19549             Roo.dd.ScrollManager.els = els;
19550         },
19551         
19552         /**
19553          * Unregisters overflow element(s) so they are no longer scrolled
19554          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19555          */
19556         unregister : function(el){
19557             if(el instanceof Array){
19558                 for(var i = 0, len = el.length; i < len; i++) {
19559                         this.unregister(el[i]);
19560                 }
19561             }else{
19562                 el = Roo.get(el);
19563                 delete els[el.id];
19564             }
19565         },
19566         
19567         /**
19568          * The number of pixels from the edge of a container the pointer needs to be to 
19569          * trigger scrolling (defaults to 25)
19570          * @type Number
19571          */
19572         thresh : 25,
19573         
19574         /**
19575          * The number of pixels to scroll in each scroll increment (defaults to 50)
19576          * @type Number
19577          */
19578         increment : 100,
19579         
19580         /**
19581          * The frequency of scrolls in milliseconds (defaults to 500)
19582          * @type Number
19583          */
19584         frequency : 500,
19585         
19586         /**
19587          * True to animate the scroll (defaults to true)
19588          * @type Boolean
19589          */
19590         animate: true,
19591         
19592         /**
19593          * The animation duration in seconds - 
19594          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19595          * @type Number
19596          */
19597         animDuration: .4,
19598         
19599         /**
19600          * Manually trigger a cache refresh.
19601          */
19602         refreshCache : function(){
19603             for(var id in els){
19604                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19605                     els[id]._region = els[id].getRegion();
19606                 }
19607             }
19608         }
19609     };
19610 }();/*
19611  * Based on:
19612  * Ext JS Library 1.1.1
19613  * Copyright(c) 2006-2007, Ext JS, LLC.
19614  *
19615  * Originally Released Under LGPL - original licence link has changed is not relivant.
19616  *
19617  * Fork - LGPL
19618  * <script type="text/javascript">
19619  */
19620  
19621
19622 /**
19623  * @class Roo.dd.Registry
19624  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19625  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19626  * @singleton
19627  */
19628 Roo.dd.Registry = function(){
19629     var elements = {}; 
19630     var handles = {}; 
19631     var autoIdSeed = 0;
19632
19633     var getId = function(el, autogen){
19634         if(typeof el == "string"){
19635             return el;
19636         }
19637         var id = el.id;
19638         if(!id && autogen !== false){
19639             id = "roodd-" + (++autoIdSeed);
19640             el.id = id;
19641         }
19642         return id;
19643     };
19644     
19645     return {
19646     /**
19647      * Register a drag drop element
19648      * @param {String|HTMLElement} element The id or DOM node to register
19649      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19650      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19651      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19652      * populated in the data object (if applicable):
19653      * <pre>
19654 Value      Description<br />
19655 ---------  ------------------------------------------<br />
19656 handles    Array of DOM nodes that trigger dragging<br />
19657            for the element being registered<br />
19658 isHandle   True if the element passed in triggers<br />
19659            dragging itself, else false
19660 </pre>
19661      */
19662         register : function(el, data){
19663             data = data || {};
19664             if(typeof el == "string"){
19665                 el = document.getElementById(el);
19666             }
19667             data.ddel = el;
19668             elements[getId(el)] = data;
19669             if(data.isHandle !== false){
19670                 handles[data.ddel.id] = data;
19671             }
19672             if(data.handles){
19673                 var hs = data.handles;
19674                 for(var i = 0, len = hs.length; i < len; i++){
19675                         handles[getId(hs[i])] = data;
19676                 }
19677             }
19678         },
19679
19680     /**
19681      * Unregister a drag drop element
19682      * @param {String|HTMLElement}  element The id or DOM node to unregister
19683      */
19684         unregister : function(el){
19685             var id = getId(el, false);
19686             var data = elements[id];
19687             if(data){
19688                 delete elements[id];
19689                 if(data.handles){
19690                     var hs = data.handles;
19691                     for(var i = 0, len = hs.length; i < len; i++){
19692                         delete handles[getId(hs[i], false)];
19693                     }
19694                 }
19695             }
19696         },
19697
19698     /**
19699      * Returns the handle registered for a DOM Node by id
19700      * @param {String|HTMLElement} id The DOM node or id to look up
19701      * @return {Object} handle The custom handle data
19702      */
19703         getHandle : function(id){
19704             if(typeof id != "string"){ // must be element?
19705                 id = id.id;
19706             }
19707             return handles[id];
19708         },
19709
19710     /**
19711      * Returns the handle that is registered for the DOM node that is the target of the event
19712      * @param {Event} e The event
19713      * @return {Object} handle The custom handle data
19714      */
19715         getHandleFromEvent : function(e){
19716             var t = Roo.lib.Event.getTarget(e);
19717             return t ? handles[t.id] : null;
19718         },
19719
19720     /**
19721      * Returns a custom data object that is registered for a DOM node by id
19722      * @param {String|HTMLElement} id The DOM node or id to look up
19723      * @return {Object} data The custom data
19724      */
19725         getTarget : function(id){
19726             if(typeof id != "string"){ // must be element?
19727                 id = id.id;
19728             }
19729             return elements[id];
19730         },
19731
19732     /**
19733      * Returns a custom data object that is registered for the DOM node that is the target of the event
19734      * @param {Event} e The event
19735      * @return {Object} data The custom data
19736      */
19737         getTargetFromEvent : function(e){
19738             var t = Roo.lib.Event.getTarget(e);
19739             return t ? elements[t.id] || handles[t.id] : null;
19740         }
19741     };
19742 }();/*
19743  * Based on:
19744  * Ext JS Library 1.1.1
19745  * Copyright(c) 2006-2007, Ext JS, LLC.
19746  *
19747  * Originally Released Under LGPL - original licence link has changed is not relivant.
19748  *
19749  * Fork - LGPL
19750  * <script type="text/javascript">
19751  */
19752  
19753
19754 /**
19755  * @class Roo.dd.StatusProxy
19756  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19757  * default drag proxy used by all Roo.dd components.
19758  * @constructor
19759  * @param {Object} config
19760  */
19761 Roo.dd.StatusProxy = function(config){
19762     Roo.apply(this, config);
19763     this.id = this.id || Roo.id();
19764     this.el = new Roo.Layer({
19765         dh: {
19766             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19767                 {tag: "div", cls: "x-dd-drop-icon"},
19768                 {tag: "div", cls: "x-dd-drag-ghost"}
19769             ]
19770         }, 
19771         shadow: !config || config.shadow !== false
19772     });
19773     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19774     this.dropStatus = this.dropNotAllowed;
19775 };
19776
19777 Roo.dd.StatusProxy.prototype = {
19778     /**
19779      * @cfg {String} dropAllowed
19780      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19781      */
19782     dropAllowed : "x-dd-drop-ok",
19783     /**
19784      * @cfg {String} dropNotAllowed
19785      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19786      */
19787     dropNotAllowed : "x-dd-drop-nodrop",
19788
19789     /**
19790      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19791      * over the current target element.
19792      * @param {String} cssClass The css class for the new drop status indicator image
19793      */
19794     setStatus : function(cssClass){
19795         cssClass = cssClass || this.dropNotAllowed;
19796         if(this.dropStatus != cssClass){
19797             this.el.replaceClass(this.dropStatus, cssClass);
19798             this.dropStatus = cssClass;
19799         }
19800     },
19801
19802     /**
19803      * Resets the status indicator to the default dropNotAllowed value
19804      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19805      */
19806     reset : function(clearGhost){
19807         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19808         this.dropStatus = this.dropNotAllowed;
19809         if(clearGhost){
19810             this.ghost.update("");
19811         }
19812     },
19813
19814     /**
19815      * Updates the contents of the ghost element
19816      * @param {String} html The html that will replace the current innerHTML of the ghost element
19817      */
19818     update : function(html){
19819         if(typeof html == "string"){
19820             this.ghost.update(html);
19821         }else{
19822             this.ghost.update("");
19823             html.style.margin = "0";
19824             this.ghost.dom.appendChild(html);
19825         }
19826         // ensure float = none set?? cant remember why though.
19827         var el = this.ghost.dom.firstChild;
19828                 if(el){
19829                         Roo.fly(el).setStyle('float', 'none');
19830                 }
19831     },
19832     
19833     /**
19834      * Returns the underlying proxy {@link Roo.Layer}
19835      * @return {Roo.Layer} el
19836     */
19837     getEl : function(){
19838         return this.el;
19839     },
19840
19841     /**
19842      * Returns the ghost element
19843      * @return {Roo.Element} el
19844      */
19845     getGhost : function(){
19846         return this.ghost;
19847     },
19848
19849     /**
19850      * Hides the proxy
19851      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19852      */
19853     hide : function(clear){
19854         this.el.hide();
19855         if(clear){
19856             this.reset(true);
19857         }
19858     },
19859
19860     /**
19861      * Stops the repair animation if it's currently running
19862      */
19863     stop : function(){
19864         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19865             this.anim.stop();
19866         }
19867     },
19868
19869     /**
19870      * Displays this proxy
19871      */
19872     show : function(){
19873         this.el.show();
19874     },
19875
19876     /**
19877      * Force the Layer to sync its shadow and shim positions to the element
19878      */
19879     sync : function(){
19880         this.el.sync();
19881     },
19882
19883     /**
19884      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19885      * invalid drop operation by the item being dragged.
19886      * @param {Array} xy The XY position of the element ([x, y])
19887      * @param {Function} callback The function to call after the repair is complete
19888      * @param {Object} scope The scope in which to execute the callback
19889      */
19890     repair : function(xy, callback, scope){
19891         this.callback = callback;
19892         this.scope = scope;
19893         if(xy && this.animRepair !== false){
19894             this.el.addClass("x-dd-drag-repair");
19895             this.el.hideUnders(true);
19896             this.anim = this.el.shift({
19897                 duration: this.repairDuration || .5,
19898                 easing: 'easeOut',
19899                 xy: xy,
19900                 stopFx: true,
19901                 callback: this.afterRepair,
19902                 scope: this
19903             });
19904         }else{
19905             this.afterRepair();
19906         }
19907     },
19908
19909     // private
19910     afterRepair : function(){
19911         this.hide(true);
19912         if(typeof this.callback == "function"){
19913             this.callback.call(this.scope || this);
19914         }
19915         this.callback = null;
19916         this.scope = null;
19917     }
19918 };/*
19919  * Based on:
19920  * Ext JS Library 1.1.1
19921  * Copyright(c) 2006-2007, Ext JS, LLC.
19922  *
19923  * Originally Released Under LGPL - original licence link has changed is not relivant.
19924  *
19925  * Fork - LGPL
19926  * <script type="text/javascript">
19927  */
19928
19929 /**
19930  * @class Roo.dd.DragSource
19931  * @extends Roo.dd.DDProxy
19932  * A simple class that provides the basic implementation needed to make any element draggable.
19933  * @constructor
19934  * @param {String/HTMLElement/Element} el The container element
19935  * @param {Object} config
19936  */
19937 Roo.dd.DragSource = function(el, config){
19938     this.el = Roo.get(el);
19939     this.dragData = {};
19940     
19941     Roo.apply(this, config);
19942     
19943     if(!this.proxy){
19944         this.proxy = new Roo.dd.StatusProxy();
19945     }
19946
19947     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19948           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19949     
19950     this.dragging = false;
19951 };
19952
19953 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19954     /**
19955      * @cfg {String} dropAllowed
19956      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19957      */
19958     dropAllowed : "x-dd-drop-ok",
19959     /**
19960      * @cfg {String} dropNotAllowed
19961      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19962      */
19963     dropNotAllowed : "x-dd-drop-nodrop",
19964
19965     /**
19966      * Returns the data object associated with this drag source
19967      * @return {Object} data An object containing arbitrary data
19968      */
19969     getDragData : function(e){
19970         return this.dragData;
19971     },
19972
19973     // private
19974     onDragEnter : function(e, id){
19975         var target = Roo.dd.DragDropMgr.getDDById(id);
19976         this.cachedTarget = target;
19977         if(this.beforeDragEnter(target, e, id) !== false){
19978             if(target.isNotifyTarget){
19979                 var status = target.notifyEnter(this, e, this.dragData);
19980                 this.proxy.setStatus(status);
19981             }else{
19982                 this.proxy.setStatus(this.dropAllowed);
19983             }
19984             
19985             if(this.afterDragEnter){
19986                 /**
19987                  * An empty function by default, but provided so that you can perform a custom action
19988                  * when the dragged item enters the drop target by providing an implementation.
19989                  * @param {Roo.dd.DragDrop} target The drop target
19990                  * @param {Event} e The event object
19991                  * @param {String} id The id of the dragged element
19992                  * @method afterDragEnter
19993                  */
19994                 this.afterDragEnter(target, e, id);
19995             }
19996         }
19997     },
19998
19999     /**
20000      * An empty function by default, but provided so that you can perform a custom action
20001      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20002      * @param {Roo.dd.DragDrop} target The drop target
20003      * @param {Event} e The event object
20004      * @param {String} id The id of the dragged element
20005      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20006      */
20007     beforeDragEnter : function(target, e, id){
20008         return true;
20009     },
20010
20011     // private
20012     alignElWithMouse: function() {
20013         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20014         this.proxy.sync();
20015     },
20016
20017     // private
20018     onDragOver : function(e, id){
20019         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20020         if(this.beforeDragOver(target, e, id) !== false){
20021             if(target.isNotifyTarget){
20022                 var status = target.notifyOver(this, e, this.dragData);
20023                 this.proxy.setStatus(status);
20024             }
20025
20026             if(this.afterDragOver){
20027                 /**
20028                  * An empty function by default, but provided so that you can perform a custom action
20029                  * while the dragged item is over the drop target by providing an implementation.
20030                  * @param {Roo.dd.DragDrop} target The drop target
20031                  * @param {Event} e The event object
20032                  * @param {String} id The id of the dragged element
20033                  * @method afterDragOver
20034                  */
20035                 this.afterDragOver(target, e, id);
20036             }
20037         }
20038     },
20039
20040     /**
20041      * An empty function by default, but provided so that you can perform a custom action
20042      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20043      * @param {Roo.dd.DragDrop} target The drop target
20044      * @param {Event} e The event object
20045      * @param {String} id The id of the dragged element
20046      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20047      */
20048     beforeDragOver : function(target, e, id){
20049         return true;
20050     },
20051
20052     // private
20053     onDragOut : function(e, id){
20054         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20055         if(this.beforeDragOut(target, e, id) !== false){
20056             if(target.isNotifyTarget){
20057                 target.notifyOut(this, e, this.dragData);
20058             }
20059             this.proxy.reset();
20060             if(this.afterDragOut){
20061                 /**
20062                  * An empty function by default, but provided so that you can perform a custom action
20063                  * after the dragged item is dragged out of the target without dropping.
20064                  * @param {Roo.dd.DragDrop} target The drop target
20065                  * @param {Event} e The event object
20066                  * @param {String} id The id of the dragged element
20067                  * @method afterDragOut
20068                  */
20069                 this.afterDragOut(target, e, id);
20070             }
20071         }
20072         this.cachedTarget = null;
20073     },
20074
20075     /**
20076      * An empty function by default, but provided so that you can perform a custom action before the dragged
20077      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20078      * @param {Roo.dd.DragDrop} target The drop target
20079      * @param {Event} e The event object
20080      * @param {String} id The id of the dragged element
20081      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20082      */
20083     beforeDragOut : function(target, e, id){
20084         return true;
20085     },
20086     
20087     // private
20088     onDragDrop : function(e, id){
20089         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20090         if(this.beforeDragDrop(target, e, id) !== false){
20091             if(target.isNotifyTarget){
20092                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20093                     this.onValidDrop(target, e, id);
20094                 }else{
20095                     this.onInvalidDrop(target, e, id);
20096                 }
20097             }else{
20098                 this.onValidDrop(target, e, id);
20099             }
20100             
20101             if(this.afterDragDrop){
20102                 /**
20103                  * An empty function by default, but provided so that you can perform a custom action
20104                  * after a valid drag drop has occurred by providing an implementation.
20105                  * @param {Roo.dd.DragDrop} target The drop target
20106                  * @param {Event} e The event object
20107                  * @param {String} id The id of the dropped element
20108                  * @method afterDragDrop
20109                  */
20110                 this.afterDragDrop(target, e, id);
20111             }
20112         }
20113         delete this.cachedTarget;
20114     },
20115
20116     /**
20117      * An empty function by default, but provided so that you can perform a custom action before the dragged
20118      * item is dropped onto the target and optionally cancel the onDragDrop.
20119      * @param {Roo.dd.DragDrop} target The drop target
20120      * @param {Event} e The event object
20121      * @param {String} id The id of the dragged element
20122      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20123      */
20124     beforeDragDrop : function(target, e, id){
20125         return true;
20126     },
20127
20128     // private
20129     onValidDrop : function(target, e, id){
20130         this.hideProxy();
20131         if(this.afterValidDrop){
20132             /**
20133              * An empty function by default, but provided so that you can perform a custom action
20134              * after a valid drop has occurred by providing an implementation.
20135              * @param {Object} target The target DD 
20136              * @param {Event} e The event object
20137              * @param {String} id The id of the dropped element
20138              * @method afterInvalidDrop
20139              */
20140             this.afterValidDrop(target, e, id);
20141         }
20142     },
20143
20144     // private
20145     getRepairXY : function(e, data){
20146         return this.el.getXY();  
20147     },
20148
20149     // private
20150     onInvalidDrop : function(target, e, id){
20151         this.beforeInvalidDrop(target, e, id);
20152         if(this.cachedTarget){
20153             if(this.cachedTarget.isNotifyTarget){
20154                 this.cachedTarget.notifyOut(this, e, this.dragData);
20155             }
20156             this.cacheTarget = null;
20157         }
20158         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20159
20160         if(this.afterInvalidDrop){
20161             /**
20162              * An empty function by default, but provided so that you can perform a custom action
20163              * after an invalid drop has occurred by providing an implementation.
20164              * @param {Event} e The event object
20165              * @param {String} id The id of the dropped element
20166              * @method afterInvalidDrop
20167              */
20168             this.afterInvalidDrop(e, id);
20169         }
20170     },
20171
20172     // private
20173     afterRepair : function(){
20174         if(Roo.enableFx){
20175             this.el.highlight(this.hlColor || "c3daf9");
20176         }
20177         this.dragging = false;
20178     },
20179
20180     /**
20181      * An empty function by default, but provided so that you can perform a custom action after an invalid
20182      * drop has occurred.
20183      * @param {Roo.dd.DragDrop} target The drop target
20184      * @param {Event} e The event object
20185      * @param {String} id The id of the dragged element
20186      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20187      */
20188     beforeInvalidDrop : function(target, e, id){
20189         return true;
20190     },
20191
20192     // private
20193     handleMouseDown : function(e){
20194         if(this.dragging) {
20195             return;
20196         }
20197         var data = this.getDragData(e);
20198         if(data && this.onBeforeDrag(data, e) !== false){
20199             this.dragData = data;
20200             this.proxy.stop();
20201             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20202         } 
20203     },
20204
20205     /**
20206      * An empty function by default, but provided so that you can perform a custom action before the initial
20207      * drag event begins and optionally cancel it.
20208      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20209      * @param {Event} e The event object
20210      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20211      */
20212     onBeforeDrag : function(data, e){
20213         return true;
20214     },
20215
20216     /**
20217      * An empty function by default, but provided so that you can perform a custom action once the initial
20218      * drag event has begun.  The drag cannot be canceled from this function.
20219      * @param {Number} x The x position of the click on the dragged object
20220      * @param {Number} y The y position of the click on the dragged object
20221      */
20222     onStartDrag : Roo.emptyFn,
20223
20224     // private - YUI override
20225     startDrag : function(x, y){
20226         this.proxy.reset();
20227         this.dragging = true;
20228         this.proxy.update("");
20229         this.onInitDrag(x, y);
20230         this.proxy.show();
20231     },
20232
20233     // private
20234     onInitDrag : function(x, y){
20235         var clone = this.el.dom.cloneNode(true);
20236         clone.id = Roo.id(); // prevent duplicate ids
20237         this.proxy.update(clone);
20238         this.onStartDrag(x, y);
20239         return true;
20240     },
20241
20242     /**
20243      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20244      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20245      */
20246     getProxy : function(){
20247         return this.proxy;  
20248     },
20249
20250     /**
20251      * Hides the drag source's {@link Roo.dd.StatusProxy}
20252      */
20253     hideProxy : function(){
20254         this.proxy.hide();  
20255         this.proxy.reset(true);
20256         this.dragging = false;
20257     },
20258
20259     // private
20260     triggerCacheRefresh : function(){
20261         Roo.dd.DDM.refreshCache(this.groups);
20262     },
20263
20264     // private - override to prevent hiding
20265     b4EndDrag: function(e) {
20266     },
20267
20268     // private - override to prevent moving
20269     endDrag : function(e){
20270         this.onEndDrag(this.dragData, e);
20271     },
20272
20273     // private
20274     onEndDrag : function(data, e){
20275     },
20276     
20277     // private - pin to cursor
20278     autoOffset : function(x, y) {
20279         this.setDelta(-12, -20);
20280     }    
20281 });/*
20282  * Based on:
20283  * Ext JS Library 1.1.1
20284  * Copyright(c) 2006-2007, Ext JS, LLC.
20285  *
20286  * Originally Released Under LGPL - original licence link has changed is not relivant.
20287  *
20288  * Fork - LGPL
20289  * <script type="text/javascript">
20290  */
20291
20292
20293 /**
20294  * @class Roo.dd.DropTarget
20295  * @extends Roo.dd.DDTarget
20296  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20297  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20298  * @constructor
20299  * @param {String/HTMLElement/Element} el The container element
20300  * @param {Object} config
20301  */
20302 Roo.dd.DropTarget = function(el, config){
20303     this.el = Roo.get(el);
20304     
20305     var listeners = false; ;
20306     if (config && config.listeners) {
20307         listeners= config.listeners;
20308         delete config.listeners;
20309     }
20310     Roo.apply(this, config);
20311     
20312     if(this.containerScroll){
20313         Roo.dd.ScrollManager.register(this.el);
20314     }
20315     this.addEvents( {
20316          /**
20317          * @scope Roo.dd.DropTarget
20318          */
20319          
20320          /**
20321          * @event enter
20322          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20323          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20324          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20325          * 
20326          * IMPORTANT : it should set this.overClass and this.dropAllowed
20327          * 
20328          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20329          * @param {Event} e The event
20330          * @param {Object} data An object containing arbitrary data supplied by the drag source
20331          */
20332         "enter" : true,
20333         
20334          /**
20335          * @event over
20336          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20337          * This method will be called on every mouse movement while the drag source is over the drop target.
20338          * This default implementation simply returns the dropAllowed config value.
20339          * 
20340          * IMPORTANT : it should set this.dropAllowed
20341          * 
20342          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20343          * @param {Event} e The event
20344          * @param {Object} data An object containing arbitrary data supplied by the drag source
20345          
20346          */
20347         "over" : true,
20348         /**
20349          * @event out
20350          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20351          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20352          * overClass (if any) from the drop element.
20353          * 
20354          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20355          * @param {Event} e The event
20356          * @param {Object} data An object containing arbitrary data supplied by the drag source
20357          */
20358          "out" : true,
20359          
20360         /**
20361          * @event drop
20362          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20363          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20364          * implementation that does something to process the drop event and returns true so that the drag source's
20365          * repair action does not run.
20366          * 
20367          * IMPORTANT : it should set this.success
20368          * 
20369          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20370          * @param {Event} e The event
20371          * @param {Object} data An object containing arbitrary data supplied by the drag source
20372         */
20373          "drop" : true
20374     });
20375             
20376      
20377     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20378         this.el.dom, 
20379         this.ddGroup || this.group,
20380         {
20381             isTarget: true,
20382             listeners : listeners || {} 
20383            
20384         
20385         }
20386     );
20387
20388 };
20389
20390 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20391     /**
20392      * @cfg {String} overClass
20393      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20394      */
20395      /**
20396      * @cfg {String} ddGroup
20397      * The drag drop group to handle drop events for
20398      */
20399      
20400     /**
20401      * @cfg {String} dropAllowed
20402      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20403      */
20404     dropAllowed : "x-dd-drop-ok",
20405     /**
20406      * @cfg {String} dropNotAllowed
20407      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20408      */
20409     dropNotAllowed : "x-dd-drop-nodrop",
20410     /**
20411      * @cfg {boolean} success
20412      * set this after drop listener.. 
20413      */
20414     success : false,
20415     /**
20416      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20417      * if the drop point is valid for over/enter..
20418      */
20419     valid : false,
20420     // private
20421     isTarget : true,
20422
20423     // private
20424     isNotifyTarget : true,
20425     
20426     /**
20427      * @hide
20428      */
20429     notifyEnter : function(dd, e, data)
20430     {
20431         this.valid = true;
20432         this.fireEvent('enter', dd, e, data);
20433         if(this.overClass){
20434             this.el.addClass(this.overClass);
20435         }
20436         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20437             this.valid ? this.dropAllowed : this.dropNotAllowed
20438         );
20439     },
20440
20441     /**
20442      * @hide
20443      */
20444     notifyOver : function(dd, e, data)
20445     {
20446         this.valid = true;
20447         this.fireEvent('over', dd, e, data);
20448         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20449             this.valid ? this.dropAllowed : this.dropNotAllowed
20450         );
20451     },
20452
20453     /**
20454      * @hide
20455      */
20456     notifyOut : function(dd, e, data)
20457     {
20458         this.fireEvent('out', dd, e, data);
20459         if(this.overClass){
20460             this.el.removeClass(this.overClass);
20461         }
20462     },
20463
20464     /**
20465      * @hide
20466      */
20467     notifyDrop : function(dd, e, data)
20468     {
20469         this.success = false;
20470         this.fireEvent('drop', dd, e, data);
20471         return this.success;
20472     }
20473 });/*
20474  * Based on:
20475  * Ext JS Library 1.1.1
20476  * Copyright(c) 2006-2007, Ext JS, LLC.
20477  *
20478  * Originally Released Under LGPL - original licence link has changed is not relivant.
20479  *
20480  * Fork - LGPL
20481  * <script type="text/javascript">
20482  */
20483
20484
20485 /**
20486  * @class Roo.dd.DragZone
20487  * @extends Roo.dd.DragSource
20488  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20489  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20490  * @constructor
20491  * @param {String/HTMLElement/Element} el The container element
20492  * @param {Object} config
20493  */
20494 Roo.dd.DragZone = function(el, config){
20495     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20496     if(this.containerScroll){
20497         Roo.dd.ScrollManager.register(this.el);
20498     }
20499 };
20500
20501 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20502     /**
20503      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20504      * for auto scrolling during drag operations.
20505      */
20506     /**
20507      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20508      * method after a failed drop (defaults to "c3daf9" - light blue)
20509      */
20510
20511     /**
20512      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20513      * for a valid target to drag based on the mouse down. Override this method
20514      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20515      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20516      * @param {EventObject} e The mouse down event
20517      * @return {Object} The dragData
20518      */
20519     getDragData : function(e){
20520         return Roo.dd.Registry.getHandleFromEvent(e);
20521     },
20522     
20523     /**
20524      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20525      * this.dragData.ddel
20526      * @param {Number} x The x position of the click on the dragged object
20527      * @param {Number} y The y position of the click on the dragged object
20528      * @return {Boolean} true to continue the drag, false to cancel
20529      */
20530     onInitDrag : function(x, y){
20531         this.proxy.update(this.dragData.ddel.cloneNode(true));
20532         this.onStartDrag(x, y);
20533         return true;
20534     },
20535     
20536     /**
20537      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20538      */
20539     afterRepair : function(){
20540         if(Roo.enableFx){
20541             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20542         }
20543         this.dragging = false;
20544     },
20545
20546     /**
20547      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20548      * the XY of this.dragData.ddel
20549      * @param {EventObject} e The mouse up event
20550      * @return {Array} The xy location (e.g. [100, 200])
20551      */
20552     getRepairXY : function(e){
20553         return Roo.Element.fly(this.dragData.ddel).getXY();  
20554     }
20555 });/*
20556  * Based on:
20557  * Ext JS Library 1.1.1
20558  * Copyright(c) 2006-2007, Ext JS, LLC.
20559  *
20560  * Originally Released Under LGPL - original licence link has changed is not relivant.
20561  *
20562  * Fork - LGPL
20563  * <script type="text/javascript">
20564  */
20565 /**
20566  * @class Roo.dd.DropZone
20567  * @extends Roo.dd.DropTarget
20568  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20569  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20570  * @constructor
20571  * @param {String/HTMLElement/Element} el The container element
20572  * @param {Object} config
20573  */
20574 Roo.dd.DropZone = function(el, config){
20575     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20576 };
20577
20578 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20579     /**
20580      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20581      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20582      * provide your own custom lookup.
20583      * @param {Event} e The event
20584      * @return {Object} data The custom data
20585      */
20586     getTargetFromEvent : function(e){
20587         return Roo.dd.Registry.getTargetFromEvent(e);
20588     },
20589
20590     /**
20591      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20592      * that it has registered.  This method has no default implementation and should be overridden to provide
20593      * node-specific processing if necessary.
20594      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20595      * {@link #getTargetFromEvent} for this node)
20596      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20597      * @param {Event} e The event
20598      * @param {Object} data An object containing arbitrary data supplied by the drag source
20599      */
20600     onNodeEnter : function(n, dd, e, data){
20601         
20602     },
20603
20604     /**
20605      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20606      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20607      * overridden to provide the proper feedback.
20608      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20609      * {@link #getTargetFromEvent} for this node)
20610      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20611      * @param {Event} e The event
20612      * @param {Object} data An object containing arbitrary data supplied by the drag source
20613      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20614      * underlying {@link Roo.dd.StatusProxy} can be updated
20615      */
20616     onNodeOver : function(n, dd, e, data){
20617         return this.dropAllowed;
20618     },
20619
20620     /**
20621      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20622      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20623      * node-specific processing if necessary.
20624      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20625      * {@link #getTargetFromEvent} for this node)
20626      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20627      * @param {Event} e The event
20628      * @param {Object} data An object containing arbitrary data supplied by the drag source
20629      */
20630     onNodeOut : function(n, dd, e, data){
20631         
20632     },
20633
20634     /**
20635      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20636      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20637      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20638      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20639      * {@link #getTargetFromEvent} for this node)
20640      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20641      * @param {Event} e The event
20642      * @param {Object} data An object containing arbitrary data supplied by the drag source
20643      * @return {Boolean} True if the drop was valid, else false
20644      */
20645     onNodeDrop : function(n, dd, e, data){
20646         return false;
20647     },
20648
20649     /**
20650      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20651      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20652      * it should be overridden to provide the proper feedback if necessary.
20653      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20654      * @param {Event} e The event
20655      * @param {Object} data An object containing arbitrary data supplied by the drag source
20656      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20657      * underlying {@link Roo.dd.StatusProxy} can be updated
20658      */
20659     onContainerOver : function(dd, e, data){
20660         return this.dropNotAllowed;
20661     },
20662
20663     /**
20664      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20665      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20666      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20667      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20668      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20669      * @param {Event} e The event
20670      * @param {Object} data An object containing arbitrary data supplied by the drag source
20671      * @return {Boolean} True if the drop was valid, else false
20672      */
20673     onContainerDrop : function(dd, e, data){
20674         return false;
20675     },
20676
20677     /**
20678      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20679      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20680      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20681      * you should override this method and provide a custom implementation.
20682      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20683      * @param {Event} e The event
20684      * @param {Object} data An object containing arbitrary data supplied by the drag source
20685      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20686      * underlying {@link Roo.dd.StatusProxy} can be updated
20687      */
20688     notifyEnter : function(dd, e, data){
20689         return this.dropNotAllowed;
20690     },
20691
20692     /**
20693      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20694      * This method will be called on every mouse movement while the drag source is over the drop zone.
20695      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20696      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20697      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20698      * registered node, it will call {@link #onContainerOver}.
20699      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20700      * @param {Event} e The event
20701      * @param {Object} data An object containing arbitrary data supplied by the drag source
20702      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20703      * underlying {@link Roo.dd.StatusProxy} can be updated
20704      */
20705     notifyOver : function(dd, e, data){
20706         var n = this.getTargetFromEvent(e);
20707         if(!n){ // not over valid drop target
20708             if(this.lastOverNode){
20709                 this.onNodeOut(this.lastOverNode, dd, e, data);
20710                 this.lastOverNode = null;
20711             }
20712             return this.onContainerOver(dd, e, data);
20713         }
20714         if(this.lastOverNode != n){
20715             if(this.lastOverNode){
20716                 this.onNodeOut(this.lastOverNode, dd, e, data);
20717             }
20718             this.onNodeEnter(n, dd, e, data);
20719             this.lastOverNode = n;
20720         }
20721         return this.onNodeOver(n, dd, e, data);
20722     },
20723
20724     /**
20725      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20726      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20727      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20728      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20729      * @param {Event} e The event
20730      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20731      */
20732     notifyOut : function(dd, e, data){
20733         if(this.lastOverNode){
20734             this.onNodeOut(this.lastOverNode, dd, e, data);
20735             this.lastOverNode = null;
20736         }
20737     },
20738
20739     /**
20740      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20741      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20742      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20743      * otherwise it will call {@link #onContainerDrop}.
20744      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20745      * @param {Event} e The event
20746      * @param {Object} data An object containing arbitrary data supplied by the drag source
20747      * @return {Boolean} True if the drop was valid, else false
20748      */
20749     notifyDrop : function(dd, e, data){
20750         if(this.lastOverNode){
20751             this.onNodeOut(this.lastOverNode, dd, e, data);
20752             this.lastOverNode = null;
20753         }
20754         var n = this.getTargetFromEvent(e);
20755         return n ?
20756             this.onNodeDrop(n, dd, e, data) :
20757             this.onContainerDrop(dd, e, data);
20758     },
20759
20760     // private
20761     triggerCacheRefresh : function(){
20762         Roo.dd.DDM.refreshCache(this.groups);
20763     }  
20764 });/*
20765  * Based on:
20766  * Ext JS Library 1.1.1
20767  * Copyright(c) 2006-2007, Ext JS, LLC.
20768  *
20769  * Originally Released Under LGPL - original licence link has changed is not relivant.
20770  *
20771  * Fork - LGPL
20772  * <script type="text/javascript">
20773  */
20774
20775
20776 /**
20777  * @class Roo.data.SortTypes
20778  * @singleton
20779  * Defines the default sorting (casting?) comparison functions used when sorting data.
20780  */
20781 Roo.data.SortTypes = {
20782     /**
20783      * Default sort that does nothing
20784      * @param {Mixed} s The value being converted
20785      * @return {Mixed} The comparison value
20786      */
20787     none : function(s){
20788         return s;
20789     },
20790     
20791     /**
20792      * The regular expression used to strip tags
20793      * @type {RegExp}
20794      * @property
20795      */
20796     stripTagsRE : /<\/?[^>]+>/gi,
20797     
20798     /**
20799      * Strips all HTML tags to sort on text only
20800      * @param {Mixed} s The value being converted
20801      * @return {String} The comparison value
20802      */
20803     asText : function(s){
20804         return String(s).replace(this.stripTagsRE, "");
20805     },
20806     
20807     /**
20808      * Strips all HTML tags to sort on text only - Case insensitive
20809      * @param {Mixed} s The value being converted
20810      * @return {String} The comparison value
20811      */
20812     asUCText : function(s){
20813         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20814     },
20815     
20816     /**
20817      * Case insensitive string
20818      * @param {Mixed} s The value being converted
20819      * @return {String} The comparison value
20820      */
20821     asUCString : function(s) {
20822         return String(s).toUpperCase();
20823     },
20824     
20825     /**
20826      * Date sorting
20827      * @param {Mixed} s The value being converted
20828      * @return {Number} The comparison value
20829      */
20830     asDate : function(s) {
20831         if(!s){
20832             return 0;
20833         }
20834         if(s instanceof Date){
20835             return s.getTime();
20836         }
20837         return Date.parse(String(s));
20838     },
20839     
20840     /**
20841      * Float sorting
20842      * @param {Mixed} s The value being converted
20843      * @return {Float} The comparison value
20844      */
20845     asFloat : function(s) {
20846         var val = parseFloat(String(s).replace(/,/g, ""));
20847         if(isNaN(val)) val = 0;
20848         return val;
20849     },
20850     
20851     /**
20852      * Integer sorting
20853      * @param {Mixed} s The value being converted
20854      * @return {Number} The comparison value
20855      */
20856     asInt : function(s) {
20857         var val = parseInt(String(s).replace(/,/g, ""));
20858         if(isNaN(val)) val = 0;
20859         return val;
20860     }
20861 };/*
20862  * Based on:
20863  * Ext JS Library 1.1.1
20864  * Copyright(c) 2006-2007, Ext JS, LLC.
20865  *
20866  * Originally Released Under LGPL - original licence link has changed is not relivant.
20867  *
20868  * Fork - LGPL
20869  * <script type="text/javascript">
20870  */
20871
20872 /**
20873 * @class Roo.data.Record
20874  * Instances of this class encapsulate both record <em>definition</em> information, and record
20875  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20876  * to access Records cached in an {@link Roo.data.Store} object.<br>
20877  * <p>
20878  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20879  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20880  * objects.<br>
20881  * <p>
20882  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20883  * @constructor
20884  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20885  * {@link #create}. The parameters are the same.
20886  * @param {Array} data An associative Array of data values keyed by the field name.
20887  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20888  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20889  * not specified an integer id is generated.
20890  */
20891 Roo.data.Record = function(data, id){
20892     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20893     this.data = data;
20894 };
20895
20896 /**
20897  * Generate a constructor for a specific record layout.
20898  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20899  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20900  * Each field definition object may contain the following properties: <ul>
20901  * <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,
20902  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20903  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20904  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20905  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20906  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20907  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20908  * this may be omitted.</p></li>
20909  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20910  * <ul><li>auto (Default, implies no conversion)</li>
20911  * <li>string</li>
20912  * <li>int</li>
20913  * <li>float</li>
20914  * <li>boolean</li>
20915  * <li>date</li></ul></p></li>
20916  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20917  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20918  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20919  * by the Reader into an object that will be stored in the Record. It is passed the
20920  * following parameters:<ul>
20921  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20922  * </ul></p></li>
20923  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20924  * </ul>
20925  * <br>usage:<br><pre><code>
20926 var TopicRecord = Roo.data.Record.create(
20927     {name: 'title', mapping: 'topic_title'},
20928     {name: 'author', mapping: 'username'},
20929     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20930     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20931     {name: 'lastPoster', mapping: 'user2'},
20932     {name: 'excerpt', mapping: 'post_text'}
20933 );
20934
20935 var myNewRecord = new TopicRecord({
20936     title: 'Do my job please',
20937     author: 'noobie',
20938     totalPosts: 1,
20939     lastPost: new Date(),
20940     lastPoster: 'Animal',
20941     excerpt: 'No way dude!'
20942 });
20943 myStore.add(myNewRecord);
20944 </code></pre>
20945  * @method create
20946  * @static
20947  */
20948 Roo.data.Record.create = function(o){
20949     var f = function(){
20950         f.superclass.constructor.apply(this, arguments);
20951     };
20952     Roo.extend(f, Roo.data.Record);
20953     var p = f.prototype;
20954     p.fields = new Roo.util.MixedCollection(false, function(field){
20955         return field.name;
20956     });
20957     for(var i = 0, len = o.length; i < len; i++){
20958         p.fields.add(new Roo.data.Field(o[i]));
20959     }
20960     f.getField = function(name){
20961         return p.fields.get(name);  
20962     };
20963     return f;
20964 };
20965
20966 Roo.data.Record.AUTO_ID = 1000;
20967 Roo.data.Record.EDIT = 'edit';
20968 Roo.data.Record.REJECT = 'reject';
20969 Roo.data.Record.COMMIT = 'commit';
20970
20971 Roo.data.Record.prototype = {
20972     /**
20973      * Readonly flag - true if this record has been modified.
20974      * @type Boolean
20975      */
20976     dirty : false,
20977     editing : false,
20978     error: null,
20979     modified: null,
20980
20981     // private
20982     join : function(store){
20983         this.store = store;
20984     },
20985
20986     /**
20987      * Set the named field to the specified value.
20988      * @param {String} name The name of the field to set.
20989      * @param {Object} value The value to set the field to.
20990      */
20991     set : function(name, value){
20992         if(this.data[name] == value){
20993             return;
20994         }
20995         this.dirty = true;
20996         if(!this.modified){
20997             this.modified = {};
20998         }
20999         if(typeof this.modified[name] == 'undefined'){
21000             this.modified[name] = this.data[name];
21001         }
21002         this.data[name] = value;
21003         if(!this.editing && this.store){
21004             this.store.afterEdit(this);
21005         }       
21006     },
21007
21008     /**
21009      * Get the value of the named field.
21010      * @param {String} name The name of the field to get the value of.
21011      * @return {Object} The value of the field.
21012      */
21013     get : function(name){
21014         return this.data[name]; 
21015     },
21016
21017     // private
21018     beginEdit : function(){
21019         this.editing = true;
21020         this.modified = {}; 
21021     },
21022
21023     // private
21024     cancelEdit : function(){
21025         this.editing = false;
21026         delete this.modified;
21027     },
21028
21029     // private
21030     endEdit : function(){
21031         this.editing = false;
21032         if(this.dirty && this.store){
21033             this.store.afterEdit(this);
21034         }
21035     },
21036
21037     /**
21038      * Usually called by the {@link Roo.data.Store} which owns the Record.
21039      * Rejects all changes made to the Record since either creation, or the last commit operation.
21040      * Modified fields are reverted to their original values.
21041      * <p>
21042      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21043      * of reject operations.
21044      */
21045     reject : function(){
21046         var m = this.modified;
21047         for(var n in m){
21048             if(typeof m[n] != "function"){
21049                 this.data[n] = m[n];
21050             }
21051         }
21052         this.dirty = false;
21053         delete this.modified;
21054         this.editing = false;
21055         if(this.store){
21056             this.store.afterReject(this);
21057         }
21058     },
21059
21060     /**
21061      * Usually called by the {@link Roo.data.Store} which owns the Record.
21062      * Commits all changes made to the Record since either creation, or the last commit operation.
21063      * <p>
21064      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21065      * of commit operations.
21066      */
21067     commit : function(){
21068         this.dirty = false;
21069         delete this.modified;
21070         this.editing = false;
21071         if(this.store){
21072             this.store.afterCommit(this);
21073         }
21074     },
21075
21076     // private
21077     hasError : function(){
21078         return this.error != null;
21079     },
21080
21081     // private
21082     clearError : function(){
21083         this.error = null;
21084     },
21085
21086     /**
21087      * Creates a copy of this record.
21088      * @param {String} id (optional) A new record id if you don't want to use this record's id
21089      * @return {Record}
21090      */
21091     copy : function(newId) {
21092         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21093     }
21094 };/*
21095  * Based on:
21096  * Ext JS Library 1.1.1
21097  * Copyright(c) 2006-2007, Ext JS, LLC.
21098  *
21099  * Originally Released Under LGPL - original licence link has changed is not relivant.
21100  *
21101  * Fork - LGPL
21102  * <script type="text/javascript">
21103  */
21104
21105
21106
21107 /**
21108  * @class Roo.data.Store
21109  * @extends Roo.util.Observable
21110  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21111  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21112  * <p>
21113  * 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
21114  * has no knowledge of the format of the data returned by the Proxy.<br>
21115  * <p>
21116  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21117  * instances from the data object. These records are cached and made available through accessor functions.
21118  * @constructor
21119  * Creates a new Store.
21120  * @param {Object} config A config object containing the objects needed for the Store to access data,
21121  * and read the data into Records.
21122  */
21123 Roo.data.Store = function(config){
21124     this.data = new Roo.util.MixedCollection(false);
21125     this.data.getKey = function(o){
21126         return o.id;
21127     };
21128     this.baseParams = {};
21129     // private
21130     this.paramNames = {
21131         "start" : "start",
21132         "limit" : "limit",
21133         "sort" : "sort",
21134         "dir" : "dir",
21135         "multisort" : "_multisort"
21136     };
21137
21138     if(config && config.data){
21139         this.inlineData = config.data;
21140         delete config.data;
21141     }
21142
21143     Roo.apply(this, config);
21144     
21145     if(this.reader){ // reader passed
21146         this.reader = Roo.factory(this.reader, Roo.data);
21147         this.reader.xmodule = this.xmodule || false;
21148         if(!this.recordType){
21149             this.recordType = this.reader.recordType;
21150         }
21151         if(this.reader.onMetaChange){
21152             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21153         }
21154     }
21155
21156     if(this.recordType){
21157         this.fields = this.recordType.prototype.fields;
21158     }
21159     this.modified = [];
21160
21161     this.addEvents({
21162         /**
21163          * @event datachanged
21164          * Fires when the data cache has changed, and a widget which is using this Store
21165          * as a Record cache should refresh its view.
21166          * @param {Store} this
21167          */
21168         datachanged : true,
21169         /**
21170          * @event metachange
21171          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21172          * @param {Store} this
21173          * @param {Object} meta The JSON metadata
21174          */
21175         metachange : true,
21176         /**
21177          * @event add
21178          * Fires when Records have been added to the Store
21179          * @param {Store} this
21180          * @param {Roo.data.Record[]} records The array of Records added
21181          * @param {Number} index The index at which the record(s) were added
21182          */
21183         add : true,
21184         /**
21185          * @event remove
21186          * Fires when a Record has been removed from the Store
21187          * @param {Store} this
21188          * @param {Roo.data.Record} record The Record that was removed
21189          * @param {Number} index The index at which the record was removed
21190          */
21191         remove : true,
21192         /**
21193          * @event update
21194          * Fires when a Record has been updated
21195          * @param {Store} this
21196          * @param {Roo.data.Record} record The Record that was updated
21197          * @param {String} operation The update operation being performed.  Value may be one of:
21198          * <pre><code>
21199  Roo.data.Record.EDIT
21200  Roo.data.Record.REJECT
21201  Roo.data.Record.COMMIT
21202          * </code></pre>
21203          */
21204         update : true,
21205         /**
21206          * @event clear
21207          * Fires when the data cache has been cleared.
21208          * @param {Store} this
21209          */
21210         clear : true,
21211         /**
21212          * @event beforeload
21213          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21214          * the load action will be canceled.
21215          * @param {Store} this
21216          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21217          */
21218         beforeload : true,
21219         /**
21220          * @event beforeloadadd
21221          * Fires after a new set of Records has been loaded.
21222          * @param {Store} this
21223          * @param {Roo.data.Record[]} records The Records that were loaded
21224          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21225          */
21226         beforeloadadd : true,
21227         /**
21228          * @event load
21229          * Fires after a new set of Records has been loaded, before they are added to the store.
21230          * @param {Store} this
21231          * @param {Roo.data.Record[]} records The Records that were loaded
21232          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21233          * @params {Object} return from reader
21234          */
21235         load : true,
21236         /**
21237          * @event loadexception
21238          * Fires if an exception occurs in the Proxy during loading.
21239          * Called with the signature of the Proxy's "loadexception" event.
21240          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21241          * 
21242          * @param {Proxy} 
21243          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21244          * @param {Object} load options 
21245          * @param {Object} jsonData from your request (normally this contains the Exception)
21246          */
21247         loadexception : true
21248     });
21249     
21250     if(this.proxy){
21251         this.proxy = Roo.factory(this.proxy, Roo.data);
21252         this.proxy.xmodule = this.xmodule || false;
21253         this.relayEvents(this.proxy,  ["loadexception"]);
21254     }
21255     this.sortToggle = {};
21256     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21257
21258     Roo.data.Store.superclass.constructor.call(this);
21259
21260     if(this.inlineData){
21261         this.loadData(this.inlineData);
21262         delete this.inlineData;
21263     }
21264 };
21265
21266 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21267      /**
21268     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21269     * without a remote query - used by combo/forms at present.
21270     */
21271     
21272     /**
21273     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21274     */
21275     /**
21276     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21277     */
21278     /**
21279     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21280     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21281     */
21282     /**
21283     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21284     * on any HTTP request
21285     */
21286     /**
21287     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21288     */
21289     /**
21290     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21291     */
21292     multiSort: false,
21293     /**
21294     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21295     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21296     */
21297     remoteSort : false,
21298
21299     /**
21300     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21301      * loaded or when a record is removed. (defaults to false).
21302     */
21303     pruneModifiedRecords : false,
21304
21305     // private
21306     lastOptions : null,
21307
21308     /**
21309      * Add Records to the Store and fires the add event.
21310      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21311      */
21312     add : function(records){
21313         records = [].concat(records);
21314         for(var i = 0, len = records.length; i < len; i++){
21315             records[i].join(this);
21316         }
21317         var index = this.data.length;
21318         this.data.addAll(records);
21319         this.fireEvent("add", this, records, index);
21320     },
21321
21322     /**
21323      * Remove a Record from the Store and fires the remove event.
21324      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21325      */
21326     remove : function(record){
21327         var index = this.data.indexOf(record);
21328         this.data.removeAt(index);
21329         if(this.pruneModifiedRecords){
21330             this.modified.remove(record);
21331         }
21332         this.fireEvent("remove", this, record, index);
21333     },
21334
21335     /**
21336      * Remove all Records from the Store and fires the clear event.
21337      */
21338     removeAll : function(){
21339         this.data.clear();
21340         if(this.pruneModifiedRecords){
21341             this.modified = [];
21342         }
21343         this.fireEvent("clear", this);
21344     },
21345
21346     /**
21347      * Inserts Records to the Store at the given index and fires the add event.
21348      * @param {Number} index The start index at which to insert the passed Records.
21349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21350      */
21351     insert : function(index, records){
21352         records = [].concat(records);
21353         for(var i = 0, len = records.length; i < len; i++){
21354             this.data.insert(index, records[i]);
21355             records[i].join(this);
21356         }
21357         this.fireEvent("add", this, records, index);
21358     },
21359
21360     /**
21361      * Get the index within the cache of the passed Record.
21362      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21363      * @return {Number} The index of the passed Record. Returns -1 if not found.
21364      */
21365     indexOf : function(record){
21366         return this.data.indexOf(record);
21367     },
21368
21369     /**
21370      * Get the index within the cache of the Record with the passed id.
21371      * @param {String} id The id of the Record to find.
21372      * @return {Number} The index of the Record. Returns -1 if not found.
21373      */
21374     indexOfId : function(id){
21375         return this.data.indexOfKey(id);
21376     },
21377
21378     /**
21379      * Get the Record with the specified id.
21380      * @param {String} id The id of the Record to find.
21381      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21382      */
21383     getById : function(id){
21384         return this.data.key(id);
21385     },
21386
21387     /**
21388      * Get the Record at the specified index.
21389      * @param {Number} index The index of the Record to find.
21390      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21391      */
21392     getAt : function(index){
21393         return this.data.itemAt(index);
21394     },
21395
21396     /**
21397      * Returns a range of Records between specified indices.
21398      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21399      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21400      * @return {Roo.data.Record[]} An array of Records
21401      */
21402     getRange : function(start, end){
21403         return this.data.getRange(start, end);
21404     },
21405
21406     // private
21407     storeOptions : function(o){
21408         o = Roo.apply({}, o);
21409         delete o.callback;
21410         delete o.scope;
21411         this.lastOptions = o;
21412     },
21413
21414     /**
21415      * Loads the Record cache from the configured Proxy using the configured Reader.
21416      * <p>
21417      * If using remote paging, then the first load call must specify the <em>start</em>
21418      * and <em>limit</em> properties in the options.params property to establish the initial
21419      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21420      * <p>
21421      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21422      * and this call will return before the new data has been loaded. Perform any post-processing
21423      * in a callback function, or in a "load" event handler.</strong>
21424      * <p>
21425      * @param {Object} options An object containing properties which control loading options:<ul>
21426      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21427      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21428      * passed the following arguments:<ul>
21429      * <li>r : Roo.data.Record[]</li>
21430      * <li>options: Options object from the load call</li>
21431      * <li>success: Boolean success indicator</li></ul></li>
21432      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21433      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21434      * </ul>
21435      */
21436     load : function(options){
21437         options = options || {};
21438         if(this.fireEvent("beforeload", this, options) !== false){
21439             this.storeOptions(options);
21440             var p = Roo.apply(options.params || {}, this.baseParams);
21441             // if meta was not loaded from remote source.. try requesting it.
21442             if (!this.reader.metaFromRemote) {
21443                 p._requestMeta = 1;
21444             }
21445             if(this.sortInfo && this.remoteSort){
21446                 var pn = this.paramNames;
21447                 p[pn["sort"]] = this.sortInfo.field;
21448                 p[pn["dir"]] = this.sortInfo.direction;
21449             }
21450             if (this.multiSort) {
21451                 var pn = this.paramNames;
21452                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21453             }
21454             
21455             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21456         }
21457     },
21458
21459     /**
21460      * Reloads the Record cache from the configured Proxy using the configured Reader and
21461      * the options from the last load operation performed.
21462      * @param {Object} options (optional) An object containing properties which may override the options
21463      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21464      * the most recently used options are reused).
21465      */
21466     reload : function(options){
21467         this.load(Roo.applyIf(options||{}, this.lastOptions));
21468     },
21469
21470     // private
21471     // Called as a callback by the Reader during a load operation.
21472     loadRecords : function(o, options, success){
21473         if(!o || success === false){
21474             if(success !== false){
21475                 this.fireEvent("load", this, [], options, o);
21476             }
21477             if(options.callback){
21478                 options.callback.call(options.scope || this, [], options, false);
21479             }
21480             return;
21481         }
21482         // if data returned failure - throw an exception.
21483         if (o.success === false) {
21484             // show a message if no listener is registered.
21485             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21486                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21487             }
21488             // loadmask wil be hooked into this..
21489             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21490             return;
21491         }
21492         var r = o.records, t = o.totalRecords || r.length;
21493         
21494         this.fireEvent("beforeloadadd", this, r, options, o);
21495         
21496         if(!options || options.add !== true){
21497             if(this.pruneModifiedRecords){
21498                 this.modified = [];
21499             }
21500             for(var i = 0, len = r.length; i < len; i++){
21501                 r[i].join(this);
21502             }
21503             if(this.snapshot){
21504                 this.data = this.snapshot;
21505                 delete this.snapshot;
21506             }
21507             this.data.clear();
21508             this.data.addAll(r);
21509             this.totalLength = t;
21510             this.applySort();
21511             this.fireEvent("datachanged", this);
21512         }else{
21513             this.totalLength = Math.max(t, this.data.length+r.length);
21514             this.add(r);
21515         }
21516         this.fireEvent("load", this, r, options, o);
21517         if(options.callback){
21518             options.callback.call(options.scope || this, r, options, true);
21519         }
21520     },
21521
21522
21523     /**
21524      * Loads data from a passed data block. A Reader which understands the format of the data
21525      * must have been configured in the constructor.
21526      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21527      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21528      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21529      */
21530     loadData : function(o, append){
21531         var r = this.reader.readRecords(o);
21532         this.loadRecords(r, {add: append}, true);
21533     },
21534
21535     /**
21536      * Gets the number of cached records.
21537      * <p>
21538      * <em>If using paging, this may not be the total size of the dataset. If the data object
21539      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21540      * the data set size</em>
21541      */
21542     getCount : function(){
21543         return this.data.length || 0;
21544     },
21545
21546     /**
21547      * Gets the total number of records in the dataset as returned by the server.
21548      * <p>
21549      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21550      * the dataset size</em>
21551      */
21552     getTotalCount : function(){
21553         return this.totalLength || 0;
21554     },
21555
21556     /**
21557      * Returns the sort state of the Store as an object with two properties:
21558      * <pre><code>
21559  field {String} The name of the field by which the Records are sorted
21560  direction {String} The sort order, "ASC" or "DESC"
21561      * </code></pre>
21562      */
21563     getSortState : function(){
21564         return this.sortInfo;
21565     },
21566
21567     // private
21568     applySort : function(){
21569         if(this.sortInfo && !this.remoteSort){
21570             var s = this.sortInfo, f = s.field;
21571             var st = this.fields.get(f).sortType;
21572             var fn = function(r1, r2){
21573                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21574                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21575             };
21576             this.data.sort(s.direction, fn);
21577             if(this.snapshot && this.snapshot != this.data){
21578                 this.snapshot.sort(s.direction, fn);
21579             }
21580         }
21581     },
21582
21583     /**
21584      * Sets the default sort column and order to be used by the next load operation.
21585      * @param {String} fieldName The name of the field to sort by.
21586      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21587      */
21588     setDefaultSort : function(field, dir){
21589         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21590     },
21591
21592     /**
21593      * Sort the Records.
21594      * If remote sorting is used, the sort is performed on the server, and the cache is
21595      * reloaded. If local sorting is used, the cache is sorted internally.
21596      * @param {String} fieldName The name of the field to sort by.
21597      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21598      */
21599     sort : function(fieldName, dir){
21600         var f = this.fields.get(fieldName);
21601         if(!dir){
21602             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21603             
21604             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21605                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21606             }else{
21607                 dir = f.sortDir;
21608             }
21609         }
21610         this.sortToggle[f.name] = dir;
21611         this.sortInfo = {field: f.name, direction: dir};
21612         if(!this.remoteSort){
21613             this.applySort();
21614             this.fireEvent("datachanged", this);
21615         }else{
21616             this.load(this.lastOptions);
21617         }
21618     },
21619
21620     /**
21621      * Calls the specified function for each of the Records in the cache.
21622      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21623      * Returning <em>false</em> aborts and exits the iteration.
21624      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21625      */
21626     each : function(fn, scope){
21627         this.data.each(fn, scope);
21628     },
21629
21630     /**
21631      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21632      * (e.g., during paging).
21633      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21634      */
21635     getModifiedRecords : function(){
21636         return this.modified;
21637     },
21638
21639     // private
21640     createFilterFn : function(property, value, anyMatch){
21641         if(!value.exec){ // not a regex
21642             value = String(value);
21643             if(value.length == 0){
21644                 return false;
21645             }
21646             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21647         }
21648         return function(r){
21649             return value.test(r.data[property]);
21650         };
21651     },
21652
21653     /**
21654      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21655      * @param {String} property A field on your records
21656      * @param {Number} start The record index to start at (defaults to 0)
21657      * @param {Number} end The last record index to include (defaults to length - 1)
21658      * @return {Number} The sum
21659      */
21660     sum : function(property, start, end){
21661         var rs = this.data.items, v = 0;
21662         start = start || 0;
21663         end = (end || end === 0) ? end : rs.length-1;
21664
21665         for(var i = start; i <= end; i++){
21666             v += (rs[i].data[property] || 0);
21667         }
21668         return v;
21669     },
21670
21671     /**
21672      * Filter the records by a specified property.
21673      * @param {String} field A field on your records
21674      * @param {String/RegExp} value Either a string that the field
21675      * should start with or a RegExp to test against the field
21676      * @param {Boolean} anyMatch True to match any part not just the beginning
21677      */
21678     filter : function(property, value, anyMatch){
21679         var fn = this.createFilterFn(property, value, anyMatch);
21680         return fn ? this.filterBy(fn) : this.clearFilter();
21681     },
21682
21683     /**
21684      * Filter by a function. The specified function will be called with each
21685      * record in this data source. If the function returns true the record is included,
21686      * otherwise it is filtered.
21687      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21688      * @param {Object} scope (optional) The scope of the function (defaults to this)
21689      */
21690     filterBy : function(fn, scope){
21691         this.snapshot = this.snapshot || this.data;
21692         this.data = this.queryBy(fn, scope||this);
21693         this.fireEvent("datachanged", this);
21694     },
21695
21696     /**
21697      * Query the records by a specified property.
21698      * @param {String} field A field on your records
21699      * @param {String/RegExp} value Either a string that the field
21700      * should start with or a RegExp to test against the field
21701      * @param {Boolean} anyMatch True to match any part not just the beginning
21702      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21703      */
21704     query : function(property, value, anyMatch){
21705         var fn = this.createFilterFn(property, value, anyMatch);
21706         return fn ? this.queryBy(fn) : this.data.clone();
21707     },
21708
21709     /**
21710      * Query by a function. The specified function will be called with each
21711      * record in this data source. If the function returns true the record is included
21712      * in the results.
21713      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21714      * @param {Object} scope (optional) The scope of the function (defaults to this)
21715       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21716      **/
21717     queryBy : function(fn, scope){
21718         var data = this.snapshot || this.data;
21719         return data.filterBy(fn, scope||this);
21720     },
21721
21722     /**
21723      * Collects unique values for a particular dataIndex from this store.
21724      * @param {String} dataIndex The property to collect
21725      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21726      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21727      * @return {Array} An array of the unique values
21728      **/
21729     collect : function(dataIndex, allowNull, bypassFilter){
21730         var d = (bypassFilter === true && this.snapshot) ?
21731                 this.snapshot.items : this.data.items;
21732         var v, sv, r = [], l = {};
21733         for(var i = 0, len = d.length; i < len; i++){
21734             v = d[i].data[dataIndex];
21735             sv = String(v);
21736             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21737                 l[sv] = true;
21738                 r[r.length] = v;
21739             }
21740         }
21741         return r;
21742     },
21743
21744     /**
21745      * Revert to a view of the Record cache with no filtering applied.
21746      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21747      */
21748     clearFilter : function(suppressEvent){
21749         if(this.snapshot && this.snapshot != this.data){
21750             this.data = this.snapshot;
21751             delete this.snapshot;
21752             if(suppressEvent !== true){
21753                 this.fireEvent("datachanged", this);
21754             }
21755         }
21756     },
21757
21758     // private
21759     afterEdit : function(record){
21760         if(this.modified.indexOf(record) == -1){
21761             this.modified.push(record);
21762         }
21763         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21764     },
21765     
21766     // private
21767     afterReject : function(record){
21768         this.modified.remove(record);
21769         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21770     },
21771
21772     // private
21773     afterCommit : function(record){
21774         this.modified.remove(record);
21775         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21776     },
21777
21778     /**
21779      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21780      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21781      */
21782     commitChanges : function(){
21783         var m = this.modified.slice(0);
21784         this.modified = [];
21785         for(var i = 0, len = m.length; i < len; i++){
21786             m[i].commit();
21787         }
21788     },
21789
21790     /**
21791      * Cancel outstanding changes on all changed records.
21792      */
21793     rejectChanges : function(){
21794         var m = this.modified.slice(0);
21795         this.modified = [];
21796         for(var i = 0, len = m.length; i < len; i++){
21797             m[i].reject();
21798         }
21799     },
21800
21801     onMetaChange : function(meta, rtype, o){
21802         this.recordType = rtype;
21803         this.fields = rtype.prototype.fields;
21804         delete this.snapshot;
21805         this.sortInfo = meta.sortInfo || this.sortInfo;
21806         this.modified = [];
21807         this.fireEvent('metachange', this, this.reader.meta);
21808     },
21809     
21810     moveIndex : function(data, type)
21811     {
21812         var index = this.indexOf(data);
21813         
21814         var newIndex = index + type;
21815         
21816         this.remove(data);
21817         
21818         this.insert(newIndex, data);
21819         
21820     }
21821 });/*
21822  * Based on:
21823  * Ext JS Library 1.1.1
21824  * Copyright(c) 2006-2007, Ext JS, LLC.
21825  *
21826  * Originally Released Under LGPL - original licence link has changed is not relivant.
21827  *
21828  * Fork - LGPL
21829  * <script type="text/javascript">
21830  */
21831
21832 /**
21833  * @class Roo.data.SimpleStore
21834  * @extends Roo.data.Store
21835  * Small helper class to make creating Stores from Array data easier.
21836  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21837  * @cfg {Array} fields An array of field definition objects, or field name strings.
21838  * @cfg {Array} data The multi-dimensional array of data
21839  * @constructor
21840  * @param {Object} config
21841  */
21842 Roo.data.SimpleStore = function(config){
21843     Roo.data.SimpleStore.superclass.constructor.call(this, {
21844         isLocal : true,
21845         reader: new Roo.data.ArrayReader({
21846                 id: config.id
21847             },
21848             Roo.data.Record.create(config.fields)
21849         ),
21850         proxy : new Roo.data.MemoryProxy(config.data)
21851     });
21852     this.load();
21853 };
21854 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21855  * Based on:
21856  * Ext JS Library 1.1.1
21857  * Copyright(c) 2006-2007, Ext JS, LLC.
21858  *
21859  * Originally Released Under LGPL - original licence link has changed is not relivant.
21860  *
21861  * Fork - LGPL
21862  * <script type="text/javascript">
21863  */
21864
21865 /**
21866 /**
21867  * @extends Roo.data.Store
21868  * @class Roo.data.JsonStore
21869  * Small helper class to make creating Stores for JSON data easier. <br/>
21870 <pre><code>
21871 var store = new Roo.data.JsonStore({
21872     url: 'get-images.php',
21873     root: 'images',
21874     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21875 });
21876 </code></pre>
21877  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21878  * JsonReader and HttpProxy (unless inline data is provided).</b>
21879  * @cfg {Array} fields An array of field definition objects, or field name strings.
21880  * @constructor
21881  * @param {Object} config
21882  */
21883 Roo.data.JsonStore = function(c){
21884     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21885         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21886         reader: new Roo.data.JsonReader(c, c.fields)
21887     }));
21888 };
21889 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21890  * Based on:
21891  * Ext JS Library 1.1.1
21892  * Copyright(c) 2006-2007, Ext JS, LLC.
21893  *
21894  * Originally Released Under LGPL - original licence link has changed is not relivant.
21895  *
21896  * Fork - LGPL
21897  * <script type="text/javascript">
21898  */
21899
21900  
21901 Roo.data.Field = function(config){
21902     if(typeof config == "string"){
21903         config = {name: config};
21904     }
21905     Roo.apply(this, config);
21906     
21907     if(!this.type){
21908         this.type = "auto";
21909     }
21910     
21911     var st = Roo.data.SortTypes;
21912     // named sortTypes are supported, here we look them up
21913     if(typeof this.sortType == "string"){
21914         this.sortType = st[this.sortType];
21915     }
21916     
21917     // set default sortType for strings and dates
21918     if(!this.sortType){
21919         switch(this.type){
21920             case "string":
21921                 this.sortType = st.asUCString;
21922                 break;
21923             case "date":
21924                 this.sortType = st.asDate;
21925                 break;
21926             default:
21927                 this.sortType = st.none;
21928         }
21929     }
21930
21931     // define once
21932     var stripRe = /[\$,%]/g;
21933
21934     // prebuilt conversion function for this field, instead of
21935     // switching every time we're reading a value
21936     if(!this.convert){
21937         var cv, dateFormat = this.dateFormat;
21938         switch(this.type){
21939             case "":
21940             case "auto":
21941             case undefined:
21942                 cv = function(v){ return v; };
21943                 break;
21944             case "string":
21945                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21946                 break;
21947             case "int":
21948                 cv = function(v){
21949                     return v !== undefined && v !== null && v !== '' ?
21950                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21951                     };
21952                 break;
21953             case "float":
21954                 cv = function(v){
21955                     return v !== undefined && v !== null && v !== '' ?
21956                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21957                     };
21958                 break;
21959             case "bool":
21960             case "boolean":
21961                 cv = function(v){ return v === true || v === "true" || v == 1; };
21962                 break;
21963             case "date":
21964                 cv = function(v){
21965                     if(!v){
21966                         return '';
21967                     }
21968                     if(v instanceof Date){
21969                         return v;
21970                     }
21971                     if(dateFormat){
21972                         if(dateFormat == "timestamp"){
21973                             return new Date(v*1000);
21974                         }
21975                         return Date.parseDate(v, dateFormat);
21976                     }
21977                     var parsed = Date.parse(v);
21978                     return parsed ? new Date(parsed) : null;
21979                 };
21980              break;
21981             
21982         }
21983         this.convert = cv;
21984     }
21985 };
21986
21987 Roo.data.Field.prototype = {
21988     dateFormat: null,
21989     defaultValue: "",
21990     mapping: null,
21991     sortType : null,
21992     sortDir : "ASC"
21993 };/*
21994  * Based on:
21995  * Ext JS Library 1.1.1
21996  * Copyright(c) 2006-2007, Ext JS, LLC.
21997  *
21998  * Originally Released Under LGPL - original licence link has changed is not relivant.
21999  *
22000  * Fork - LGPL
22001  * <script type="text/javascript">
22002  */
22003  
22004 // Base class for reading structured data from a data source.  This class is intended to be
22005 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22006
22007 /**
22008  * @class Roo.data.DataReader
22009  * Base class for reading structured data from a data source.  This class is intended to be
22010  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22011  */
22012
22013 Roo.data.DataReader = function(meta, recordType){
22014     
22015     this.meta = meta;
22016     
22017     this.recordType = recordType instanceof Array ? 
22018         Roo.data.Record.create(recordType) : recordType;
22019 };
22020
22021 Roo.data.DataReader.prototype = {
22022      /**
22023      * Create an empty record
22024      * @param {Object} data (optional) - overlay some values
22025      * @return {Roo.data.Record} record created.
22026      */
22027     newRow :  function(d) {
22028         var da =  {};
22029         this.recordType.prototype.fields.each(function(c) {
22030             switch( c.type) {
22031                 case 'int' : da[c.name] = 0; break;
22032                 case 'date' : da[c.name] = new Date(); break;
22033                 case 'float' : da[c.name] = 0.0; break;
22034                 case 'boolean' : da[c.name] = false; break;
22035                 case 'array' : da[c.name] = []; break;
22036                 default : da[c.name] = ""; break;
22037             }
22038             
22039         });
22040         return new this.recordType(Roo.apply(da, d));
22041     }
22042     
22043 };/*
22044  * Based on:
22045  * Ext JS Library 1.1.1
22046  * Copyright(c) 2006-2007, Ext JS, LLC.
22047  *
22048  * Originally Released Under LGPL - original licence link has changed is not relivant.
22049  *
22050  * Fork - LGPL
22051  * <script type="text/javascript">
22052  */
22053
22054 /**
22055  * @class Roo.data.DataProxy
22056  * @extends Roo.data.Observable
22057  * This class is an abstract base class for implementations which provide retrieval of
22058  * unformatted data objects.<br>
22059  * <p>
22060  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22061  * (of the appropriate type which knows how to parse the data object) to provide a block of
22062  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22063  * <p>
22064  * Custom implementations must implement the load method as described in
22065  * {@link Roo.data.HttpProxy#load}.
22066  */
22067 Roo.data.DataProxy = function(){
22068     this.addEvents({
22069         /**
22070          * @event beforeload
22071          * Fires before a network request is made to retrieve a data object.
22072          * @param {Object} This DataProxy object.
22073          * @param {Object} params The params parameter to the load function.
22074          */
22075         beforeload : true,
22076         /**
22077          * @event load
22078          * Fires before the load method's callback is called.
22079          * @param {Object} This DataProxy object.
22080          * @param {Object} o The data object.
22081          * @param {Object} arg The callback argument object passed to the load function.
22082          */
22083         load : true,
22084         /**
22085          * @event loadexception
22086          * Fires if an Exception occurs during data retrieval.
22087          * @param {Object} This DataProxy object.
22088          * @param {Object} o The data object.
22089          * @param {Object} arg The callback argument object passed to the load function.
22090          * @param {Object} e The Exception.
22091          */
22092         loadexception : true
22093     });
22094     Roo.data.DataProxy.superclass.constructor.call(this);
22095 };
22096
22097 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22098
22099     /**
22100      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
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  * @class Roo.data.MemoryProxy
22114  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22115  * to the Reader when its load method is called.
22116  * @constructor
22117  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22118  */
22119 Roo.data.MemoryProxy = function(data){
22120     if (data.data) {
22121         data = data.data;
22122     }
22123     Roo.data.MemoryProxy.superclass.constructor.call(this);
22124     this.data = data;
22125 };
22126
22127 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22128     /**
22129      * Load data from the requested source (in this case an in-memory
22130      * data object passed to the constructor), read the data object into
22131      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22132      * process that block using the passed callback.
22133      * @param {Object} params This parameter is not used by the MemoryProxy class.
22134      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22135      * object into a block of Roo.data.Records.
22136      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22137      * The function must be passed <ul>
22138      * <li>The Record block object</li>
22139      * <li>The "arg" argument from the load function</li>
22140      * <li>A boolean success indicator</li>
22141      * </ul>
22142      * @param {Object} scope The scope in which to call the callback
22143      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22144      */
22145     load : function(params, reader, callback, scope, arg){
22146         params = params || {};
22147         var result;
22148         try {
22149             result = reader.readRecords(this.data);
22150         }catch(e){
22151             this.fireEvent("loadexception", this, arg, null, e);
22152             callback.call(scope, null, arg, false);
22153             return;
22154         }
22155         callback.call(scope, result, arg, true);
22156     },
22157     
22158     // private
22159     update : function(params, records){
22160         
22161     }
22162 });/*
22163  * Based on:
22164  * Ext JS Library 1.1.1
22165  * Copyright(c) 2006-2007, Ext JS, LLC.
22166  *
22167  * Originally Released Under LGPL - original licence link has changed is not relivant.
22168  *
22169  * Fork - LGPL
22170  * <script type="text/javascript">
22171  */
22172 /**
22173  * @class Roo.data.HttpProxy
22174  * @extends Roo.data.DataProxy
22175  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22176  * configured to reference a certain URL.<br><br>
22177  * <p>
22178  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22179  * from which the running page was served.<br><br>
22180  * <p>
22181  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22182  * <p>
22183  * Be aware that to enable the browser to parse an XML document, the server must set
22184  * the Content-Type header in the HTTP response to "text/xml".
22185  * @constructor
22186  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22187  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22188  * will be used to make the request.
22189  */
22190 Roo.data.HttpProxy = function(conn){
22191     Roo.data.HttpProxy.superclass.constructor.call(this);
22192     // is conn a conn config or a real conn?
22193     this.conn = conn;
22194     this.useAjax = !conn || !conn.events;
22195   
22196 };
22197
22198 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22199     // thse are take from connection...
22200     
22201     /**
22202      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22203      */
22204     /**
22205      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22206      * extra parameters to each request made by this object. (defaults to undefined)
22207      */
22208     /**
22209      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22210      *  to each request made by this object. (defaults to undefined)
22211      */
22212     /**
22213      * @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)
22214      */
22215     /**
22216      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22217      */
22218      /**
22219      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22220      * @type Boolean
22221      */
22222   
22223
22224     /**
22225      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22226      * @type Boolean
22227      */
22228     /**
22229      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22230      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22231      * a finer-grained basis than the DataProxy events.
22232      */
22233     getConnection : function(){
22234         return this.useAjax ? Roo.Ajax : this.conn;
22235     },
22236
22237     /**
22238      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22239      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22240      * process that block using the passed callback.
22241      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22242      * for the request to the remote server.
22243      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22244      * object into a block of Roo.data.Records.
22245      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22246      * The function must be passed <ul>
22247      * <li>The Record block object</li>
22248      * <li>The "arg" argument from the load function</li>
22249      * <li>A boolean success indicator</li>
22250      * </ul>
22251      * @param {Object} scope The scope in which to call the callback
22252      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22253      */
22254     load : function(params, reader, callback, scope, arg){
22255         if(this.fireEvent("beforeload", this, params) !== false){
22256             var  o = {
22257                 params : params || {},
22258                 request: {
22259                     callback : callback,
22260                     scope : scope,
22261                     arg : arg
22262                 },
22263                 reader: reader,
22264                 callback : this.loadResponse,
22265                 scope: this
22266             };
22267             if(this.useAjax){
22268                 Roo.applyIf(o, this.conn);
22269                 if(this.activeRequest){
22270                     Roo.Ajax.abort(this.activeRequest);
22271                 }
22272                 this.activeRequest = Roo.Ajax.request(o);
22273             }else{
22274                 this.conn.request(o);
22275             }
22276         }else{
22277             callback.call(scope||this, null, arg, false);
22278         }
22279     },
22280
22281     // private
22282     loadResponse : function(o, success, response){
22283         delete this.activeRequest;
22284         if(!success){
22285             this.fireEvent("loadexception", this, o, response);
22286             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22287             return;
22288         }
22289         var result;
22290         try {
22291             result = o.reader.read(response);
22292         }catch(e){
22293             this.fireEvent("loadexception", this, o, response, e);
22294             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22295             return;
22296         }
22297         
22298         this.fireEvent("load", this, o, o.request.arg);
22299         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22300     },
22301
22302     // private
22303     update : function(dataSet){
22304
22305     },
22306
22307     // private
22308     updateResponse : function(dataSet){
22309
22310     }
22311 });/*
22312  * Based on:
22313  * Ext JS Library 1.1.1
22314  * Copyright(c) 2006-2007, Ext JS, LLC.
22315  *
22316  * Originally Released Under LGPL - original licence link has changed is not relivant.
22317  *
22318  * Fork - LGPL
22319  * <script type="text/javascript">
22320  */
22321
22322 /**
22323  * @class Roo.data.ScriptTagProxy
22324  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22325  * other than the originating domain of the running page.<br><br>
22326  * <p>
22327  * <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
22328  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22329  * <p>
22330  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22331  * source code that is used as the source inside a &lt;script> tag.<br><br>
22332  * <p>
22333  * In order for the browser to process the returned data, the server must wrap the data object
22334  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22335  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22336  * depending on whether the callback name was passed:
22337  * <p>
22338  * <pre><code>
22339 boolean scriptTag = false;
22340 String cb = request.getParameter("callback");
22341 if (cb != null) {
22342     scriptTag = true;
22343     response.setContentType("text/javascript");
22344 } else {
22345     response.setContentType("application/x-json");
22346 }
22347 Writer out = response.getWriter();
22348 if (scriptTag) {
22349     out.write(cb + "(");
22350 }
22351 out.print(dataBlock.toJsonString());
22352 if (scriptTag) {
22353     out.write(");");
22354 }
22355 </pre></code>
22356  *
22357  * @constructor
22358  * @param {Object} config A configuration object.
22359  */
22360 Roo.data.ScriptTagProxy = function(config){
22361     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22362     Roo.apply(this, config);
22363     this.head = document.getElementsByTagName("head")[0];
22364 };
22365
22366 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22367
22368 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22369     /**
22370      * @cfg {String} url The URL from which to request the data object.
22371      */
22372     /**
22373      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22374      */
22375     timeout : 30000,
22376     /**
22377      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22378      * the server the name of the callback function set up by the load call to process the returned data object.
22379      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22380      * javascript output which calls this named function passing the data object as its only parameter.
22381      */
22382     callbackParam : "callback",
22383     /**
22384      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22385      * name to the request.
22386      */
22387     nocache : true,
22388
22389     /**
22390      * Load data from the configured URL, read the data object into
22391      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22392      * process that block using the passed callback.
22393      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22394      * for the request to the remote server.
22395      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22396      * object into a block of Roo.data.Records.
22397      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22398      * The function must be passed <ul>
22399      * <li>The Record block object</li>
22400      * <li>The "arg" argument from the load function</li>
22401      * <li>A boolean success indicator</li>
22402      * </ul>
22403      * @param {Object} scope The scope in which to call the callback
22404      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22405      */
22406     load : function(params, reader, callback, scope, arg){
22407         if(this.fireEvent("beforeload", this, params) !== false){
22408
22409             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22410
22411             var url = this.url;
22412             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22413             if(this.nocache){
22414                 url += "&_dc=" + (new Date().getTime());
22415             }
22416             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22417             var trans = {
22418                 id : transId,
22419                 cb : "stcCallback"+transId,
22420                 scriptId : "stcScript"+transId,
22421                 params : params,
22422                 arg : arg,
22423                 url : url,
22424                 callback : callback,
22425                 scope : scope,
22426                 reader : reader
22427             };
22428             var conn = this;
22429
22430             window[trans.cb] = function(o){
22431                 conn.handleResponse(o, trans);
22432             };
22433
22434             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22435
22436             if(this.autoAbort !== false){
22437                 this.abort();
22438             }
22439
22440             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22441
22442             var script = document.createElement("script");
22443             script.setAttribute("src", url);
22444             script.setAttribute("type", "text/javascript");
22445             script.setAttribute("id", trans.scriptId);
22446             this.head.appendChild(script);
22447
22448             this.trans = trans;
22449         }else{
22450             callback.call(scope||this, null, arg, false);
22451         }
22452     },
22453
22454     // private
22455     isLoading : function(){
22456         return this.trans ? true : false;
22457     },
22458
22459     /**
22460      * Abort the current server request.
22461      */
22462     abort : function(){
22463         if(this.isLoading()){
22464             this.destroyTrans(this.trans);
22465         }
22466     },
22467
22468     // private
22469     destroyTrans : function(trans, isLoaded){
22470         this.head.removeChild(document.getElementById(trans.scriptId));
22471         clearTimeout(trans.timeoutId);
22472         if(isLoaded){
22473             window[trans.cb] = undefined;
22474             try{
22475                 delete window[trans.cb];
22476             }catch(e){}
22477         }else{
22478             // if hasn't been loaded, wait for load to remove it to prevent script error
22479             window[trans.cb] = function(){
22480                 window[trans.cb] = undefined;
22481                 try{
22482                     delete window[trans.cb];
22483                 }catch(e){}
22484             };
22485         }
22486     },
22487
22488     // private
22489     handleResponse : function(o, trans){
22490         this.trans = false;
22491         this.destroyTrans(trans, true);
22492         var result;
22493         try {
22494             result = trans.reader.readRecords(o);
22495         }catch(e){
22496             this.fireEvent("loadexception", this, o, trans.arg, e);
22497             trans.callback.call(trans.scope||window, null, trans.arg, false);
22498             return;
22499         }
22500         this.fireEvent("load", this, o, trans.arg);
22501         trans.callback.call(trans.scope||window, result, trans.arg, true);
22502     },
22503
22504     // private
22505     handleFailure : function(trans){
22506         this.trans = false;
22507         this.destroyTrans(trans, false);
22508         this.fireEvent("loadexception", this, null, trans.arg);
22509         trans.callback.call(trans.scope||window, null, trans.arg, false);
22510     }
22511 });/*
22512  * Based on:
22513  * Ext JS Library 1.1.1
22514  * Copyright(c) 2006-2007, Ext JS, LLC.
22515  *
22516  * Originally Released Under LGPL - original licence link has changed is not relivant.
22517  *
22518  * Fork - LGPL
22519  * <script type="text/javascript">
22520  */
22521
22522 /**
22523  * @class Roo.data.JsonReader
22524  * @extends Roo.data.DataReader
22525  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22526  * based on mappings in a provided Roo.data.Record constructor.
22527  * 
22528  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22529  * in the reply previously. 
22530  * 
22531  * <p>
22532  * Example code:
22533  * <pre><code>
22534 var RecordDef = Roo.data.Record.create([
22535     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22536     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22537 ]);
22538 var myReader = new Roo.data.JsonReader({
22539     totalProperty: "results",    // The property which contains the total dataset size (optional)
22540     root: "rows",                // The property which contains an Array of row objects
22541     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22542 }, RecordDef);
22543 </code></pre>
22544  * <p>
22545  * This would consume a JSON file like this:
22546  * <pre><code>
22547 { 'results': 2, 'rows': [
22548     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22549     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22550 }
22551 </code></pre>
22552  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22553  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22554  * paged from the remote server.
22555  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22556  * @cfg {String} root name of the property which contains the Array of row objects.
22557  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22558  * @constructor
22559  * Create a new JsonReader
22560  * @param {Object} meta Metadata configuration options
22561  * @param {Object} recordType Either an Array of field definition objects,
22562  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22563  */
22564 Roo.data.JsonReader = function(meta, recordType){
22565     
22566     meta = meta || {};
22567     // set some defaults:
22568     Roo.applyIf(meta, {
22569         totalProperty: 'total',
22570         successProperty : 'success',
22571         root : 'data',
22572         id : 'id'
22573     });
22574     
22575     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22576 };
22577 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22578     
22579     /**
22580      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22581      * Used by Store query builder to append _requestMeta to params.
22582      * 
22583      */
22584     metaFromRemote : false,
22585     /**
22586      * This method is only used by a DataProxy which has retrieved data from a remote server.
22587      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22588      * @return {Object} data A data block which is used by an Roo.data.Store object as
22589      * a cache of Roo.data.Records.
22590      */
22591     read : function(response){
22592         var json = response.responseText;
22593        
22594         var o = /* eval:var:o */ eval("("+json+")");
22595         if(!o) {
22596             throw {message: "JsonReader.read: Json object not found"};
22597         }
22598         
22599         if(o.metaData){
22600             
22601             delete this.ef;
22602             this.metaFromRemote = true;
22603             this.meta = o.metaData;
22604             this.recordType = Roo.data.Record.create(o.metaData.fields);
22605             this.onMetaChange(this.meta, this.recordType, o);
22606         }
22607         return this.readRecords(o);
22608     },
22609
22610     // private function a store will implement
22611     onMetaChange : function(meta, recordType, o){
22612
22613     },
22614
22615     /**
22616          * @ignore
22617          */
22618     simpleAccess: function(obj, subsc) {
22619         return obj[subsc];
22620     },
22621
22622         /**
22623          * @ignore
22624          */
22625     getJsonAccessor: function(){
22626         var re = /[\[\.]/;
22627         return function(expr) {
22628             try {
22629                 return(re.test(expr))
22630                     ? new Function("obj", "return obj." + expr)
22631                     : function(obj){
22632                         return obj[expr];
22633                     };
22634             } catch(e){}
22635             return Roo.emptyFn;
22636         };
22637     }(),
22638
22639     /**
22640      * Create a data block containing Roo.data.Records from an XML document.
22641      * @param {Object} o An object which contains an Array of row objects in the property specified
22642      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22643      * which contains the total size of the dataset.
22644      * @return {Object} data A data block which is used by an Roo.data.Store object as
22645      * a cache of Roo.data.Records.
22646      */
22647     readRecords : function(o){
22648         /**
22649          * After any data loads, the raw JSON data is available for further custom processing.
22650          * @type Object
22651          */
22652         this.o = o;
22653         var s = this.meta, Record = this.recordType,
22654             f = Record.prototype.fields, fi = f.items, fl = f.length;
22655
22656 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22657         if (!this.ef) {
22658             if(s.totalProperty) {
22659                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22660                 }
22661                 if(s.successProperty) {
22662                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22663                 }
22664                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22665                 if (s.id) {
22666                         var g = this.getJsonAccessor(s.id);
22667                         this.getId = function(rec) {
22668                                 var r = g(rec);
22669                                 return (r === undefined || r === "") ? null : r;
22670                         };
22671                 } else {
22672                         this.getId = function(){return null;};
22673                 }
22674             this.ef = [];
22675             for(var jj = 0; jj < fl; jj++){
22676                 f = fi[jj];
22677                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22678                 this.ef[jj] = this.getJsonAccessor(map);
22679             }
22680         }
22681
22682         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22683         if(s.totalProperty){
22684             var vt = parseInt(this.getTotal(o), 10);
22685             if(!isNaN(vt)){
22686                 totalRecords = vt;
22687             }
22688         }
22689         if(s.successProperty){
22690             var vs = this.getSuccess(o);
22691             if(vs === false || vs === 'false'){
22692                 success = false;
22693             }
22694         }
22695         var records = [];
22696             for(var i = 0; i < c; i++){
22697                     var n = root[i];
22698                 var values = {};
22699                 var id = this.getId(n);
22700                 for(var j = 0; j < fl; j++){
22701                     f = fi[j];
22702                 var v = this.ef[j](n);
22703                 if (!f.convert) {
22704                     Roo.log('missing convert for ' + f.name);
22705                     Roo.log(f);
22706                     continue;
22707                 }
22708                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22709                 }
22710                 var record = new Record(values, id);
22711                 record.json = n;
22712                 records[i] = record;
22713             }
22714             return {
22715             raw : o,
22716                 success : success,
22717                 records : records,
22718                 totalRecords : totalRecords
22719             };
22720     }
22721 });/*
22722  * Based on:
22723  * Ext JS Library 1.1.1
22724  * Copyright(c) 2006-2007, Ext JS, LLC.
22725  *
22726  * Originally Released Under LGPL - original licence link has changed is not relivant.
22727  *
22728  * Fork - LGPL
22729  * <script type="text/javascript">
22730  */
22731
22732 /**
22733  * @class Roo.data.XmlReader
22734  * @extends Roo.data.DataReader
22735  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22736  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22737  * <p>
22738  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22739  * header in the HTTP response must be set to "text/xml".</em>
22740  * <p>
22741  * Example code:
22742  * <pre><code>
22743 var RecordDef = Roo.data.Record.create([
22744    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22745    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22746 ]);
22747 var myReader = new Roo.data.XmlReader({
22748    totalRecords: "results", // The element which contains the total dataset size (optional)
22749    record: "row",           // The repeated element which contains row information
22750    id: "id"                 // The element within the row that provides an ID for the record (optional)
22751 }, RecordDef);
22752 </code></pre>
22753  * <p>
22754  * This would consume an XML file like this:
22755  * <pre><code>
22756 &lt;?xml?>
22757 &lt;dataset>
22758  &lt;results>2&lt;/results>
22759  &lt;row>
22760    &lt;id>1&lt;/id>
22761    &lt;name>Bill&lt;/name>
22762    &lt;occupation>Gardener&lt;/occupation>
22763  &lt;/row>
22764  &lt;row>
22765    &lt;id>2&lt;/id>
22766    &lt;name>Ben&lt;/name>
22767    &lt;occupation>Horticulturalist&lt;/occupation>
22768  &lt;/row>
22769 &lt;/dataset>
22770 </code></pre>
22771  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22772  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22773  * paged from the remote server.
22774  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22775  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22776  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22777  * a record identifier value.
22778  * @constructor
22779  * Create a new XmlReader
22780  * @param {Object} meta Metadata configuration options
22781  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22782  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22783  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22784  */
22785 Roo.data.XmlReader = function(meta, recordType){
22786     meta = meta || {};
22787     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22788 };
22789 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22790     /**
22791      * This method is only used by a DataProxy which has retrieved data from a remote server.
22792          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22793          * to contain a method called 'responseXML' that returns an XML document object.
22794      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22795      * a cache of Roo.data.Records.
22796      */
22797     read : function(response){
22798         var doc = response.responseXML;
22799         if(!doc) {
22800             throw {message: "XmlReader.read: XML Document not available"};
22801         }
22802         return this.readRecords(doc);
22803     },
22804
22805     /**
22806      * Create a data block containing Roo.data.Records from an XML document.
22807          * @param {Object} doc A parsed XML document.
22808      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22809      * a cache of Roo.data.Records.
22810      */
22811     readRecords : function(doc){
22812         /**
22813          * After any data loads/reads, the raw XML Document is available for further custom processing.
22814          * @type XMLDocument
22815          */
22816         this.xmlData = doc;
22817         var root = doc.documentElement || doc;
22818         var q = Roo.DomQuery;
22819         var recordType = this.recordType, fields = recordType.prototype.fields;
22820         var sid = this.meta.id;
22821         var totalRecords = 0, success = true;
22822         if(this.meta.totalRecords){
22823             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22824         }
22825         
22826         if(this.meta.success){
22827             var sv = q.selectValue(this.meta.success, root, true);
22828             success = sv !== false && sv !== 'false';
22829         }
22830         var records = [];
22831         var ns = q.select(this.meta.record, root);
22832         for(var i = 0, len = ns.length; i < len; i++) {
22833                 var n = ns[i];
22834                 var values = {};
22835                 var id = sid ? q.selectValue(sid, n) : undefined;
22836                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22837                     var f = fields.items[j];
22838                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22839                     v = f.convert(v);
22840                     values[f.name] = v;
22841                 }
22842                 var record = new recordType(values, id);
22843                 record.node = n;
22844                 records[records.length] = record;
22845             }
22846
22847             return {
22848                 success : success,
22849                 records : records,
22850                 totalRecords : totalRecords || records.length
22851             };
22852     }
22853 });/*
22854  * Based on:
22855  * Ext JS Library 1.1.1
22856  * Copyright(c) 2006-2007, Ext JS, LLC.
22857  *
22858  * Originally Released Under LGPL - original licence link has changed is not relivant.
22859  *
22860  * Fork - LGPL
22861  * <script type="text/javascript">
22862  */
22863
22864 /**
22865  * @class Roo.data.ArrayReader
22866  * @extends Roo.data.DataReader
22867  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22868  * Each element of that Array represents a row of data fields. The
22869  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22870  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22871  * <p>
22872  * Example code:.
22873  * <pre><code>
22874 var RecordDef = Roo.data.Record.create([
22875     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22876     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22877 ]);
22878 var myReader = new Roo.data.ArrayReader({
22879     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22880 }, RecordDef);
22881 </code></pre>
22882  * <p>
22883  * This would consume an Array like this:
22884  * <pre><code>
22885 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22886   </code></pre>
22887  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22888  * @constructor
22889  * Create a new JsonReader
22890  * @param {Object} meta Metadata configuration options.
22891  * @param {Object} recordType Either an Array of field definition objects
22892  * as specified to {@link Roo.data.Record#create},
22893  * or an {@link Roo.data.Record} object
22894  * created using {@link Roo.data.Record#create}.
22895  */
22896 Roo.data.ArrayReader = function(meta, recordType){
22897     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22898 };
22899
22900 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22901     /**
22902      * Create a data block containing Roo.data.Records from an XML document.
22903      * @param {Object} o An Array of row objects which represents the dataset.
22904      * @return {Object} data A data block which is used by an Roo.data.Store object as
22905      * a cache of Roo.data.Records.
22906      */
22907     readRecords : function(o){
22908         var sid = this.meta ? this.meta.id : null;
22909         var recordType = this.recordType, fields = recordType.prototype.fields;
22910         var records = [];
22911         var root = o;
22912             for(var i = 0; i < root.length; i++){
22913                     var n = root[i];
22914                 var values = {};
22915                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22916                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22917                 var f = fields.items[j];
22918                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22919                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22920                 v = f.convert(v);
22921                 values[f.name] = v;
22922             }
22923                 var record = new recordType(values, id);
22924                 record.json = n;
22925                 records[records.length] = record;
22926             }
22927             return {
22928                 records : records,
22929                 totalRecords : records.length
22930             };
22931     }
22932 });/*
22933  * Based on:
22934  * Ext JS Library 1.1.1
22935  * Copyright(c) 2006-2007, Ext JS, LLC.
22936  *
22937  * Originally Released Under LGPL - original licence link has changed is not relivant.
22938  *
22939  * Fork - LGPL
22940  * <script type="text/javascript">
22941  */
22942
22943
22944 /**
22945  * @class Roo.data.Tree
22946  * @extends Roo.util.Observable
22947  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22948  * in the tree have most standard DOM functionality.
22949  * @constructor
22950  * @param {Node} root (optional) The root node
22951  */
22952 Roo.data.Tree = function(root){
22953    this.nodeHash = {};
22954    /**
22955     * The root node for this tree
22956     * @type Node
22957     */
22958    this.root = null;
22959    if(root){
22960        this.setRootNode(root);
22961    }
22962    this.addEvents({
22963        /**
22964         * @event append
22965         * Fires when a new child node is appended to a node in this tree.
22966         * @param {Tree} tree The owner tree
22967         * @param {Node} parent The parent node
22968         * @param {Node} node The newly appended node
22969         * @param {Number} index The index of the newly appended node
22970         */
22971        "append" : true,
22972        /**
22973         * @event remove
22974         * Fires when a child node is removed from a node in this tree.
22975         * @param {Tree} tree The owner tree
22976         * @param {Node} parent The parent node
22977         * @param {Node} node The child node removed
22978         */
22979        "remove" : true,
22980        /**
22981         * @event move
22982         * Fires when a node is moved to a new location in the tree
22983         * @param {Tree} tree The owner tree
22984         * @param {Node} node The node moved
22985         * @param {Node} oldParent The old parent of this node
22986         * @param {Node} newParent The new parent of this node
22987         * @param {Number} index The index it was moved to
22988         */
22989        "move" : true,
22990        /**
22991         * @event insert
22992         * Fires when a new child node is inserted in a node in this tree.
22993         * @param {Tree} tree The owner tree
22994         * @param {Node} parent The parent node
22995         * @param {Node} node The child node inserted
22996         * @param {Node} refNode The child node the node was inserted before
22997         */
22998        "insert" : true,
22999        /**
23000         * @event beforeappend
23001         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23002         * @param {Tree} tree The owner tree
23003         * @param {Node} parent The parent node
23004         * @param {Node} node The child node to be appended
23005         */
23006        "beforeappend" : true,
23007        /**
23008         * @event beforeremove
23009         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23010         * @param {Tree} tree The owner tree
23011         * @param {Node} parent The parent node
23012         * @param {Node} node The child node to be removed
23013         */
23014        "beforeremove" : true,
23015        /**
23016         * @event beforemove
23017         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23018         * @param {Tree} tree The owner tree
23019         * @param {Node} node The node being moved
23020         * @param {Node} oldParent The parent of the node
23021         * @param {Node} newParent The new parent the node is moving to
23022         * @param {Number} index The index it is being moved to
23023         */
23024        "beforemove" : true,
23025        /**
23026         * @event beforeinsert
23027         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23028         * @param {Tree} tree The owner tree
23029         * @param {Node} parent The parent node
23030         * @param {Node} node The child node to be inserted
23031         * @param {Node} refNode The child node the node is being inserted before
23032         */
23033        "beforeinsert" : true
23034    });
23035
23036     Roo.data.Tree.superclass.constructor.call(this);
23037 };
23038
23039 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23040     pathSeparator: "/",
23041
23042     proxyNodeEvent : function(){
23043         return this.fireEvent.apply(this, arguments);
23044     },
23045
23046     /**
23047      * Returns the root node for this tree.
23048      * @return {Node}
23049      */
23050     getRootNode : function(){
23051         return this.root;
23052     },
23053
23054     /**
23055      * Sets the root node for this tree.
23056      * @param {Node} node
23057      * @return {Node}
23058      */
23059     setRootNode : function(node){
23060         this.root = node;
23061         node.ownerTree = this;
23062         node.isRoot = true;
23063         this.registerNode(node);
23064         return node;
23065     },
23066
23067     /**
23068      * Gets a node in this tree by its id.
23069      * @param {String} id
23070      * @return {Node}
23071      */
23072     getNodeById : function(id){
23073         return this.nodeHash[id];
23074     },
23075
23076     registerNode : function(node){
23077         this.nodeHash[node.id] = node;
23078     },
23079
23080     unregisterNode : function(node){
23081         delete this.nodeHash[node.id];
23082     },
23083
23084     toString : function(){
23085         return "[Tree"+(this.id?" "+this.id:"")+"]";
23086     }
23087 });
23088
23089 /**
23090  * @class Roo.data.Node
23091  * @extends Roo.util.Observable
23092  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23093  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23094  * @constructor
23095  * @param {Object} attributes The attributes/config for the node
23096  */
23097 Roo.data.Node = function(attributes){
23098     /**
23099      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23100      * @type {Object}
23101      */
23102     this.attributes = attributes || {};
23103     this.leaf = this.attributes.leaf;
23104     /**
23105      * The node id. @type String
23106      */
23107     this.id = this.attributes.id;
23108     if(!this.id){
23109         this.id = Roo.id(null, "ynode-");
23110         this.attributes.id = this.id;
23111     }
23112      
23113     
23114     /**
23115      * All child nodes of this node. @type Array
23116      */
23117     this.childNodes = [];
23118     if(!this.childNodes.indexOf){ // indexOf is a must
23119         this.childNodes.indexOf = function(o){
23120             for(var i = 0, len = this.length; i < len; i++){
23121                 if(this[i] == o) {
23122                     return i;
23123                 }
23124             }
23125             return -1;
23126         };
23127     }
23128     /**
23129      * The parent node for this node. @type Node
23130      */
23131     this.parentNode = null;
23132     /**
23133      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23134      */
23135     this.firstChild = null;
23136     /**
23137      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23138      */
23139     this.lastChild = null;
23140     /**
23141      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23142      */
23143     this.previousSibling = null;
23144     /**
23145      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23146      */
23147     this.nextSibling = null;
23148
23149     this.addEvents({
23150        /**
23151         * @event append
23152         * Fires when a new child node is appended
23153         * @param {Tree} tree The owner tree
23154         * @param {Node} this This node
23155         * @param {Node} node The newly appended node
23156         * @param {Number} index The index of the newly appended node
23157         */
23158        "append" : true,
23159        /**
23160         * @event remove
23161         * Fires when a child node is removed
23162         * @param {Tree} tree The owner tree
23163         * @param {Node} this This node
23164         * @param {Node} node The removed node
23165         */
23166        "remove" : true,
23167        /**
23168         * @event move
23169         * Fires when this node is moved to a new location in the tree
23170         * @param {Tree} tree The owner tree
23171         * @param {Node} this This node
23172         * @param {Node} oldParent The old parent of this node
23173         * @param {Node} newParent The new parent of this node
23174         * @param {Number} index The index it was moved to
23175         */
23176        "move" : true,
23177        /**
23178         * @event insert
23179         * Fires when a new child node is inserted.
23180         * @param {Tree} tree The owner tree
23181         * @param {Node} this This node
23182         * @param {Node} node The child node inserted
23183         * @param {Node} refNode The child node the node was inserted before
23184         */
23185        "insert" : true,
23186        /**
23187         * @event beforeappend
23188         * Fires before a new child is appended, return false to cancel the append.
23189         * @param {Tree} tree The owner tree
23190         * @param {Node} this This node
23191         * @param {Node} node The child node to be appended
23192         */
23193        "beforeappend" : true,
23194        /**
23195         * @event beforeremove
23196         * Fires before a child is removed, return false to cancel the remove.
23197         * @param {Tree} tree The owner tree
23198         * @param {Node} this This node
23199         * @param {Node} node The child node to be removed
23200         */
23201        "beforeremove" : true,
23202        /**
23203         * @event beforemove
23204         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23205         * @param {Tree} tree The owner tree
23206         * @param {Node} this This node
23207         * @param {Node} oldParent The parent of this node
23208         * @param {Node} newParent The new parent this node is moving to
23209         * @param {Number} index The index it is being moved to
23210         */
23211        "beforemove" : true,
23212        /**
23213         * @event beforeinsert
23214         * Fires before a new child is inserted, return false to cancel the insert.
23215         * @param {Tree} tree The owner tree
23216         * @param {Node} this This node
23217         * @param {Node} node The child node to be inserted
23218         * @param {Node} refNode The child node the node is being inserted before
23219         */
23220        "beforeinsert" : true
23221    });
23222     this.listeners = this.attributes.listeners;
23223     Roo.data.Node.superclass.constructor.call(this);
23224 };
23225
23226 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23227     fireEvent : function(evtName){
23228         // first do standard event for this node
23229         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23230             return false;
23231         }
23232         // then bubble it up to the tree if the event wasn't cancelled
23233         var ot = this.getOwnerTree();
23234         if(ot){
23235             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23236                 return false;
23237             }
23238         }
23239         return true;
23240     },
23241
23242     /**
23243      * Returns true if this node is a leaf
23244      * @return {Boolean}
23245      */
23246     isLeaf : function(){
23247         return this.leaf === true;
23248     },
23249
23250     // private
23251     setFirstChild : function(node){
23252         this.firstChild = node;
23253     },
23254
23255     //private
23256     setLastChild : function(node){
23257         this.lastChild = node;
23258     },
23259
23260
23261     /**
23262      * Returns true if this node is the last child of its parent
23263      * @return {Boolean}
23264      */
23265     isLast : function(){
23266        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23267     },
23268
23269     /**
23270      * Returns true if this node is the first child of its parent
23271      * @return {Boolean}
23272      */
23273     isFirst : function(){
23274        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23275     },
23276
23277     hasChildNodes : function(){
23278         return !this.isLeaf() && this.childNodes.length > 0;
23279     },
23280
23281     /**
23282      * Insert node(s) as the last child node of this node.
23283      * @param {Node/Array} node The node or Array of nodes to append
23284      * @return {Node} The appended node if single append, or null if an array was passed
23285      */
23286     appendChild : function(node){
23287         var multi = false;
23288         if(node instanceof Array){
23289             multi = node;
23290         }else if(arguments.length > 1){
23291             multi = arguments;
23292         }
23293         // if passed an array or multiple args do them one by one
23294         if(multi){
23295             for(var i = 0, len = multi.length; i < len; i++) {
23296                 this.appendChild(multi[i]);
23297             }
23298         }else{
23299             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23300                 return false;
23301             }
23302             var index = this.childNodes.length;
23303             var oldParent = node.parentNode;
23304             // it's a move, make sure we move it cleanly
23305             if(oldParent){
23306                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23307                     return false;
23308                 }
23309                 oldParent.removeChild(node);
23310             }
23311             index = this.childNodes.length;
23312             if(index == 0){
23313                 this.setFirstChild(node);
23314             }
23315             this.childNodes.push(node);
23316             node.parentNode = this;
23317             var ps = this.childNodes[index-1];
23318             if(ps){
23319                 node.previousSibling = ps;
23320                 ps.nextSibling = node;
23321             }else{
23322                 node.previousSibling = null;
23323             }
23324             node.nextSibling = null;
23325             this.setLastChild(node);
23326             node.setOwnerTree(this.getOwnerTree());
23327             this.fireEvent("append", this.ownerTree, this, node, index);
23328             if(oldParent){
23329                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23330             }
23331             return node;
23332         }
23333     },
23334
23335     /**
23336      * Removes a child node from this node.
23337      * @param {Node} node The node to remove
23338      * @return {Node} The removed node
23339      */
23340     removeChild : function(node){
23341         var index = this.childNodes.indexOf(node);
23342         if(index == -1){
23343             return false;
23344         }
23345         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23346             return false;
23347         }
23348
23349         // remove it from childNodes collection
23350         this.childNodes.splice(index, 1);
23351
23352         // update siblings
23353         if(node.previousSibling){
23354             node.previousSibling.nextSibling = node.nextSibling;
23355         }
23356         if(node.nextSibling){
23357             node.nextSibling.previousSibling = node.previousSibling;
23358         }
23359
23360         // update child refs
23361         if(this.firstChild == node){
23362             this.setFirstChild(node.nextSibling);
23363         }
23364         if(this.lastChild == node){
23365             this.setLastChild(node.previousSibling);
23366         }
23367
23368         node.setOwnerTree(null);
23369         // clear any references from the node
23370         node.parentNode = null;
23371         node.previousSibling = null;
23372         node.nextSibling = null;
23373         this.fireEvent("remove", this.ownerTree, this, node);
23374         return node;
23375     },
23376
23377     /**
23378      * Inserts the first node before the second node in this nodes childNodes collection.
23379      * @param {Node} node The node to insert
23380      * @param {Node} refNode The node to insert before (if null the node is appended)
23381      * @return {Node} The inserted node
23382      */
23383     insertBefore : function(node, refNode){
23384         if(!refNode){ // like standard Dom, refNode can be null for append
23385             return this.appendChild(node);
23386         }
23387         // nothing to do
23388         if(node == refNode){
23389             return false;
23390         }
23391
23392         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23393             return false;
23394         }
23395         var index = this.childNodes.indexOf(refNode);
23396         var oldParent = node.parentNode;
23397         var refIndex = index;
23398
23399         // when moving internally, indexes will change after remove
23400         if(oldParent == this && this.childNodes.indexOf(node) < index){
23401             refIndex--;
23402         }
23403
23404         // it's a move, make sure we move it cleanly
23405         if(oldParent){
23406             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23407                 return false;
23408             }
23409             oldParent.removeChild(node);
23410         }
23411         if(refIndex == 0){
23412             this.setFirstChild(node);
23413         }
23414         this.childNodes.splice(refIndex, 0, node);
23415         node.parentNode = this;
23416         var ps = this.childNodes[refIndex-1];
23417         if(ps){
23418             node.previousSibling = ps;
23419             ps.nextSibling = node;
23420         }else{
23421             node.previousSibling = null;
23422         }
23423         node.nextSibling = refNode;
23424         refNode.previousSibling = node;
23425         node.setOwnerTree(this.getOwnerTree());
23426         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23427         if(oldParent){
23428             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23429         }
23430         return node;
23431     },
23432
23433     /**
23434      * Returns the child node at the specified index.
23435      * @param {Number} index
23436      * @return {Node}
23437      */
23438     item : function(index){
23439         return this.childNodes[index];
23440     },
23441
23442     /**
23443      * Replaces one child node in this node with another.
23444      * @param {Node} newChild The replacement node
23445      * @param {Node} oldChild The node to replace
23446      * @return {Node} The replaced node
23447      */
23448     replaceChild : function(newChild, oldChild){
23449         this.insertBefore(newChild, oldChild);
23450         this.removeChild(oldChild);
23451         return oldChild;
23452     },
23453
23454     /**
23455      * Returns the index of a child node
23456      * @param {Node} node
23457      * @return {Number} The index of the node or -1 if it was not found
23458      */
23459     indexOf : function(child){
23460         return this.childNodes.indexOf(child);
23461     },
23462
23463     /**
23464      * Returns the tree this node is in.
23465      * @return {Tree}
23466      */
23467     getOwnerTree : function(){
23468         // if it doesn't have one, look for one
23469         if(!this.ownerTree){
23470             var p = this;
23471             while(p){
23472                 if(p.ownerTree){
23473                     this.ownerTree = p.ownerTree;
23474                     break;
23475                 }
23476                 p = p.parentNode;
23477             }
23478         }
23479         return this.ownerTree;
23480     },
23481
23482     /**
23483      * Returns depth of this node (the root node has a depth of 0)
23484      * @return {Number}
23485      */
23486     getDepth : function(){
23487         var depth = 0;
23488         var p = this;
23489         while(p.parentNode){
23490             ++depth;
23491             p = p.parentNode;
23492         }
23493         return depth;
23494     },
23495
23496     // private
23497     setOwnerTree : function(tree){
23498         // if it's move, we need to update everyone
23499         if(tree != this.ownerTree){
23500             if(this.ownerTree){
23501                 this.ownerTree.unregisterNode(this);
23502             }
23503             this.ownerTree = tree;
23504             var cs = this.childNodes;
23505             for(var i = 0, len = cs.length; i < len; i++) {
23506                 cs[i].setOwnerTree(tree);
23507             }
23508             if(tree){
23509                 tree.registerNode(this);
23510             }
23511         }
23512     },
23513
23514     /**
23515      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23516      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23517      * @return {String} The path
23518      */
23519     getPath : function(attr){
23520         attr = attr || "id";
23521         var p = this.parentNode;
23522         var b = [this.attributes[attr]];
23523         while(p){
23524             b.unshift(p.attributes[attr]);
23525             p = p.parentNode;
23526         }
23527         var sep = this.getOwnerTree().pathSeparator;
23528         return sep + b.join(sep);
23529     },
23530
23531     /**
23532      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23533      * function call will be the scope provided or the current node. The arguments to the function
23534      * will be the args provided or the current node. If the function returns false at any point,
23535      * the bubble is stopped.
23536      * @param {Function} fn The function to call
23537      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23538      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23539      */
23540     bubble : function(fn, scope, args){
23541         var p = this;
23542         while(p){
23543             if(fn.call(scope || p, args || p) === false){
23544                 break;
23545             }
23546             p = p.parentNode;
23547         }
23548     },
23549
23550     /**
23551      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23552      * function call will be the scope provided or the current node. The arguments to the function
23553      * will be the args provided or the current node. If the function returns false at any point,
23554      * the cascade is stopped on that branch.
23555      * @param {Function} fn The function to call
23556      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23557      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23558      */
23559     cascade : function(fn, scope, args){
23560         if(fn.call(scope || this, args || this) !== false){
23561             var cs = this.childNodes;
23562             for(var i = 0, len = cs.length; i < len; i++) {
23563                 cs[i].cascade(fn, scope, args);
23564             }
23565         }
23566     },
23567
23568     /**
23569      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23570      * function call will be the scope provided or the current node. The arguments to the function
23571      * will be the args provided or the current node. If the function returns false at any point,
23572      * the iteration stops.
23573      * @param {Function} fn The function to call
23574      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23575      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23576      */
23577     eachChild : function(fn, scope, args){
23578         var cs = this.childNodes;
23579         for(var i = 0, len = cs.length; i < len; i++) {
23580                 if(fn.call(scope || this, args || cs[i]) === false){
23581                     break;
23582                 }
23583         }
23584     },
23585
23586     /**
23587      * Finds the first child that has the attribute with the specified value.
23588      * @param {String} attribute The attribute name
23589      * @param {Mixed} value The value to search for
23590      * @return {Node} The found child or null if none was found
23591      */
23592     findChild : function(attribute, value){
23593         var cs = this.childNodes;
23594         for(var i = 0, len = cs.length; i < len; i++) {
23595                 if(cs[i].attributes[attribute] == value){
23596                     return cs[i];
23597                 }
23598         }
23599         return null;
23600     },
23601
23602     /**
23603      * Finds the first child by a custom function. The child matches if the function passed
23604      * returns true.
23605      * @param {Function} fn
23606      * @param {Object} scope (optional)
23607      * @return {Node} The found child or null if none was found
23608      */
23609     findChildBy : function(fn, scope){
23610         var cs = this.childNodes;
23611         for(var i = 0, len = cs.length; i < len; i++) {
23612                 if(fn.call(scope||cs[i], cs[i]) === true){
23613                     return cs[i];
23614                 }
23615         }
23616         return null;
23617     },
23618
23619     /**
23620      * Sorts this nodes children using the supplied sort function
23621      * @param {Function} fn
23622      * @param {Object} scope (optional)
23623      */
23624     sort : function(fn, scope){
23625         var cs = this.childNodes;
23626         var len = cs.length;
23627         if(len > 0){
23628             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23629             cs.sort(sortFn);
23630             for(var i = 0; i < len; i++){
23631                 var n = cs[i];
23632                 n.previousSibling = cs[i-1];
23633                 n.nextSibling = cs[i+1];
23634                 if(i == 0){
23635                     this.setFirstChild(n);
23636                 }
23637                 if(i == len-1){
23638                     this.setLastChild(n);
23639                 }
23640             }
23641         }
23642     },
23643
23644     /**
23645      * Returns true if this node is an ancestor (at any point) of the passed node.
23646      * @param {Node} node
23647      * @return {Boolean}
23648      */
23649     contains : function(node){
23650         return node.isAncestor(this);
23651     },
23652
23653     /**
23654      * Returns true if the passed node is an ancestor (at any point) of this node.
23655      * @param {Node} node
23656      * @return {Boolean}
23657      */
23658     isAncestor : function(node){
23659         var p = this.parentNode;
23660         while(p){
23661             if(p == node){
23662                 return true;
23663             }
23664             p = p.parentNode;
23665         }
23666         return false;
23667     },
23668
23669     toString : function(){
23670         return "[Node"+(this.id?" "+this.id:"")+"]";
23671     }
23672 });/*
23673  * Based on:
23674  * Ext JS Library 1.1.1
23675  * Copyright(c) 2006-2007, Ext JS, LLC.
23676  *
23677  * Originally Released Under LGPL - original licence link has changed is not relivant.
23678  *
23679  * Fork - LGPL
23680  * <script type="text/javascript">
23681  */
23682  (function(){ 
23683 /**
23684  * @class Roo.Layer
23685  * @extends Roo.Element
23686  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23687  * automatic maintaining of shadow/shim positions.
23688  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23689  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23690  * you can pass a string with a CSS class name. False turns off the shadow.
23691  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23692  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23693  * @cfg {String} cls CSS class to add to the element
23694  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23695  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23696  * @constructor
23697  * @param {Object} config An object with config options.
23698  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23699  */
23700
23701 Roo.Layer = function(config, existingEl){
23702     config = config || {};
23703     var dh = Roo.DomHelper;
23704     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23705     if(existingEl){
23706         this.dom = Roo.getDom(existingEl);
23707     }
23708     if(!this.dom){
23709         var o = config.dh || {tag: "div", cls: "x-layer"};
23710         this.dom = dh.append(pel, o);
23711     }
23712     if(config.cls){
23713         this.addClass(config.cls);
23714     }
23715     this.constrain = config.constrain !== false;
23716     this.visibilityMode = Roo.Element.VISIBILITY;
23717     if(config.id){
23718         this.id = this.dom.id = config.id;
23719     }else{
23720         this.id = Roo.id(this.dom);
23721     }
23722     this.zindex = config.zindex || this.getZIndex();
23723     this.position("absolute", this.zindex);
23724     if(config.shadow){
23725         this.shadowOffset = config.shadowOffset || 4;
23726         this.shadow = new Roo.Shadow({
23727             offset : this.shadowOffset,
23728             mode : config.shadow
23729         });
23730     }else{
23731         this.shadowOffset = 0;
23732     }
23733     this.useShim = config.shim !== false && Roo.useShims;
23734     this.useDisplay = config.useDisplay;
23735     this.hide();
23736 };
23737
23738 var supr = Roo.Element.prototype;
23739
23740 // shims are shared among layer to keep from having 100 iframes
23741 var shims = [];
23742
23743 Roo.extend(Roo.Layer, Roo.Element, {
23744
23745     getZIndex : function(){
23746         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23747     },
23748
23749     getShim : function(){
23750         if(!this.useShim){
23751             return null;
23752         }
23753         if(this.shim){
23754             return this.shim;
23755         }
23756         var shim = shims.shift();
23757         if(!shim){
23758             shim = this.createShim();
23759             shim.enableDisplayMode('block');
23760             shim.dom.style.display = 'none';
23761             shim.dom.style.visibility = 'visible';
23762         }
23763         var pn = this.dom.parentNode;
23764         if(shim.dom.parentNode != pn){
23765             pn.insertBefore(shim.dom, this.dom);
23766         }
23767         shim.setStyle('z-index', this.getZIndex()-2);
23768         this.shim = shim;
23769         return shim;
23770     },
23771
23772     hideShim : function(){
23773         if(this.shim){
23774             this.shim.setDisplayed(false);
23775             shims.push(this.shim);
23776             delete this.shim;
23777         }
23778     },
23779
23780     disableShadow : function(){
23781         if(this.shadow){
23782             this.shadowDisabled = true;
23783             this.shadow.hide();
23784             this.lastShadowOffset = this.shadowOffset;
23785             this.shadowOffset = 0;
23786         }
23787     },
23788
23789     enableShadow : function(show){
23790         if(this.shadow){
23791             this.shadowDisabled = false;
23792             this.shadowOffset = this.lastShadowOffset;
23793             delete this.lastShadowOffset;
23794             if(show){
23795                 this.sync(true);
23796             }
23797         }
23798     },
23799
23800     // private
23801     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23802     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23803     sync : function(doShow){
23804         var sw = this.shadow;
23805         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23806             var sh = this.getShim();
23807
23808             var w = this.getWidth(),
23809                 h = this.getHeight();
23810
23811             var l = this.getLeft(true),
23812                 t = this.getTop(true);
23813
23814             if(sw && !this.shadowDisabled){
23815                 if(doShow && !sw.isVisible()){
23816                     sw.show(this);
23817                 }else{
23818                     sw.realign(l, t, w, h);
23819                 }
23820                 if(sh){
23821                     if(doShow){
23822                        sh.show();
23823                     }
23824                     // fit the shim behind the shadow, so it is shimmed too
23825                     var a = sw.adjusts, s = sh.dom.style;
23826                     s.left = (Math.min(l, l+a.l))+"px";
23827                     s.top = (Math.min(t, t+a.t))+"px";
23828                     s.width = (w+a.w)+"px";
23829                     s.height = (h+a.h)+"px";
23830                 }
23831             }else if(sh){
23832                 if(doShow){
23833                    sh.show();
23834                 }
23835                 sh.setSize(w, h);
23836                 sh.setLeftTop(l, t);
23837             }
23838             
23839         }
23840     },
23841
23842     // private
23843     destroy : function(){
23844         this.hideShim();
23845         if(this.shadow){
23846             this.shadow.hide();
23847         }
23848         this.removeAllListeners();
23849         var pn = this.dom.parentNode;
23850         if(pn){
23851             pn.removeChild(this.dom);
23852         }
23853         Roo.Element.uncache(this.id);
23854     },
23855
23856     remove : function(){
23857         this.destroy();
23858     },
23859
23860     // private
23861     beginUpdate : function(){
23862         this.updating = true;
23863     },
23864
23865     // private
23866     endUpdate : function(){
23867         this.updating = false;
23868         this.sync(true);
23869     },
23870
23871     // private
23872     hideUnders : function(negOffset){
23873         if(this.shadow){
23874             this.shadow.hide();
23875         }
23876         this.hideShim();
23877     },
23878
23879     // private
23880     constrainXY : function(){
23881         if(this.constrain){
23882             var vw = Roo.lib.Dom.getViewWidth(),
23883                 vh = Roo.lib.Dom.getViewHeight();
23884             var s = Roo.get(document).getScroll();
23885
23886             var xy = this.getXY();
23887             var x = xy[0], y = xy[1];   
23888             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23889             // only move it if it needs it
23890             var moved = false;
23891             // first validate right/bottom
23892             if((x + w) > vw+s.left){
23893                 x = vw - w - this.shadowOffset;
23894                 moved = true;
23895             }
23896             if((y + h) > vh+s.top){
23897                 y = vh - h - this.shadowOffset;
23898                 moved = true;
23899             }
23900             // then make sure top/left isn't negative
23901             if(x < s.left){
23902                 x = s.left;
23903                 moved = true;
23904             }
23905             if(y < s.top){
23906                 y = s.top;
23907                 moved = true;
23908             }
23909             if(moved){
23910                 if(this.avoidY){
23911                     var ay = this.avoidY;
23912                     if(y <= ay && (y+h) >= ay){
23913                         y = ay-h-5;   
23914                     }
23915                 }
23916                 xy = [x, y];
23917                 this.storeXY(xy);
23918                 supr.setXY.call(this, xy);
23919                 this.sync();
23920             }
23921         }
23922     },
23923
23924     isVisible : function(){
23925         return this.visible;    
23926     },
23927
23928     // private
23929     showAction : function(){
23930         this.visible = true; // track visibility to prevent getStyle calls
23931         if(this.useDisplay === true){
23932             this.setDisplayed("");
23933         }else if(this.lastXY){
23934             supr.setXY.call(this, this.lastXY);
23935         }else if(this.lastLT){
23936             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23937         }
23938     },
23939
23940     // private
23941     hideAction : function(){
23942         this.visible = false;
23943         if(this.useDisplay === true){
23944             this.setDisplayed(false);
23945         }else{
23946             this.setLeftTop(-10000,-10000);
23947         }
23948     },
23949
23950     // overridden Element method
23951     setVisible : function(v, a, d, c, e){
23952         if(v){
23953             this.showAction();
23954         }
23955         if(a && v){
23956             var cb = function(){
23957                 this.sync(true);
23958                 if(c){
23959                     c();
23960                 }
23961             }.createDelegate(this);
23962             supr.setVisible.call(this, true, true, d, cb, e);
23963         }else{
23964             if(!v){
23965                 this.hideUnders(true);
23966             }
23967             var cb = c;
23968             if(a){
23969                 cb = function(){
23970                     this.hideAction();
23971                     if(c){
23972                         c();
23973                     }
23974                 }.createDelegate(this);
23975             }
23976             supr.setVisible.call(this, v, a, d, cb, e);
23977             if(v){
23978                 this.sync(true);
23979             }else if(!a){
23980                 this.hideAction();
23981             }
23982         }
23983     },
23984
23985     storeXY : function(xy){
23986         delete this.lastLT;
23987         this.lastXY = xy;
23988     },
23989
23990     storeLeftTop : function(left, top){
23991         delete this.lastXY;
23992         this.lastLT = [left, top];
23993     },
23994
23995     // private
23996     beforeFx : function(){
23997         this.beforeAction();
23998         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23999     },
24000
24001     // private
24002     afterFx : function(){
24003         Roo.Layer.superclass.afterFx.apply(this, arguments);
24004         this.sync(this.isVisible());
24005     },
24006
24007     // private
24008     beforeAction : function(){
24009         if(!this.updating && this.shadow){
24010             this.shadow.hide();
24011         }
24012     },
24013
24014     // overridden Element method
24015     setLeft : function(left){
24016         this.storeLeftTop(left, this.getTop(true));
24017         supr.setLeft.apply(this, arguments);
24018         this.sync();
24019     },
24020
24021     setTop : function(top){
24022         this.storeLeftTop(this.getLeft(true), top);
24023         supr.setTop.apply(this, arguments);
24024         this.sync();
24025     },
24026
24027     setLeftTop : function(left, top){
24028         this.storeLeftTop(left, top);
24029         supr.setLeftTop.apply(this, arguments);
24030         this.sync();
24031     },
24032
24033     setXY : function(xy, a, d, c, e){
24034         this.fixDisplay();
24035         this.beforeAction();
24036         this.storeXY(xy);
24037         var cb = this.createCB(c);
24038         supr.setXY.call(this, xy, a, d, cb, e);
24039         if(!a){
24040             cb();
24041         }
24042     },
24043
24044     // private
24045     createCB : function(c){
24046         var el = this;
24047         return function(){
24048             el.constrainXY();
24049             el.sync(true);
24050             if(c){
24051                 c();
24052             }
24053         };
24054     },
24055
24056     // overridden Element method
24057     setX : function(x, a, d, c, e){
24058         this.setXY([x, this.getY()], a, d, c, e);
24059     },
24060
24061     // overridden Element method
24062     setY : function(y, a, d, c, e){
24063         this.setXY([this.getX(), y], a, d, c, e);
24064     },
24065
24066     // overridden Element method
24067     setSize : function(w, h, a, d, c, e){
24068         this.beforeAction();
24069         var cb = this.createCB(c);
24070         supr.setSize.call(this, w, h, a, d, cb, e);
24071         if(!a){
24072             cb();
24073         }
24074     },
24075
24076     // overridden Element method
24077     setWidth : function(w, a, d, c, e){
24078         this.beforeAction();
24079         var cb = this.createCB(c);
24080         supr.setWidth.call(this, w, a, d, cb, e);
24081         if(!a){
24082             cb();
24083         }
24084     },
24085
24086     // overridden Element method
24087     setHeight : function(h, a, d, c, e){
24088         this.beforeAction();
24089         var cb = this.createCB(c);
24090         supr.setHeight.call(this, h, a, d, cb, e);
24091         if(!a){
24092             cb();
24093         }
24094     },
24095
24096     // overridden Element method
24097     setBounds : function(x, y, w, h, a, d, c, e){
24098         this.beforeAction();
24099         var cb = this.createCB(c);
24100         if(!a){
24101             this.storeXY([x, y]);
24102             supr.setXY.call(this, [x, y]);
24103             supr.setSize.call(this, w, h, a, d, cb, e);
24104             cb();
24105         }else{
24106             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24107         }
24108         return this;
24109     },
24110     
24111     /**
24112      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24113      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24114      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24115      * @param {Number} zindex The new z-index to set
24116      * @return {this} The Layer
24117      */
24118     setZIndex : function(zindex){
24119         this.zindex = zindex;
24120         this.setStyle("z-index", zindex + 2);
24121         if(this.shadow){
24122             this.shadow.setZIndex(zindex + 1);
24123         }
24124         if(this.shim){
24125             this.shim.setStyle("z-index", zindex);
24126         }
24127     }
24128 });
24129 })();/*
24130  * Based on:
24131  * Ext JS Library 1.1.1
24132  * Copyright(c) 2006-2007, Ext JS, LLC.
24133  *
24134  * Originally Released Under LGPL - original licence link has changed is not relivant.
24135  *
24136  * Fork - LGPL
24137  * <script type="text/javascript">
24138  */
24139
24140
24141 /**
24142  * @class Roo.Shadow
24143  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24144  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24145  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24146  * @constructor
24147  * Create a new Shadow
24148  * @param {Object} config The config object
24149  */
24150 Roo.Shadow = function(config){
24151     Roo.apply(this, config);
24152     if(typeof this.mode != "string"){
24153         this.mode = this.defaultMode;
24154     }
24155     var o = this.offset, a = {h: 0};
24156     var rad = Math.floor(this.offset/2);
24157     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24158         case "drop":
24159             a.w = 0;
24160             a.l = a.t = o;
24161             a.t -= 1;
24162             if(Roo.isIE){
24163                 a.l -= this.offset + rad;
24164                 a.t -= this.offset + rad;
24165                 a.w -= rad;
24166                 a.h -= rad;
24167                 a.t += 1;
24168             }
24169         break;
24170         case "sides":
24171             a.w = (o*2);
24172             a.l = -o;
24173             a.t = o-1;
24174             if(Roo.isIE){
24175                 a.l -= (this.offset - rad);
24176                 a.t -= this.offset + rad;
24177                 a.l += 1;
24178                 a.w -= (this.offset - rad)*2;
24179                 a.w -= rad + 1;
24180                 a.h -= 1;
24181             }
24182         break;
24183         case "frame":
24184             a.w = a.h = (o*2);
24185             a.l = a.t = -o;
24186             a.t += 1;
24187             a.h -= 2;
24188             if(Roo.isIE){
24189                 a.l -= (this.offset - rad);
24190                 a.t -= (this.offset - rad);
24191                 a.l += 1;
24192                 a.w -= (this.offset + rad + 1);
24193                 a.h -= (this.offset + rad);
24194                 a.h += 1;
24195             }
24196         break;
24197     };
24198
24199     this.adjusts = a;
24200 };
24201
24202 Roo.Shadow.prototype = {
24203     /**
24204      * @cfg {String} mode
24205      * The shadow display mode.  Supports the following options:<br />
24206      * sides: Shadow displays on both sides and bottom only<br />
24207      * frame: Shadow displays equally on all four sides<br />
24208      * drop: Traditional bottom-right drop shadow (default)
24209      */
24210     /**
24211      * @cfg {String} offset
24212      * The number of pixels to offset the shadow from the element (defaults to 4)
24213      */
24214     offset: 4,
24215
24216     // private
24217     defaultMode: "drop",
24218
24219     /**
24220      * Displays the shadow under the target element
24221      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24222      */
24223     show : function(target){
24224         target = Roo.get(target);
24225         if(!this.el){
24226             this.el = Roo.Shadow.Pool.pull();
24227             if(this.el.dom.nextSibling != target.dom){
24228                 this.el.insertBefore(target);
24229             }
24230         }
24231         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24232         if(Roo.isIE){
24233             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24234         }
24235         this.realign(
24236             target.getLeft(true),
24237             target.getTop(true),
24238             target.getWidth(),
24239             target.getHeight()
24240         );
24241         this.el.dom.style.display = "block";
24242     },
24243
24244     /**
24245      * Returns true if the shadow is visible, else false
24246      */
24247     isVisible : function(){
24248         return this.el ? true : false;  
24249     },
24250
24251     /**
24252      * Direct alignment when values are already available. Show must be called at least once before
24253      * calling this method to ensure it is initialized.
24254      * @param {Number} left The target element left position
24255      * @param {Number} top The target element top position
24256      * @param {Number} width The target element width
24257      * @param {Number} height The target element height
24258      */
24259     realign : function(l, t, w, h){
24260         if(!this.el){
24261             return;
24262         }
24263         var a = this.adjusts, d = this.el.dom, s = d.style;
24264         var iea = 0;
24265         s.left = (l+a.l)+"px";
24266         s.top = (t+a.t)+"px";
24267         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24268  
24269         if(s.width != sws || s.height != shs){
24270             s.width = sws;
24271             s.height = shs;
24272             if(!Roo.isIE){
24273                 var cn = d.childNodes;
24274                 var sww = Math.max(0, (sw-12))+"px";
24275                 cn[0].childNodes[1].style.width = sww;
24276                 cn[1].childNodes[1].style.width = sww;
24277                 cn[2].childNodes[1].style.width = sww;
24278                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24279             }
24280         }
24281     },
24282
24283     /**
24284      * Hides this shadow
24285      */
24286     hide : function(){
24287         if(this.el){
24288             this.el.dom.style.display = "none";
24289             Roo.Shadow.Pool.push(this.el);
24290             delete this.el;
24291         }
24292     },
24293
24294     /**
24295      * Adjust the z-index of this shadow
24296      * @param {Number} zindex The new z-index
24297      */
24298     setZIndex : function(z){
24299         this.zIndex = z;
24300         if(this.el){
24301             this.el.setStyle("z-index", z);
24302         }
24303     }
24304 };
24305
24306 // Private utility class that manages the internal Shadow cache
24307 Roo.Shadow.Pool = function(){
24308     var p = [];
24309     var markup = Roo.isIE ?
24310                  '<div class="x-ie-shadow"></div>' :
24311                  '<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>';
24312     return {
24313         pull : function(){
24314             var sh = p.shift();
24315             if(!sh){
24316                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24317                 sh.autoBoxAdjust = false;
24318             }
24319             return sh;
24320         },
24321
24322         push : function(sh){
24323             p.push(sh);
24324         }
24325     };
24326 }();/*
24327  * Based on:
24328  * Ext JS Library 1.1.1
24329  * Copyright(c) 2006-2007, Ext JS, LLC.
24330  *
24331  * Originally Released Under LGPL - original licence link has changed is not relivant.
24332  *
24333  * Fork - LGPL
24334  * <script type="text/javascript">
24335  */
24336
24337
24338 /**
24339  * @class Roo.SplitBar
24340  * @extends Roo.util.Observable
24341  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24342  * <br><br>
24343  * Usage:
24344  * <pre><code>
24345 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24346                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24347 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24348 split.minSize = 100;
24349 split.maxSize = 600;
24350 split.animate = true;
24351 split.on('moved', splitterMoved);
24352 </code></pre>
24353  * @constructor
24354  * Create a new SplitBar
24355  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24356  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24357  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24358  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24359                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24360                         position of the SplitBar).
24361  */
24362 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24363     
24364     /** @private */
24365     this.el = Roo.get(dragElement, true);
24366     this.el.dom.unselectable = "on";
24367     /** @private */
24368     this.resizingEl = Roo.get(resizingElement, true);
24369
24370     /**
24371      * @private
24372      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24373      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24374      * @type Number
24375      */
24376     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24377     
24378     /**
24379      * The minimum size of the resizing element. (Defaults to 0)
24380      * @type Number
24381      */
24382     this.minSize = 0;
24383     
24384     /**
24385      * The maximum size of the resizing element. (Defaults to 2000)
24386      * @type Number
24387      */
24388     this.maxSize = 2000;
24389     
24390     /**
24391      * Whether to animate the transition to the new size
24392      * @type Boolean
24393      */
24394     this.animate = false;
24395     
24396     /**
24397      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24398      * @type Boolean
24399      */
24400     this.useShim = false;
24401     
24402     /** @private */
24403     this.shim = null;
24404     
24405     if(!existingProxy){
24406         /** @private */
24407         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24408     }else{
24409         this.proxy = Roo.get(existingProxy).dom;
24410     }
24411     /** @private */
24412     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24413     
24414     /** @private */
24415     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24416     
24417     /** @private */
24418     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24419     
24420     /** @private */
24421     this.dragSpecs = {};
24422     
24423     /**
24424      * @private The adapter to use to positon and resize elements
24425      */
24426     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24427     this.adapter.init(this);
24428     
24429     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24430         /** @private */
24431         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24432         this.el.addClass("x-splitbar-h");
24433     }else{
24434         /** @private */
24435         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24436         this.el.addClass("x-splitbar-v");
24437     }
24438     
24439     this.addEvents({
24440         /**
24441          * @event resize
24442          * Fires when the splitter is moved (alias for {@link #event-moved})
24443          * @param {Roo.SplitBar} this
24444          * @param {Number} newSize the new width or height
24445          */
24446         "resize" : true,
24447         /**
24448          * @event moved
24449          * Fires when the splitter is moved
24450          * @param {Roo.SplitBar} this
24451          * @param {Number} newSize the new width or height
24452          */
24453         "moved" : true,
24454         /**
24455          * @event beforeresize
24456          * Fires before the splitter is dragged
24457          * @param {Roo.SplitBar} this
24458          */
24459         "beforeresize" : true,
24460
24461         "beforeapply" : true
24462     });
24463
24464     Roo.util.Observable.call(this);
24465 };
24466
24467 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24468     onStartProxyDrag : function(x, y){
24469         this.fireEvent("beforeresize", this);
24470         if(!this.overlay){
24471             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24472             o.unselectable();
24473             o.enableDisplayMode("block");
24474             // all splitbars share the same overlay
24475             Roo.SplitBar.prototype.overlay = o;
24476         }
24477         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24478         this.overlay.show();
24479         Roo.get(this.proxy).setDisplayed("block");
24480         var size = this.adapter.getElementSize(this);
24481         this.activeMinSize = this.getMinimumSize();;
24482         this.activeMaxSize = this.getMaximumSize();;
24483         var c1 = size - this.activeMinSize;
24484         var c2 = Math.max(this.activeMaxSize - size, 0);
24485         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24486             this.dd.resetConstraints();
24487             this.dd.setXConstraint(
24488                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24489                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24490             );
24491             this.dd.setYConstraint(0, 0);
24492         }else{
24493             this.dd.resetConstraints();
24494             this.dd.setXConstraint(0, 0);
24495             this.dd.setYConstraint(
24496                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24497                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24498             );
24499          }
24500         this.dragSpecs.startSize = size;
24501         this.dragSpecs.startPoint = [x, y];
24502         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24503     },
24504     
24505     /** 
24506      * @private Called after the drag operation by the DDProxy
24507      */
24508     onEndProxyDrag : function(e){
24509         Roo.get(this.proxy).setDisplayed(false);
24510         var endPoint = Roo.lib.Event.getXY(e);
24511         if(this.overlay){
24512             this.overlay.hide();
24513         }
24514         var newSize;
24515         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24516             newSize = this.dragSpecs.startSize + 
24517                 (this.placement == Roo.SplitBar.LEFT ?
24518                     endPoint[0] - this.dragSpecs.startPoint[0] :
24519                     this.dragSpecs.startPoint[0] - endPoint[0]
24520                 );
24521         }else{
24522             newSize = this.dragSpecs.startSize + 
24523                 (this.placement == Roo.SplitBar.TOP ?
24524                     endPoint[1] - this.dragSpecs.startPoint[1] :
24525                     this.dragSpecs.startPoint[1] - endPoint[1]
24526                 );
24527         }
24528         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24529         if(newSize != this.dragSpecs.startSize){
24530             if(this.fireEvent('beforeapply', this, newSize) !== false){
24531                 this.adapter.setElementSize(this, newSize);
24532                 this.fireEvent("moved", this, newSize);
24533                 this.fireEvent("resize", this, newSize);
24534             }
24535         }
24536     },
24537     
24538     /**
24539      * Get the adapter this SplitBar uses
24540      * @return The adapter object
24541      */
24542     getAdapter : function(){
24543         return this.adapter;
24544     },
24545     
24546     /**
24547      * Set the adapter this SplitBar uses
24548      * @param {Object} adapter A SplitBar adapter object
24549      */
24550     setAdapter : function(adapter){
24551         this.adapter = adapter;
24552         this.adapter.init(this);
24553     },
24554     
24555     /**
24556      * Gets the minimum size for the resizing element
24557      * @return {Number} The minimum size
24558      */
24559     getMinimumSize : function(){
24560         return this.minSize;
24561     },
24562     
24563     /**
24564      * Sets the minimum size for the resizing element
24565      * @param {Number} minSize The minimum size
24566      */
24567     setMinimumSize : function(minSize){
24568         this.minSize = minSize;
24569     },
24570     
24571     /**
24572      * Gets the maximum size for the resizing element
24573      * @return {Number} The maximum size
24574      */
24575     getMaximumSize : function(){
24576         return this.maxSize;
24577     },
24578     
24579     /**
24580      * Sets the maximum size for the resizing element
24581      * @param {Number} maxSize The maximum size
24582      */
24583     setMaximumSize : function(maxSize){
24584         this.maxSize = maxSize;
24585     },
24586     
24587     /**
24588      * Sets the initialize size for the resizing element
24589      * @param {Number} size The initial size
24590      */
24591     setCurrentSize : function(size){
24592         var oldAnimate = this.animate;
24593         this.animate = false;
24594         this.adapter.setElementSize(this, size);
24595         this.animate = oldAnimate;
24596     },
24597     
24598     /**
24599      * Destroy this splitbar. 
24600      * @param {Boolean} removeEl True to remove the element
24601      */
24602     destroy : function(removeEl){
24603         if(this.shim){
24604             this.shim.remove();
24605         }
24606         this.dd.unreg();
24607         this.proxy.parentNode.removeChild(this.proxy);
24608         if(removeEl){
24609             this.el.remove();
24610         }
24611     }
24612 });
24613
24614 /**
24615  * @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.
24616  */
24617 Roo.SplitBar.createProxy = function(dir){
24618     var proxy = new Roo.Element(document.createElement("div"));
24619     proxy.unselectable();
24620     var cls = 'x-splitbar-proxy';
24621     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24622     document.body.appendChild(proxy.dom);
24623     return proxy.dom;
24624 };
24625
24626 /** 
24627  * @class Roo.SplitBar.BasicLayoutAdapter
24628  * Default Adapter. It assumes the splitter and resizing element are not positioned
24629  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24630  */
24631 Roo.SplitBar.BasicLayoutAdapter = function(){
24632 };
24633
24634 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24635     // do nothing for now
24636     init : function(s){
24637     
24638     },
24639     /**
24640      * Called before drag operations to get the current size of the resizing element. 
24641      * @param {Roo.SplitBar} s The SplitBar using this adapter
24642      */
24643      getElementSize : function(s){
24644         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24645             return s.resizingEl.getWidth();
24646         }else{
24647             return s.resizingEl.getHeight();
24648         }
24649     },
24650     
24651     /**
24652      * Called after drag operations to set the size of the resizing element.
24653      * @param {Roo.SplitBar} s The SplitBar using this adapter
24654      * @param {Number} newSize The new size to set
24655      * @param {Function} onComplete A function to be invoked when resizing is complete
24656      */
24657     setElementSize : function(s, newSize, onComplete){
24658         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24659             if(!s.animate){
24660                 s.resizingEl.setWidth(newSize);
24661                 if(onComplete){
24662                     onComplete(s, newSize);
24663                 }
24664             }else{
24665                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24666             }
24667         }else{
24668             
24669             if(!s.animate){
24670                 s.resizingEl.setHeight(newSize);
24671                 if(onComplete){
24672                     onComplete(s, newSize);
24673                 }
24674             }else{
24675                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24676             }
24677         }
24678     }
24679 };
24680
24681 /** 
24682  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24683  * @extends Roo.SplitBar.BasicLayoutAdapter
24684  * Adapter that  moves the splitter element to align with the resized sizing element. 
24685  * Used with an absolute positioned SplitBar.
24686  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24687  * document.body, make sure you assign an id to the body element.
24688  */
24689 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24690     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24691     this.container = Roo.get(container);
24692 };
24693
24694 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24695     init : function(s){
24696         this.basic.init(s);
24697     },
24698     
24699     getElementSize : function(s){
24700         return this.basic.getElementSize(s);
24701     },
24702     
24703     setElementSize : function(s, newSize, onComplete){
24704         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24705     },
24706     
24707     moveSplitter : function(s){
24708         var yes = Roo.SplitBar;
24709         switch(s.placement){
24710             case yes.LEFT:
24711                 s.el.setX(s.resizingEl.getRight());
24712                 break;
24713             case yes.RIGHT:
24714                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24715                 break;
24716             case yes.TOP:
24717                 s.el.setY(s.resizingEl.getBottom());
24718                 break;
24719             case yes.BOTTOM:
24720                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24721                 break;
24722         }
24723     }
24724 };
24725
24726 /**
24727  * Orientation constant - Create a vertical SplitBar
24728  * @static
24729  * @type Number
24730  */
24731 Roo.SplitBar.VERTICAL = 1;
24732
24733 /**
24734  * Orientation constant - Create a horizontal SplitBar
24735  * @static
24736  * @type Number
24737  */
24738 Roo.SplitBar.HORIZONTAL = 2;
24739
24740 /**
24741  * Placement constant - The resizing element is to the left of the splitter element
24742  * @static
24743  * @type Number
24744  */
24745 Roo.SplitBar.LEFT = 1;
24746
24747 /**
24748  * Placement constant - The resizing element is to the right of the splitter element
24749  * @static
24750  * @type Number
24751  */
24752 Roo.SplitBar.RIGHT = 2;
24753
24754 /**
24755  * Placement constant - The resizing element is positioned above the splitter element
24756  * @static
24757  * @type Number
24758  */
24759 Roo.SplitBar.TOP = 3;
24760
24761 /**
24762  * Placement constant - The resizing element is positioned under splitter element
24763  * @static
24764  * @type Number
24765  */
24766 Roo.SplitBar.BOTTOM = 4;
24767 /*
24768  * Based on:
24769  * Ext JS Library 1.1.1
24770  * Copyright(c) 2006-2007, Ext JS, LLC.
24771  *
24772  * Originally Released Under LGPL - original licence link has changed is not relivant.
24773  *
24774  * Fork - LGPL
24775  * <script type="text/javascript">
24776  */
24777
24778 /**
24779  * @class Roo.View
24780  * @extends Roo.util.Observable
24781  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24782  * This class also supports single and multi selection modes. <br>
24783  * Create a data model bound view:
24784  <pre><code>
24785  var store = new Roo.data.Store(...);
24786
24787  var view = new Roo.View({
24788     el : "my-element",
24789     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24790  
24791     singleSelect: true,
24792     selectedClass: "ydataview-selected",
24793     store: store
24794  });
24795
24796  // listen for node click?
24797  view.on("click", function(vw, index, node, e){
24798  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24799  });
24800
24801  // load XML data
24802  dataModel.load("foobar.xml");
24803  </code></pre>
24804  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24805  * <br><br>
24806  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24807  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24808  * 
24809  * Note: old style constructor is still suported (container, template, config)
24810  * 
24811  * @constructor
24812  * Create a new View
24813  * @param {Object} config The config object
24814  * 
24815  */
24816 Roo.View = function(config, depreciated_tpl, depreciated_config){
24817     
24818     if (typeof(depreciated_tpl) == 'undefined') {
24819         // new way.. - universal constructor.
24820         Roo.apply(this, config);
24821         this.el  = Roo.get(this.el);
24822     } else {
24823         // old format..
24824         this.el  = Roo.get(config);
24825         this.tpl = depreciated_tpl;
24826         Roo.apply(this, depreciated_config);
24827     }
24828     this.wrapEl  = this.el.wrap().wrap();
24829     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24830     
24831     
24832     if(typeof(this.tpl) == "string"){
24833         this.tpl = new Roo.Template(this.tpl);
24834     } else {
24835         // support xtype ctors..
24836         this.tpl = new Roo.factory(this.tpl, Roo);
24837     }
24838     
24839     
24840     this.tpl.compile();
24841    
24842   
24843     
24844      
24845     /** @private */
24846     this.addEvents({
24847         /**
24848          * @event beforeclick
24849          * Fires before a click is processed. Returns false to cancel the default action.
24850          * @param {Roo.View} this
24851          * @param {Number} index The index of the target node
24852          * @param {HTMLElement} node The target node
24853          * @param {Roo.EventObject} e The raw event object
24854          */
24855             "beforeclick" : true,
24856         /**
24857          * @event click
24858          * Fires when a template node is clicked.
24859          * @param {Roo.View} this
24860          * @param {Number} index The index of the target node
24861          * @param {HTMLElement} node The target node
24862          * @param {Roo.EventObject} e The raw event object
24863          */
24864             "click" : true,
24865         /**
24866          * @event dblclick
24867          * Fires when a template node is double clicked.
24868          * @param {Roo.View} this
24869          * @param {Number} index The index of the target node
24870          * @param {HTMLElement} node The target node
24871          * @param {Roo.EventObject} e The raw event object
24872          */
24873             "dblclick" : true,
24874         /**
24875          * @event contextmenu
24876          * Fires when a template node is right clicked.
24877          * @param {Roo.View} this
24878          * @param {Number} index The index of the target node
24879          * @param {HTMLElement} node The target node
24880          * @param {Roo.EventObject} e The raw event object
24881          */
24882             "contextmenu" : true,
24883         /**
24884          * @event selectionchange
24885          * Fires when the selected nodes change.
24886          * @param {Roo.View} this
24887          * @param {Array} selections Array of the selected nodes
24888          */
24889             "selectionchange" : true,
24890     
24891         /**
24892          * @event beforeselect
24893          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24894          * @param {Roo.View} this
24895          * @param {HTMLElement} node The node to be selected
24896          * @param {Array} selections Array of currently selected nodes
24897          */
24898             "beforeselect" : true,
24899         /**
24900          * @event preparedata
24901          * Fires on every row to render, to allow you to change the data.
24902          * @param {Roo.View} this
24903          * @param {Object} data to be rendered (change this)
24904          */
24905           "preparedata" : true
24906           
24907           
24908         });
24909
24910
24911
24912     this.el.on({
24913         "click": this.onClick,
24914         "dblclick": this.onDblClick,
24915         "contextmenu": this.onContextMenu,
24916         scope:this
24917     });
24918
24919     this.selections = [];
24920     this.nodes = [];
24921     this.cmp = new Roo.CompositeElementLite([]);
24922     if(this.store){
24923         this.store = Roo.factory(this.store, Roo.data);
24924         this.setStore(this.store, true);
24925     }
24926     
24927     if ( this.footer && this.footer.xtype) {
24928            
24929          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24930         
24931         this.footer.dataSource = this.store
24932         this.footer.container = fctr;
24933         this.footer = Roo.factory(this.footer, Roo);
24934         fctr.insertFirst(this.el);
24935         
24936         // this is a bit insane - as the paging toolbar seems to detach the el..
24937 //        dom.parentNode.parentNode.parentNode
24938          // they get detached?
24939     }
24940     
24941     
24942     Roo.View.superclass.constructor.call(this);
24943     
24944     
24945 };
24946
24947 Roo.extend(Roo.View, Roo.util.Observable, {
24948     
24949      /**
24950      * @cfg {Roo.data.Store} store Data store to load data from.
24951      */
24952     store : false,
24953     
24954     /**
24955      * @cfg {String|Roo.Element} el The container element.
24956      */
24957     el : '',
24958     
24959     /**
24960      * @cfg {String|Roo.Template} tpl The template used by this View 
24961      */
24962     tpl : false,
24963     /**
24964      * @cfg {String} dataName the named area of the template to use as the data area
24965      *                          Works with domtemplates roo-name="name"
24966      */
24967     dataName: false,
24968     /**
24969      * @cfg {String} selectedClass The css class to add to selected nodes
24970      */
24971     selectedClass : "x-view-selected",
24972      /**
24973      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24974      */
24975     emptyText : "",
24976     
24977     /**
24978      * @cfg {String} text to display on mask (default Loading)
24979      */
24980     mask : false,
24981     /**
24982      * @cfg {Boolean} multiSelect Allow multiple selection
24983      */
24984     multiSelect : false,
24985     /**
24986      * @cfg {Boolean} singleSelect Allow single selection
24987      */
24988     singleSelect:  false,
24989     
24990     /**
24991      * @cfg {Boolean} toggleSelect - selecting 
24992      */
24993     toggleSelect : false,
24994     
24995     /**
24996      * Returns the element this view is bound to.
24997      * @return {Roo.Element}
24998      */
24999     getEl : function(){
25000         return this.wrapEl;
25001     },
25002     
25003     
25004
25005     /**
25006      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25007      */
25008     refresh : function(){
25009         Roo.log('refresh');
25010         var t = this.tpl;
25011         
25012         // if we are using something like 'domtemplate', then
25013         // the what gets used is:
25014         // t.applySubtemplate(NAME, data, wrapping data..)
25015         // the outer template then get' applied with
25016         //     the store 'extra data'
25017         // and the body get's added to the
25018         //      roo-name="data" node?
25019         //      <span class='roo-tpl-{name}'></span> ?????
25020         
25021         
25022         
25023         this.clearSelections();
25024         this.el.update("");
25025         var html = [];
25026         var records = this.store.getRange();
25027         if(records.length < 1) {
25028             
25029             // is this valid??  = should it render a template??
25030             
25031             this.el.update(this.emptyText);
25032             return;
25033         }
25034         var el = this.el;
25035         if (this.dataName) {
25036             this.el.update(t.apply(this.store.meta)); //????
25037             el = this.el.child('.roo-tpl-' + this.dataName);
25038         }
25039         
25040         for(var i = 0, len = records.length; i < len; i++){
25041             var data = this.prepareData(records[i].data, i, records[i]);
25042             this.fireEvent("preparedata", this, data, i, records[i]);
25043             html[html.length] = Roo.util.Format.trim(
25044                 this.dataName ?
25045                     t.applySubtemplate(this.dataName, data, this.store.meta) :
25046                     t.apply(data)
25047             );
25048         }
25049         
25050         
25051         
25052         el.update(html.join(""));
25053         this.nodes = el.dom.childNodes;
25054         this.updateIndexes(0);
25055     },
25056     
25057
25058     /**
25059      * Function to override to reformat the data that is sent to
25060      * the template for each node.
25061      * DEPRICATED - use the preparedata event handler.
25062      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25063      * a JSON object for an UpdateManager bound view).
25064      */
25065     prepareData : function(data, index, record)
25066     {
25067         this.fireEvent("preparedata", this, data, index, record);
25068         return data;
25069     },
25070
25071     onUpdate : function(ds, record){
25072          Roo.log('on update');   
25073         this.clearSelections();
25074         var index = this.store.indexOf(record);
25075         var n = this.nodes[index];
25076         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25077         n.parentNode.removeChild(n);
25078         this.updateIndexes(index, index);
25079     },
25080
25081     
25082     
25083 // --------- FIXME     
25084     onAdd : function(ds, records, index)
25085     {
25086         Roo.log(['on Add', ds, records, index] );        
25087         this.clearSelections();
25088         if(this.nodes.length == 0){
25089             this.refresh();
25090             return;
25091         }
25092         var n = this.nodes[index];
25093         for(var i = 0, len = records.length; i < len; i++){
25094             var d = this.prepareData(records[i].data, i, records[i]);
25095             if(n){
25096                 this.tpl.insertBefore(n, d);
25097             }else{
25098                 
25099                 this.tpl.append(this.el, d);
25100             }
25101         }
25102         this.updateIndexes(index);
25103     },
25104
25105     onRemove : function(ds, record, index){
25106         Roo.log('onRemove');
25107         this.clearSelections();
25108         var el = this.dataName  ?
25109             this.el.child('.roo-tpl-' + this.dataName) :
25110             this.el; 
25111         
25112         el.dom.removeChild(this.nodes[index]);
25113         this.updateIndexes(index);
25114     },
25115
25116     /**
25117      * Refresh an individual node.
25118      * @param {Number} index
25119      */
25120     refreshNode : function(index){
25121         this.onUpdate(this.store, this.store.getAt(index));
25122     },
25123
25124     updateIndexes : function(startIndex, endIndex){
25125         var ns = this.nodes;
25126         startIndex = startIndex || 0;
25127         endIndex = endIndex || ns.length - 1;
25128         for(var i = startIndex; i <= endIndex; i++){
25129             ns[i].nodeIndex = i;
25130         }
25131     },
25132
25133     /**
25134      * Changes the data store this view uses and refresh the view.
25135      * @param {Store} store
25136      */
25137     setStore : function(store, initial){
25138         if(!initial && this.store){
25139             this.store.un("datachanged", this.refresh);
25140             this.store.un("add", this.onAdd);
25141             this.store.un("remove", this.onRemove);
25142             this.store.un("update", this.onUpdate);
25143             this.store.un("clear", this.refresh);
25144             this.store.un("beforeload", this.onBeforeLoad);
25145             this.store.un("load", this.onLoad);
25146             this.store.un("loadexception", this.onLoad);
25147         }
25148         if(store){
25149           
25150             store.on("datachanged", this.refresh, this);
25151             store.on("add", this.onAdd, this);
25152             store.on("remove", this.onRemove, this);
25153             store.on("update", this.onUpdate, this);
25154             store.on("clear", this.refresh, this);
25155             store.on("beforeload", this.onBeforeLoad, this);
25156             store.on("load", this.onLoad, this);
25157             store.on("loadexception", this.onLoad, this);
25158         }
25159         
25160         if(store){
25161             this.refresh();
25162         }
25163     },
25164     /**
25165      * onbeforeLoad - masks the loading area.
25166      *
25167      */
25168     onBeforeLoad : function(store,opts)
25169     {
25170          Roo.log('onBeforeLoad');   
25171         if (!opts.add) {
25172             this.el.update("");
25173         }
25174         this.el.mask(this.mask ? this.mask : "Loading" ); 
25175     },
25176     onLoad : function ()
25177     {
25178         this.el.unmask();
25179     },
25180     
25181
25182     /**
25183      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25184      * @param {HTMLElement} node
25185      * @return {HTMLElement} The template node
25186      */
25187     findItemFromChild : function(node){
25188         var el = this.dataName  ?
25189             this.el.child('.roo-tpl-' + this.dataName,true) :
25190             this.el.dom; 
25191         
25192         if(!node || node.parentNode == el){
25193                     return node;
25194             }
25195             var p = node.parentNode;
25196             while(p && p != el){
25197             if(p.parentNode == el){
25198                 return p;
25199             }
25200             p = p.parentNode;
25201         }
25202             return null;
25203     },
25204
25205     /** @ignore */
25206     onClick : function(e){
25207         var item = this.findItemFromChild(e.getTarget());
25208         if(item){
25209             var index = this.indexOf(item);
25210             if(this.onItemClick(item, index, e) !== false){
25211                 this.fireEvent("click", this, index, item, e);
25212             }
25213         }else{
25214             this.clearSelections();
25215         }
25216     },
25217
25218     /** @ignore */
25219     onContextMenu : function(e){
25220         var item = this.findItemFromChild(e.getTarget());
25221         if(item){
25222             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25223         }
25224     },
25225
25226     /** @ignore */
25227     onDblClick : function(e){
25228         var item = this.findItemFromChild(e.getTarget());
25229         if(item){
25230             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25231         }
25232     },
25233
25234     onItemClick : function(item, index, e)
25235     {
25236         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25237             return false;
25238         }
25239         if (this.toggleSelect) {
25240             var m = this.isSelected(item) ? 'unselect' : 'select';
25241             Roo.log(m);
25242             var _t = this;
25243             _t[m](item, true, false);
25244             return true;
25245         }
25246         if(this.multiSelect || this.singleSelect){
25247             if(this.multiSelect && e.shiftKey && this.lastSelection){
25248                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25249             }else{
25250                 this.select(item, this.multiSelect && e.ctrlKey);
25251                 this.lastSelection = item;
25252             }
25253             e.preventDefault();
25254         }
25255         return true;
25256     },
25257
25258     /**
25259      * Get the number of selected nodes.
25260      * @return {Number}
25261      */
25262     getSelectionCount : function(){
25263         return this.selections.length;
25264     },
25265
25266     /**
25267      * Get the currently selected nodes.
25268      * @return {Array} An array of HTMLElements
25269      */
25270     getSelectedNodes : function(){
25271         return this.selections;
25272     },
25273
25274     /**
25275      * Get the indexes of the selected nodes.
25276      * @return {Array}
25277      */
25278     getSelectedIndexes : function(){
25279         var indexes = [], s = this.selections;
25280         for(var i = 0, len = s.length; i < len; i++){
25281             indexes.push(s[i].nodeIndex);
25282         }
25283         return indexes;
25284     },
25285
25286     /**
25287      * Clear all selections
25288      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25289      */
25290     clearSelections : function(suppressEvent){
25291         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25292             this.cmp.elements = this.selections;
25293             this.cmp.removeClass(this.selectedClass);
25294             this.selections = [];
25295             if(!suppressEvent){
25296                 this.fireEvent("selectionchange", this, this.selections);
25297             }
25298         }
25299     },
25300
25301     /**
25302      * Returns true if the passed node is selected
25303      * @param {HTMLElement/Number} node The node or node index
25304      * @return {Boolean}
25305      */
25306     isSelected : function(node){
25307         var s = this.selections;
25308         if(s.length < 1){
25309             return false;
25310         }
25311         node = this.getNode(node);
25312         return s.indexOf(node) !== -1;
25313     },
25314
25315     /**
25316      * Selects nodes.
25317      * @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
25318      * @param {Boolean} keepExisting (optional) true to keep existing selections
25319      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25320      */
25321     select : function(nodeInfo, keepExisting, suppressEvent){
25322         if(nodeInfo instanceof Array){
25323             if(!keepExisting){
25324                 this.clearSelections(true);
25325             }
25326             for(var i = 0, len = nodeInfo.length; i < len; i++){
25327                 this.select(nodeInfo[i], true, true);
25328             }
25329             return;
25330         } 
25331         var node = this.getNode(nodeInfo);
25332         if(!node || this.isSelected(node)){
25333             return; // already selected.
25334         }
25335         if(!keepExisting){
25336             this.clearSelections(true);
25337         }
25338         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25339             Roo.fly(node).addClass(this.selectedClass);
25340             this.selections.push(node);
25341             if(!suppressEvent){
25342                 this.fireEvent("selectionchange", this, this.selections);
25343             }
25344         }
25345         
25346         
25347     },
25348       /**
25349      * Unselects nodes.
25350      * @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
25351      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25352      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25353      */
25354     unselect : function(nodeInfo, keepExisting, suppressEvent)
25355     {
25356         if(nodeInfo instanceof Array){
25357             Roo.each(this.selections, function(s) {
25358                 this.unselect(s, nodeInfo);
25359             }, this);
25360             return;
25361         }
25362         var node = this.getNode(nodeInfo);
25363         if(!node || !this.isSelected(node)){
25364             Roo.log("not selected");
25365             return; // not selected.
25366         }
25367         // fireevent???
25368         var ns = [];
25369         Roo.each(this.selections, function(s) {
25370             if (s == node ) {
25371                 Roo.fly(node).removeClass(this.selectedClass);
25372
25373                 return;
25374             }
25375             ns.push(s);
25376         },this);
25377         
25378         this.selections= ns;
25379         this.fireEvent("selectionchange", this, this.selections);
25380     },
25381
25382     /**
25383      * Gets a template node.
25384      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25385      * @return {HTMLElement} The node or null if it wasn't found
25386      */
25387     getNode : function(nodeInfo){
25388         if(typeof nodeInfo == "string"){
25389             return document.getElementById(nodeInfo);
25390         }else if(typeof nodeInfo == "number"){
25391             return this.nodes[nodeInfo];
25392         }
25393         return nodeInfo;
25394     },
25395
25396     /**
25397      * Gets a range template nodes.
25398      * @param {Number} startIndex
25399      * @param {Number} endIndex
25400      * @return {Array} An array of nodes
25401      */
25402     getNodes : function(start, end){
25403         var ns = this.nodes;
25404         start = start || 0;
25405         end = typeof end == "undefined" ? ns.length - 1 : end;
25406         var nodes = [];
25407         if(start <= end){
25408             for(var i = start; i <= end; i++){
25409                 nodes.push(ns[i]);
25410             }
25411         } else{
25412             for(var i = start; i >= end; i--){
25413                 nodes.push(ns[i]);
25414             }
25415         }
25416         return nodes;
25417     },
25418
25419     /**
25420      * Finds the index of the passed node
25421      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25422      * @return {Number} The index of the node or -1
25423      */
25424     indexOf : function(node){
25425         node = this.getNode(node);
25426         if(typeof node.nodeIndex == "number"){
25427             return node.nodeIndex;
25428         }
25429         var ns = this.nodes;
25430         for(var i = 0, len = ns.length; i < len; i++){
25431             if(ns[i] == node){
25432                 return i;
25433             }
25434         }
25435         return -1;
25436     }
25437 });
25438 /*
25439  * Based on:
25440  * Ext JS Library 1.1.1
25441  * Copyright(c) 2006-2007, Ext JS, LLC.
25442  *
25443  * Originally Released Under LGPL - original licence link has changed is not relivant.
25444  *
25445  * Fork - LGPL
25446  * <script type="text/javascript">
25447  */
25448
25449 /**
25450  * @class Roo.JsonView
25451  * @extends Roo.View
25452  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25453 <pre><code>
25454 var view = new Roo.JsonView({
25455     container: "my-element",
25456     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25457     multiSelect: true, 
25458     jsonRoot: "data" 
25459 });
25460
25461 // listen for node click?
25462 view.on("click", function(vw, index, node, e){
25463     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25464 });
25465
25466 // direct load of JSON data
25467 view.load("foobar.php");
25468
25469 // Example from my blog list
25470 var tpl = new Roo.Template(
25471     '&lt;div class="entry"&gt;' +
25472     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25473     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25474     "&lt;/div&gt;&lt;hr /&gt;"
25475 );
25476
25477 var moreView = new Roo.JsonView({
25478     container :  "entry-list", 
25479     template : tpl,
25480     jsonRoot: "posts"
25481 });
25482 moreView.on("beforerender", this.sortEntries, this);
25483 moreView.load({
25484     url: "/blog/get-posts.php",
25485     params: "allposts=true",
25486     text: "Loading Blog Entries..."
25487 });
25488 </code></pre>
25489
25490 * Note: old code is supported with arguments : (container, template, config)
25491
25492
25493  * @constructor
25494  * Create a new JsonView
25495  * 
25496  * @param {Object} config The config object
25497  * 
25498  */
25499 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25500     
25501     
25502     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25503
25504     var um = this.el.getUpdateManager();
25505     um.setRenderer(this);
25506     um.on("update", this.onLoad, this);
25507     um.on("failure", this.onLoadException, this);
25508
25509     /**
25510      * @event beforerender
25511      * Fires before rendering of the downloaded JSON data.
25512      * @param {Roo.JsonView} this
25513      * @param {Object} data The JSON data loaded
25514      */
25515     /**
25516      * @event load
25517      * Fires when data is loaded.
25518      * @param {Roo.JsonView} this
25519      * @param {Object} data The JSON data loaded
25520      * @param {Object} response The raw Connect response object
25521      */
25522     /**
25523      * @event loadexception
25524      * Fires when loading fails.
25525      * @param {Roo.JsonView} this
25526      * @param {Object} response The raw Connect response object
25527      */
25528     this.addEvents({
25529         'beforerender' : true,
25530         'load' : true,
25531         'loadexception' : true
25532     });
25533 };
25534 Roo.extend(Roo.JsonView, Roo.View, {
25535     /**
25536      * @type {String} The root property in the loaded JSON object that contains the data
25537      */
25538     jsonRoot : "",
25539
25540     /**
25541      * Refreshes the view.
25542      */
25543     refresh : function(){
25544         this.clearSelections();
25545         this.el.update("");
25546         var html = [];
25547         var o = this.jsonData;
25548         if(o && o.length > 0){
25549             for(var i = 0, len = o.length; i < len; i++){
25550                 var data = this.prepareData(o[i], i, o);
25551                 html[html.length] = this.tpl.apply(data);
25552             }
25553         }else{
25554             html.push(this.emptyText);
25555         }
25556         this.el.update(html.join(""));
25557         this.nodes = this.el.dom.childNodes;
25558         this.updateIndexes(0);
25559     },
25560
25561     /**
25562      * 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.
25563      * @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:
25564      <pre><code>
25565      view.load({
25566          url: "your-url.php",
25567          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25568          callback: yourFunction,
25569          scope: yourObject, //(optional scope)
25570          discardUrl: false,
25571          nocache: false,
25572          text: "Loading...",
25573          timeout: 30,
25574          scripts: false
25575      });
25576      </code></pre>
25577      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25578      * 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.
25579      * @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}
25580      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25581      * @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.
25582      */
25583     load : function(){
25584         var um = this.el.getUpdateManager();
25585         um.update.apply(um, arguments);
25586     },
25587
25588     render : function(el, response){
25589         this.clearSelections();
25590         this.el.update("");
25591         var o;
25592         try{
25593             o = Roo.util.JSON.decode(response.responseText);
25594             if(this.jsonRoot){
25595                 
25596                 o = o[this.jsonRoot];
25597             }
25598         } catch(e){
25599         }
25600         /**
25601          * The current JSON data or null
25602          */
25603         this.jsonData = o;
25604         this.beforeRender();
25605         this.refresh();
25606     },
25607
25608 /**
25609  * Get the number of records in the current JSON dataset
25610  * @return {Number}
25611  */
25612     getCount : function(){
25613         return this.jsonData ? this.jsonData.length : 0;
25614     },
25615
25616 /**
25617  * Returns the JSON object for the specified node(s)
25618  * @param {HTMLElement/Array} node The node or an array of nodes
25619  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25620  * you get the JSON object for the node
25621  */
25622     getNodeData : function(node){
25623         if(node instanceof Array){
25624             var data = [];
25625             for(var i = 0, len = node.length; i < len; i++){
25626                 data.push(this.getNodeData(node[i]));
25627             }
25628             return data;
25629         }
25630         return this.jsonData[this.indexOf(node)] || null;
25631     },
25632
25633     beforeRender : function(){
25634         this.snapshot = this.jsonData;
25635         if(this.sortInfo){
25636             this.sort.apply(this, this.sortInfo);
25637         }
25638         this.fireEvent("beforerender", this, this.jsonData);
25639     },
25640
25641     onLoad : function(el, o){
25642         this.fireEvent("load", this, this.jsonData, o);
25643     },
25644
25645     onLoadException : function(el, o){
25646         this.fireEvent("loadexception", this, o);
25647     },
25648
25649 /**
25650  * Filter the data by a specific property.
25651  * @param {String} property A property on your JSON objects
25652  * @param {String/RegExp} value Either string that the property values
25653  * should start with, or a RegExp to test against the property
25654  */
25655     filter : function(property, value){
25656         if(this.jsonData){
25657             var data = [];
25658             var ss = this.snapshot;
25659             if(typeof value == "string"){
25660                 var vlen = value.length;
25661                 if(vlen == 0){
25662                     this.clearFilter();
25663                     return;
25664                 }
25665                 value = value.toLowerCase();
25666                 for(var i = 0, len = ss.length; i < len; i++){
25667                     var o = ss[i];
25668                     if(o[property].substr(0, vlen).toLowerCase() == value){
25669                         data.push(o);
25670                     }
25671                 }
25672             } else if(value.exec){ // regex?
25673                 for(var i = 0, len = ss.length; i < len; i++){
25674                     var o = ss[i];
25675                     if(value.test(o[property])){
25676                         data.push(o);
25677                     }
25678                 }
25679             } else{
25680                 return;
25681             }
25682             this.jsonData = data;
25683             this.refresh();
25684         }
25685     },
25686
25687 /**
25688  * Filter by a function. The passed function will be called with each
25689  * object in the current dataset. If the function returns true the value is kept,
25690  * otherwise it is filtered.
25691  * @param {Function} fn
25692  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25693  */
25694     filterBy : function(fn, scope){
25695         if(this.jsonData){
25696             var data = [];
25697             var ss = this.snapshot;
25698             for(var i = 0, len = ss.length; i < len; i++){
25699                 var o = ss[i];
25700                 if(fn.call(scope || this, o)){
25701                     data.push(o);
25702                 }
25703             }
25704             this.jsonData = data;
25705             this.refresh();
25706         }
25707     },
25708
25709 /**
25710  * Clears the current filter.
25711  */
25712     clearFilter : function(){
25713         if(this.snapshot && this.jsonData != this.snapshot){
25714             this.jsonData = this.snapshot;
25715             this.refresh();
25716         }
25717     },
25718
25719
25720 /**
25721  * Sorts the data for this view and refreshes it.
25722  * @param {String} property A property on your JSON objects to sort on
25723  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25724  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25725  */
25726     sort : function(property, dir, sortType){
25727         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25728         if(this.jsonData){
25729             var p = property;
25730             var dsc = dir && dir.toLowerCase() == "desc";
25731             var f = function(o1, o2){
25732                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25733                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25734                 ;
25735                 if(v1 < v2){
25736                     return dsc ? +1 : -1;
25737                 } else if(v1 > v2){
25738                     return dsc ? -1 : +1;
25739                 } else{
25740                     return 0;
25741                 }
25742             };
25743             this.jsonData.sort(f);
25744             this.refresh();
25745             if(this.jsonData != this.snapshot){
25746                 this.snapshot.sort(f);
25747             }
25748         }
25749     }
25750 });/*
25751  * Based on:
25752  * Ext JS Library 1.1.1
25753  * Copyright(c) 2006-2007, Ext JS, LLC.
25754  *
25755  * Originally Released Under LGPL - original licence link has changed is not relivant.
25756  *
25757  * Fork - LGPL
25758  * <script type="text/javascript">
25759  */
25760  
25761
25762 /**
25763  * @class Roo.ColorPalette
25764  * @extends Roo.Component
25765  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25766  * Here's an example of typical usage:
25767  * <pre><code>
25768 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25769 cp.render('my-div');
25770
25771 cp.on('select', function(palette, selColor){
25772     // do something with selColor
25773 });
25774 </code></pre>
25775  * @constructor
25776  * Create a new ColorPalette
25777  * @param {Object} config The config object
25778  */
25779 Roo.ColorPalette = function(config){
25780     Roo.ColorPalette.superclass.constructor.call(this, config);
25781     this.addEvents({
25782         /**
25783              * @event select
25784              * Fires when a color is selected
25785              * @param {ColorPalette} this
25786              * @param {String} color The 6-digit color hex code (without the # symbol)
25787              */
25788         select: true
25789     });
25790
25791     if(this.handler){
25792         this.on("select", this.handler, this.scope, true);
25793     }
25794 };
25795 Roo.extend(Roo.ColorPalette, Roo.Component, {
25796     /**
25797      * @cfg {String} itemCls
25798      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25799      */
25800     itemCls : "x-color-palette",
25801     /**
25802      * @cfg {String} value
25803      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25804      * the hex codes are case-sensitive.
25805      */
25806     value : null,
25807     clickEvent:'click',
25808     // private
25809     ctype: "Roo.ColorPalette",
25810
25811     /**
25812      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25813      */
25814     allowReselect : false,
25815
25816     /**
25817      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25818      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25819      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25820      * of colors with the width setting until the box is symmetrical.</p>
25821      * <p>You can override individual colors if needed:</p>
25822      * <pre><code>
25823 var cp = new Roo.ColorPalette();
25824 cp.colors[0] = "FF0000";  // change the first box to red
25825 </code></pre>
25826
25827 Or you can provide a custom array of your own for complete control:
25828 <pre><code>
25829 var cp = new Roo.ColorPalette();
25830 cp.colors = ["000000", "993300", "333300"];
25831 </code></pre>
25832      * @type Array
25833      */
25834     colors : [
25835         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25836         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25837         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25838         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25839         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25840     ],
25841
25842     // private
25843     onRender : function(container, position){
25844         var t = new Roo.MasterTemplate(
25845             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25846         );
25847         var c = this.colors;
25848         for(var i = 0, len = c.length; i < len; i++){
25849             t.add([c[i]]);
25850         }
25851         var el = document.createElement("div");
25852         el.className = this.itemCls;
25853         t.overwrite(el);
25854         container.dom.insertBefore(el, position);
25855         this.el = Roo.get(el);
25856         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25857         if(this.clickEvent != 'click'){
25858             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25859         }
25860     },
25861
25862     // private
25863     afterRender : function(){
25864         Roo.ColorPalette.superclass.afterRender.call(this);
25865         if(this.value){
25866             var s = this.value;
25867             this.value = null;
25868             this.select(s);
25869         }
25870     },
25871
25872     // private
25873     handleClick : function(e, t){
25874         e.preventDefault();
25875         if(!this.disabled){
25876             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25877             this.select(c.toUpperCase());
25878         }
25879     },
25880
25881     /**
25882      * Selects the specified color in the palette (fires the select event)
25883      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25884      */
25885     select : function(color){
25886         color = color.replace("#", "");
25887         if(color != this.value || this.allowReselect){
25888             var el = this.el;
25889             if(this.value){
25890                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25891             }
25892             el.child("a.color-"+color).addClass("x-color-palette-sel");
25893             this.value = color;
25894             this.fireEvent("select", this, color);
25895         }
25896     }
25897 });/*
25898  * Based on:
25899  * Ext JS Library 1.1.1
25900  * Copyright(c) 2006-2007, Ext JS, LLC.
25901  *
25902  * Originally Released Under LGPL - original licence link has changed is not relivant.
25903  *
25904  * Fork - LGPL
25905  * <script type="text/javascript">
25906  */
25907  
25908 /**
25909  * @class Roo.DatePicker
25910  * @extends Roo.Component
25911  * Simple date picker class.
25912  * @constructor
25913  * Create a new DatePicker
25914  * @param {Object} config The config object
25915  */
25916 Roo.DatePicker = function(config){
25917     Roo.DatePicker.superclass.constructor.call(this, config);
25918
25919     this.value = config && config.value ?
25920                  config.value.clearTime() : new Date().clearTime();
25921
25922     this.addEvents({
25923         /**
25924              * @event select
25925              * Fires when a date is selected
25926              * @param {DatePicker} this
25927              * @param {Date} date The selected date
25928              */
25929         'select': true,
25930         /**
25931              * @event monthchange
25932              * Fires when the displayed month changes 
25933              * @param {DatePicker} this
25934              * @param {Date} date The selected month
25935              */
25936         'monthchange': true
25937     });
25938
25939     if(this.handler){
25940         this.on("select", this.handler,  this.scope || this);
25941     }
25942     // build the disabledDatesRE
25943     if(!this.disabledDatesRE && this.disabledDates){
25944         var dd = this.disabledDates;
25945         var re = "(?:";
25946         for(var i = 0; i < dd.length; i++){
25947             re += dd[i];
25948             if(i != dd.length-1) re += "|";
25949         }
25950         this.disabledDatesRE = new RegExp(re + ")");
25951     }
25952 };
25953
25954 Roo.extend(Roo.DatePicker, Roo.Component, {
25955     /**
25956      * @cfg {String} todayText
25957      * The text to display on the button that selects the current date (defaults to "Today")
25958      */
25959     todayText : "Today",
25960     /**
25961      * @cfg {String} okText
25962      * The text to display on the ok button
25963      */
25964     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25965     /**
25966      * @cfg {String} cancelText
25967      * The text to display on the cancel button
25968      */
25969     cancelText : "Cancel",
25970     /**
25971      * @cfg {String} todayTip
25972      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25973      */
25974     todayTip : "{0} (Spacebar)",
25975     /**
25976      * @cfg {Date} minDate
25977      * Minimum allowable date (JavaScript date object, defaults to null)
25978      */
25979     minDate : null,
25980     /**
25981      * @cfg {Date} maxDate
25982      * Maximum allowable date (JavaScript date object, defaults to null)
25983      */
25984     maxDate : null,
25985     /**
25986      * @cfg {String} minText
25987      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25988      */
25989     minText : "This date is before the minimum date",
25990     /**
25991      * @cfg {String} maxText
25992      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25993      */
25994     maxText : "This date is after the maximum date",
25995     /**
25996      * @cfg {String} format
25997      * The default date format string which can be overriden for localization support.  The format must be
25998      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25999      */
26000     format : "m/d/y",
26001     /**
26002      * @cfg {Array} disabledDays
26003      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26004      */
26005     disabledDays : null,
26006     /**
26007      * @cfg {String} disabledDaysText
26008      * The tooltip to display when the date falls on a disabled day (defaults to "")
26009      */
26010     disabledDaysText : "",
26011     /**
26012      * @cfg {RegExp} disabledDatesRE
26013      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26014      */
26015     disabledDatesRE : null,
26016     /**
26017      * @cfg {String} disabledDatesText
26018      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26019      */
26020     disabledDatesText : "",
26021     /**
26022      * @cfg {Boolean} constrainToViewport
26023      * True to constrain the date picker to the viewport (defaults to true)
26024      */
26025     constrainToViewport : true,
26026     /**
26027      * @cfg {Array} monthNames
26028      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26029      */
26030     monthNames : Date.monthNames,
26031     /**
26032      * @cfg {Array} dayNames
26033      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26034      */
26035     dayNames : Date.dayNames,
26036     /**
26037      * @cfg {String} nextText
26038      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26039      */
26040     nextText: 'Next Month (Control+Right)',
26041     /**
26042      * @cfg {String} prevText
26043      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26044      */
26045     prevText: 'Previous Month (Control+Left)',
26046     /**
26047      * @cfg {String} monthYearText
26048      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26049      */
26050     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26051     /**
26052      * @cfg {Number} startDay
26053      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26054      */
26055     startDay : 0,
26056     /**
26057      * @cfg {Bool} showClear
26058      * Show a clear button (usefull for date form elements that can be blank.)
26059      */
26060     
26061     showClear: false,
26062     
26063     /**
26064      * Sets the value of the date field
26065      * @param {Date} value The date to set
26066      */
26067     setValue : function(value){
26068         var old = this.value;
26069         
26070         if (typeof(value) == 'string') {
26071          
26072             value = Date.parseDate(value, this.format);
26073         }
26074         if (!value) {
26075             value = new Date();
26076         }
26077         
26078         this.value = value.clearTime(true);
26079         if(this.el){
26080             this.update(this.value);
26081         }
26082     },
26083
26084     /**
26085      * Gets the current selected value of the date field
26086      * @return {Date} The selected date
26087      */
26088     getValue : function(){
26089         return this.value;
26090     },
26091
26092     // private
26093     focus : function(){
26094         if(this.el){
26095             this.update(this.activeDate);
26096         }
26097     },
26098
26099     // privateval
26100     onRender : function(container, position){
26101         
26102         var m = [
26103              '<table cellspacing="0">',
26104                 '<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>',
26105                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26106         var dn = this.dayNames;
26107         for(var i = 0; i < 7; i++){
26108             var d = this.startDay+i;
26109             if(d > 6){
26110                 d = d-7;
26111             }
26112             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26113         }
26114         m[m.length] = "</tr></thead><tbody><tr>";
26115         for(var i = 0; i < 42; i++) {
26116             if(i % 7 == 0 && i != 0){
26117                 m[m.length] = "</tr><tr>";
26118             }
26119             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26120         }
26121         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26122             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26123
26124         var el = document.createElement("div");
26125         el.className = "x-date-picker";
26126         el.innerHTML = m.join("");
26127
26128         container.dom.insertBefore(el, position);
26129
26130         this.el = Roo.get(el);
26131         this.eventEl = Roo.get(el.firstChild);
26132
26133         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26134             handler: this.showPrevMonth,
26135             scope: this,
26136             preventDefault:true,
26137             stopDefault:true
26138         });
26139
26140         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26141             handler: this.showNextMonth,
26142             scope: this,
26143             preventDefault:true,
26144             stopDefault:true
26145         });
26146
26147         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26148
26149         this.monthPicker = this.el.down('div.x-date-mp');
26150         this.monthPicker.enableDisplayMode('block');
26151         
26152         var kn = new Roo.KeyNav(this.eventEl, {
26153             "left" : function(e){
26154                 e.ctrlKey ?
26155                     this.showPrevMonth() :
26156                     this.update(this.activeDate.add("d", -1));
26157             },
26158
26159             "right" : function(e){
26160                 e.ctrlKey ?
26161                     this.showNextMonth() :
26162                     this.update(this.activeDate.add("d", 1));
26163             },
26164
26165             "up" : function(e){
26166                 e.ctrlKey ?
26167                     this.showNextYear() :
26168                     this.update(this.activeDate.add("d", -7));
26169             },
26170
26171             "down" : function(e){
26172                 e.ctrlKey ?
26173                     this.showPrevYear() :
26174                     this.update(this.activeDate.add("d", 7));
26175             },
26176
26177             "pageUp" : function(e){
26178                 this.showNextMonth();
26179             },
26180
26181             "pageDown" : function(e){
26182                 this.showPrevMonth();
26183             },
26184
26185             "enter" : function(e){
26186                 e.stopPropagation();
26187                 return true;
26188             },
26189
26190             scope : this
26191         });
26192
26193         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26194
26195         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26196
26197         this.el.unselectable();
26198         
26199         this.cells = this.el.select("table.x-date-inner tbody td");
26200         this.textNodes = this.el.query("table.x-date-inner tbody span");
26201
26202         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26203             text: "&#160;",
26204             tooltip: this.monthYearText
26205         });
26206
26207         this.mbtn.on('click', this.showMonthPicker, this);
26208         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26209
26210
26211         var today = (new Date()).dateFormat(this.format);
26212         
26213         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26214         if (this.showClear) {
26215             baseTb.add( new Roo.Toolbar.Fill());
26216         }
26217         baseTb.add({
26218             text: String.format(this.todayText, today),
26219             tooltip: String.format(this.todayTip, today),
26220             handler: this.selectToday,
26221             scope: this
26222         });
26223         
26224         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26225             
26226         //});
26227         if (this.showClear) {
26228             
26229             baseTb.add( new Roo.Toolbar.Fill());
26230             baseTb.add({
26231                 text: '&#160;',
26232                 cls: 'x-btn-icon x-btn-clear',
26233                 handler: function() {
26234                     //this.value = '';
26235                     this.fireEvent("select", this, '');
26236                 },
26237                 scope: this
26238             });
26239         }
26240         
26241         
26242         if(Roo.isIE){
26243             this.el.repaint();
26244         }
26245         this.update(this.value);
26246     },
26247
26248     createMonthPicker : function(){
26249         if(!this.monthPicker.dom.firstChild){
26250             var buf = ['<table border="0" cellspacing="0">'];
26251             for(var i = 0; i < 6; i++){
26252                 buf.push(
26253                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26254                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26255                     i == 0 ?
26256                     '<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>' :
26257                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26258                 );
26259             }
26260             buf.push(
26261                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26262                     this.okText,
26263                     '</button><button type="button" class="x-date-mp-cancel">',
26264                     this.cancelText,
26265                     '</button></td></tr>',
26266                 '</table>'
26267             );
26268             this.monthPicker.update(buf.join(''));
26269             this.monthPicker.on('click', this.onMonthClick, this);
26270             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26271
26272             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26273             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26274
26275             this.mpMonths.each(function(m, a, i){
26276                 i += 1;
26277                 if((i%2) == 0){
26278                     m.dom.xmonth = 5 + Math.round(i * .5);
26279                 }else{
26280                     m.dom.xmonth = Math.round((i-1) * .5);
26281                 }
26282             });
26283         }
26284     },
26285
26286     showMonthPicker : function(){
26287         this.createMonthPicker();
26288         var size = this.el.getSize();
26289         this.monthPicker.setSize(size);
26290         this.monthPicker.child('table').setSize(size);
26291
26292         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26293         this.updateMPMonth(this.mpSelMonth);
26294         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26295         this.updateMPYear(this.mpSelYear);
26296
26297         this.monthPicker.slideIn('t', {duration:.2});
26298     },
26299
26300     updateMPYear : function(y){
26301         this.mpyear = y;
26302         var ys = this.mpYears.elements;
26303         for(var i = 1; i <= 10; i++){
26304             var td = ys[i-1], y2;
26305             if((i%2) == 0){
26306                 y2 = y + Math.round(i * .5);
26307                 td.firstChild.innerHTML = y2;
26308                 td.xyear = y2;
26309             }else{
26310                 y2 = y - (5-Math.round(i * .5));
26311                 td.firstChild.innerHTML = y2;
26312                 td.xyear = y2;
26313             }
26314             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26315         }
26316     },
26317
26318     updateMPMonth : function(sm){
26319         this.mpMonths.each(function(m, a, i){
26320             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26321         });
26322     },
26323
26324     selectMPMonth: function(m){
26325         
26326     },
26327
26328     onMonthClick : function(e, t){
26329         e.stopEvent();
26330         var el = new Roo.Element(t), pn;
26331         if(el.is('button.x-date-mp-cancel')){
26332             this.hideMonthPicker();
26333         }
26334         else if(el.is('button.x-date-mp-ok')){
26335             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26336             this.hideMonthPicker();
26337         }
26338         else if(pn = el.up('td.x-date-mp-month', 2)){
26339             this.mpMonths.removeClass('x-date-mp-sel');
26340             pn.addClass('x-date-mp-sel');
26341             this.mpSelMonth = pn.dom.xmonth;
26342         }
26343         else if(pn = el.up('td.x-date-mp-year', 2)){
26344             this.mpYears.removeClass('x-date-mp-sel');
26345             pn.addClass('x-date-mp-sel');
26346             this.mpSelYear = pn.dom.xyear;
26347         }
26348         else if(el.is('a.x-date-mp-prev')){
26349             this.updateMPYear(this.mpyear-10);
26350         }
26351         else if(el.is('a.x-date-mp-next')){
26352             this.updateMPYear(this.mpyear+10);
26353         }
26354     },
26355
26356     onMonthDblClick : function(e, t){
26357         e.stopEvent();
26358         var el = new Roo.Element(t), pn;
26359         if(pn = el.up('td.x-date-mp-month', 2)){
26360             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26361             this.hideMonthPicker();
26362         }
26363         else if(pn = el.up('td.x-date-mp-year', 2)){
26364             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26365             this.hideMonthPicker();
26366         }
26367     },
26368
26369     hideMonthPicker : function(disableAnim){
26370         if(this.monthPicker){
26371             if(disableAnim === true){
26372                 this.monthPicker.hide();
26373             }else{
26374                 this.monthPicker.slideOut('t', {duration:.2});
26375             }
26376         }
26377     },
26378
26379     // private
26380     showPrevMonth : function(e){
26381         this.update(this.activeDate.add("mo", -1));
26382     },
26383
26384     // private
26385     showNextMonth : function(e){
26386         this.update(this.activeDate.add("mo", 1));
26387     },
26388
26389     // private
26390     showPrevYear : function(){
26391         this.update(this.activeDate.add("y", -1));
26392     },
26393
26394     // private
26395     showNextYear : function(){
26396         this.update(this.activeDate.add("y", 1));
26397     },
26398
26399     // private
26400     handleMouseWheel : function(e){
26401         var delta = e.getWheelDelta();
26402         if(delta > 0){
26403             this.showPrevMonth();
26404             e.stopEvent();
26405         } else if(delta < 0){
26406             this.showNextMonth();
26407             e.stopEvent();
26408         }
26409     },
26410
26411     // private
26412     handleDateClick : function(e, t){
26413         e.stopEvent();
26414         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26415             this.setValue(new Date(t.dateValue));
26416             this.fireEvent("select", this, this.value);
26417         }
26418     },
26419
26420     // private
26421     selectToday : function(){
26422         this.setValue(new Date().clearTime());
26423         this.fireEvent("select", this, this.value);
26424     },
26425
26426     // private
26427     update : function(date)
26428     {
26429         var vd = this.activeDate;
26430         this.activeDate = date;
26431         if(vd && this.el){
26432             var t = date.getTime();
26433             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26434                 this.cells.removeClass("x-date-selected");
26435                 this.cells.each(function(c){
26436                    if(c.dom.firstChild.dateValue == t){
26437                        c.addClass("x-date-selected");
26438                        setTimeout(function(){
26439                             try{c.dom.firstChild.focus();}catch(e){}
26440                        }, 50);
26441                        return false;
26442                    }
26443                 });
26444                 return;
26445             }
26446         }
26447         
26448         var days = date.getDaysInMonth();
26449         var firstOfMonth = date.getFirstDateOfMonth();
26450         var startingPos = firstOfMonth.getDay()-this.startDay;
26451
26452         if(startingPos <= this.startDay){
26453             startingPos += 7;
26454         }
26455
26456         var pm = date.add("mo", -1);
26457         var prevStart = pm.getDaysInMonth()-startingPos;
26458
26459         var cells = this.cells.elements;
26460         var textEls = this.textNodes;
26461         days += startingPos;
26462
26463         // convert everything to numbers so it's fast
26464         var day = 86400000;
26465         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26466         var today = new Date().clearTime().getTime();
26467         var sel = date.clearTime().getTime();
26468         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26469         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26470         var ddMatch = this.disabledDatesRE;
26471         var ddText = this.disabledDatesText;
26472         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26473         var ddaysText = this.disabledDaysText;
26474         var format = this.format;
26475
26476         var setCellClass = function(cal, cell){
26477             cell.title = "";
26478             var t = d.getTime();
26479             cell.firstChild.dateValue = t;
26480             if(t == today){
26481                 cell.className += " x-date-today";
26482                 cell.title = cal.todayText;
26483             }
26484             if(t == sel){
26485                 cell.className += " x-date-selected";
26486                 setTimeout(function(){
26487                     try{cell.firstChild.focus();}catch(e){}
26488                 }, 50);
26489             }
26490             // disabling
26491             if(t < min) {
26492                 cell.className = " x-date-disabled";
26493                 cell.title = cal.minText;
26494                 return;
26495             }
26496             if(t > max) {
26497                 cell.className = " x-date-disabled";
26498                 cell.title = cal.maxText;
26499                 return;
26500             }
26501             if(ddays){
26502                 if(ddays.indexOf(d.getDay()) != -1){
26503                     cell.title = ddaysText;
26504                     cell.className = " x-date-disabled";
26505                 }
26506             }
26507             if(ddMatch && format){
26508                 var fvalue = d.dateFormat(format);
26509                 if(ddMatch.test(fvalue)){
26510                     cell.title = ddText.replace("%0", fvalue);
26511                     cell.className = " x-date-disabled";
26512                 }
26513             }
26514         };
26515
26516         var i = 0;
26517         for(; i < startingPos; i++) {
26518             textEls[i].innerHTML = (++prevStart);
26519             d.setDate(d.getDate()+1);
26520             cells[i].className = "x-date-prevday";
26521             setCellClass(this, cells[i]);
26522         }
26523         for(; i < days; i++){
26524             intDay = i - startingPos + 1;
26525             textEls[i].innerHTML = (intDay);
26526             d.setDate(d.getDate()+1);
26527             cells[i].className = "x-date-active";
26528             setCellClass(this, cells[i]);
26529         }
26530         var extraDays = 0;
26531         for(; i < 42; i++) {
26532              textEls[i].innerHTML = (++extraDays);
26533              d.setDate(d.getDate()+1);
26534              cells[i].className = "x-date-nextday";
26535              setCellClass(this, cells[i]);
26536         }
26537
26538         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26539         this.fireEvent('monthchange', this, date);
26540         
26541         if(!this.internalRender){
26542             var main = this.el.dom.firstChild;
26543             var w = main.offsetWidth;
26544             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26545             Roo.fly(main).setWidth(w);
26546             this.internalRender = true;
26547             // opera does not respect the auto grow header center column
26548             // then, after it gets a width opera refuses to recalculate
26549             // without a second pass
26550             if(Roo.isOpera && !this.secondPass){
26551                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26552                 this.secondPass = true;
26553                 this.update.defer(10, this, [date]);
26554             }
26555         }
26556         
26557         
26558     }
26559 });        /*
26560  * Based on:
26561  * Ext JS Library 1.1.1
26562  * Copyright(c) 2006-2007, Ext JS, LLC.
26563  *
26564  * Originally Released Under LGPL - original licence link has changed is not relivant.
26565  *
26566  * Fork - LGPL
26567  * <script type="text/javascript">
26568  */
26569 /**
26570  * @class Roo.TabPanel
26571  * @extends Roo.util.Observable
26572  * A lightweight tab container.
26573  * <br><br>
26574  * Usage:
26575  * <pre><code>
26576 // basic tabs 1, built from existing content
26577 var tabs = new Roo.TabPanel("tabs1");
26578 tabs.addTab("script", "View Script");
26579 tabs.addTab("markup", "View Markup");
26580 tabs.activate("script");
26581
26582 // more advanced tabs, built from javascript
26583 var jtabs = new Roo.TabPanel("jtabs");
26584 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26585
26586 // set up the UpdateManager
26587 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26588 var updater = tab2.getUpdateManager();
26589 updater.setDefaultUrl("ajax1.htm");
26590 tab2.on('activate', updater.refresh, updater, true);
26591
26592 // Use setUrl for Ajax loading
26593 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26594 tab3.setUrl("ajax2.htm", null, true);
26595
26596 // Disabled tab
26597 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26598 tab4.disable();
26599
26600 jtabs.activate("jtabs-1");
26601  * </code></pre>
26602  * @constructor
26603  * Create a new TabPanel.
26604  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26605  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26606  */
26607 Roo.TabPanel = function(container, config){
26608     /**
26609     * The container element for this TabPanel.
26610     * @type Roo.Element
26611     */
26612     this.el = Roo.get(container, true);
26613     if(config){
26614         if(typeof config == "boolean"){
26615             this.tabPosition = config ? "bottom" : "top";
26616         }else{
26617             Roo.apply(this, config);
26618         }
26619     }
26620     if(this.tabPosition == "bottom"){
26621         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26622         this.el.addClass("x-tabs-bottom");
26623     }
26624     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26625     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26626     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26627     if(Roo.isIE){
26628         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26629     }
26630     if(this.tabPosition != "bottom"){
26631         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26632          * @type Roo.Element
26633          */
26634         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26635         this.el.addClass("x-tabs-top");
26636     }
26637     this.items = [];
26638
26639     this.bodyEl.setStyle("position", "relative");
26640
26641     this.active = null;
26642     this.activateDelegate = this.activate.createDelegate(this);
26643
26644     this.addEvents({
26645         /**
26646          * @event tabchange
26647          * Fires when the active tab changes
26648          * @param {Roo.TabPanel} this
26649          * @param {Roo.TabPanelItem} activePanel The new active tab
26650          */
26651         "tabchange": true,
26652         /**
26653          * @event beforetabchange
26654          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26655          * @param {Roo.TabPanel} this
26656          * @param {Object} e Set cancel to true on this object to cancel the tab change
26657          * @param {Roo.TabPanelItem} tab The tab being changed to
26658          */
26659         "beforetabchange" : true
26660     });
26661
26662     Roo.EventManager.onWindowResize(this.onResize, this);
26663     this.cpad = this.el.getPadding("lr");
26664     this.hiddenCount = 0;
26665
26666
26667     // toolbar on the tabbar support...
26668     if (this.toolbar) {
26669         var tcfg = this.toolbar;
26670         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26671         this.toolbar = new Roo.Toolbar(tcfg);
26672         if (Roo.isSafari) {
26673             var tbl = tcfg.container.child('table', true);
26674             tbl.setAttribute('width', '100%');
26675         }
26676         
26677     }
26678    
26679
26680
26681     Roo.TabPanel.superclass.constructor.call(this);
26682 };
26683
26684 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26685     /*
26686      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26687      */
26688     tabPosition : "top",
26689     /*
26690      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26691      */
26692     currentTabWidth : 0,
26693     /*
26694      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26695      */
26696     minTabWidth : 40,
26697     /*
26698      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26699      */
26700     maxTabWidth : 250,
26701     /*
26702      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26703      */
26704     preferredTabWidth : 175,
26705     /*
26706      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26707      */
26708     resizeTabs : false,
26709     /*
26710      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26711      */
26712     monitorResize : true,
26713     /*
26714      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26715      */
26716     toolbar : false,
26717
26718     /**
26719      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26720      * @param {String} id The id of the div to use <b>or create</b>
26721      * @param {String} text The text for the tab
26722      * @param {String} content (optional) Content to put in the TabPanelItem body
26723      * @param {Boolean} closable (optional) True to create a close icon on the tab
26724      * @return {Roo.TabPanelItem} The created TabPanelItem
26725      */
26726     addTab : function(id, text, content, closable){
26727         var item = new Roo.TabPanelItem(this, id, text, closable);
26728         this.addTabItem(item);
26729         if(content){
26730             item.setContent(content);
26731         }
26732         return item;
26733     },
26734
26735     /**
26736      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26737      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26738      * @return {Roo.TabPanelItem}
26739      */
26740     getTab : function(id){
26741         return this.items[id];
26742     },
26743
26744     /**
26745      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26746      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26747      */
26748     hideTab : function(id){
26749         var t = this.items[id];
26750         if(!t.isHidden()){
26751            t.setHidden(true);
26752            this.hiddenCount++;
26753            this.autoSizeTabs();
26754         }
26755     },
26756
26757     /**
26758      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26759      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26760      */
26761     unhideTab : function(id){
26762         var t = this.items[id];
26763         if(t.isHidden()){
26764            t.setHidden(false);
26765            this.hiddenCount--;
26766            this.autoSizeTabs();
26767         }
26768     },
26769
26770     /**
26771      * Adds an existing {@link Roo.TabPanelItem}.
26772      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26773      */
26774     addTabItem : function(item){
26775         this.items[item.id] = item;
26776         this.items.push(item);
26777         if(this.resizeTabs){
26778            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26779            this.autoSizeTabs();
26780         }else{
26781             item.autoSize();
26782         }
26783     },
26784
26785     /**
26786      * Removes a {@link Roo.TabPanelItem}.
26787      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26788      */
26789     removeTab : function(id){
26790         var items = this.items;
26791         var tab = items[id];
26792         if(!tab) { return; }
26793         var index = items.indexOf(tab);
26794         if(this.active == tab && items.length > 1){
26795             var newTab = this.getNextAvailable(index);
26796             if(newTab) {
26797                 newTab.activate();
26798             }
26799         }
26800         this.stripEl.dom.removeChild(tab.pnode.dom);
26801         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26802             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26803         }
26804         items.splice(index, 1);
26805         delete this.items[tab.id];
26806         tab.fireEvent("close", tab);
26807         tab.purgeListeners();
26808         this.autoSizeTabs();
26809     },
26810
26811     getNextAvailable : function(start){
26812         var items = this.items;
26813         var index = start;
26814         // look for a next tab that will slide over to
26815         // replace the one being removed
26816         while(index < items.length){
26817             var item = items[++index];
26818             if(item && !item.isHidden()){
26819                 return item;
26820             }
26821         }
26822         // if one isn't found select the previous tab (on the left)
26823         index = start;
26824         while(index >= 0){
26825             var item = items[--index];
26826             if(item && !item.isHidden()){
26827                 return item;
26828             }
26829         }
26830         return null;
26831     },
26832
26833     /**
26834      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26835      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26836      */
26837     disableTab : function(id){
26838         var tab = this.items[id];
26839         if(tab && this.active != tab){
26840             tab.disable();
26841         }
26842     },
26843
26844     /**
26845      * Enables a {@link Roo.TabPanelItem} that is disabled.
26846      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26847      */
26848     enableTab : function(id){
26849         var tab = this.items[id];
26850         tab.enable();
26851     },
26852
26853     /**
26854      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26855      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26856      * @return {Roo.TabPanelItem} The TabPanelItem.
26857      */
26858     activate : function(id){
26859         var tab = this.items[id];
26860         if(!tab){
26861             return null;
26862         }
26863         if(tab == this.active || tab.disabled){
26864             return tab;
26865         }
26866         var e = {};
26867         this.fireEvent("beforetabchange", this, e, tab);
26868         if(e.cancel !== true && !tab.disabled){
26869             if(this.active){
26870                 this.active.hide();
26871             }
26872             this.active = this.items[id];
26873             this.active.show();
26874             this.fireEvent("tabchange", this, this.active);
26875         }
26876         return tab;
26877     },
26878
26879     /**
26880      * Gets the active {@link Roo.TabPanelItem}.
26881      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26882      */
26883     getActiveTab : function(){
26884         return this.active;
26885     },
26886
26887     /**
26888      * Updates the tab body element to fit the height of the container element
26889      * for overflow scrolling
26890      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26891      */
26892     syncHeight : function(targetHeight){
26893         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26894         var bm = this.bodyEl.getMargins();
26895         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26896         this.bodyEl.setHeight(newHeight);
26897         return newHeight;
26898     },
26899
26900     onResize : function(){
26901         if(this.monitorResize){
26902             this.autoSizeTabs();
26903         }
26904     },
26905
26906     /**
26907      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26908      */
26909     beginUpdate : function(){
26910         this.updating = true;
26911     },
26912
26913     /**
26914      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26915      */
26916     endUpdate : function(){
26917         this.updating = false;
26918         this.autoSizeTabs();
26919     },
26920
26921     /**
26922      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26923      */
26924     autoSizeTabs : function(){
26925         var count = this.items.length;
26926         var vcount = count - this.hiddenCount;
26927         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26928         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26929         var availWidth = Math.floor(w / vcount);
26930         var b = this.stripBody;
26931         if(b.getWidth() > w){
26932             var tabs = this.items;
26933             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26934             if(availWidth < this.minTabWidth){
26935                 /*if(!this.sleft){    // incomplete scrolling code
26936                     this.createScrollButtons();
26937                 }
26938                 this.showScroll();
26939                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26940             }
26941         }else{
26942             if(this.currentTabWidth < this.preferredTabWidth){
26943                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26944             }
26945         }
26946     },
26947
26948     /**
26949      * Returns the number of tabs in this TabPanel.
26950      * @return {Number}
26951      */
26952      getCount : function(){
26953          return this.items.length;
26954      },
26955
26956     /**
26957      * Resizes all the tabs to the passed width
26958      * @param {Number} The new width
26959      */
26960     setTabWidth : function(width){
26961         this.currentTabWidth = width;
26962         for(var i = 0, len = this.items.length; i < len; i++) {
26963                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26964         }
26965     },
26966
26967     /**
26968      * Destroys this TabPanel
26969      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26970      */
26971     destroy : function(removeEl){
26972         Roo.EventManager.removeResizeListener(this.onResize, this);
26973         for(var i = 0, len = this.items.length; i < len; i++){
26974             this.items[i].purgeListeners();
26975         }
26976         if(removeEl === true){
26977             this.el.update("");
26978             this.el.remove();
26979         }
26980     }
26981 });
26982
26983 /**
26984  * @class Roo.TabPanelItem
26985  * @extends Roo.util.Observable
26986  * Represents an individual item (tab plus body) in a TabPanel.
26987  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26988  * @param {String} id The id of this TabPanelItem
26989  * @param {String} text The text for the tab of this TabPanelItem
26990  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26991  */
26992 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26993     /**
26994      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26995      * @type Roo.TabPanel
26996      */
26997     this.tabPanel = tabPanel;
26998     /**
26999      * The id for this TabPanelItem
27000      * @type String
27001      */
27002     this.id = id;
27003     /** @private */
27004     this.disabled = false;
27005     /** @private */
27006     this.text = text;
27007     /** @private */
27008     this.loaded = false;
27009     this.closable = closable;
27010
27011     /**
27012      * The body element for this TabPanelItem.
27013      * @type Roo.Element
27014      */
27015     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27016     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27017     this.bodyEl.setStyle("display", "block");
27018     this.bodyEl.setStyle("zoom", "1");
27019     this.hideAction();
27020
27021     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27022     /** @private */
27023     this.el = Roo.get(els.el, true);
27024     this.inner = Roo.get(els.inner, true);
27025     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27026     this.pnode = Roo.get(els.el.parentNode, true);
27027     this.el.on("mousedown", this.onTabMouseDown, this);
27028     this.el.on("click", this.onTabClick, this);
27029     /** @private */
27030     if(closable){
27031         var c = Roo.get(els.close, true);
27032         c.dom.title = this.closeText;
27033         c.addClassOnOver("close-over");
27034         c.on("click", this.closeClick, this);
27035      }
27036
27037     this.addEvents({
27038          /**
27039          * @event activate
27040          * Fires when this tab becomes the active tab.
27041          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27042          * @param {Roo.TabPanelItem} this
27043          */
27044         "activate": true,
27045         /**
27046          * @event beforeclose
27047          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27048          * @param {Roo.TabPanelItem} this
27049          * @param {Object} e Set cancel to true on this object to cancel the close.
27050          */
27051         "beforeclose": true,
27052         /**
27053          * @event close
27054          * Fires when this tab is closed.
27055          * @param {Roo.TabPanelItem} this
27056          */
27057          "close": true,
27058         /**
27059          * @event deactivate
27060          * Fires when this tab is no longer the active tab.
27061          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27062          * @param {Roo.TabPanelItem} this
27063          */
27064          "deactivate" : true
27065     });
27066     this.hidden = false;
27067
27068     Roo.TabPanelItem.superclass.constructor.call(this);
27069 };
27070
27071 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27072     purgeListeners : function(){
27073        Roo.util.Observable.prototype.purgeListeners.call(this);
27074        this.el.removeAllListeners();
27075     },
27076     /**
27077      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27078      */
27079     show : function(){
27080         this.pnode.addClass("on");
27081         this.showAction();
27082         if(Roo.isOpera){
27083             this.tabPanel.stripWrap.repaint();
27084         }
27085         this.fireEvent("activate", this.tabPanel, this);
27086     },
27087
27088     /**
27089      * Returns true if this tab is the active tab.
27090      * @return {Boolean}
27091      */
27092     isActive : function(){
27093         return this.tabPanel.getActiveTab() == this;
27094     },
27095
27096     /**
27097      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27098      */
27099     hide : function(){
27100         this.pnode.removeClass("on");
27101         this.hideAction();
27102         this.fireEvent("deactivate", this.tabPanel, this);
27103     },
27104
27105     hideAction : function(){
27106         this.bodyEl.hide();
27107         this.bodyEl.setStyle("position", "absolute");
27108         this.bodyEl.setLeft("-20000px");
27109         this.bodyEl.setTop("-20000px");
27110     },
27111
27112     showAction : function(){
27113         this.bodyEl.setStyle("position", "relative");
27114         this.bodyEl.setTop("");
27115         this.bodyEl.setLeft("");
27116         this.bodyEl.show();
27117     },
27118
27119     /**
27120      * Set the tooltip for the tab.
27121      * @param {String} tooltip The tab's tooltip
27122      */
27123     setTooltip : function(text){
27124         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27125             this.textEl.dom.qtip = text;
27126             this.textEl.dom.removeAttribute('title');
27127         }else{
27128             this.textEl.dom.title = text;
27129         }
27130     },
27131
27132     onTabClick : function(e){
27133         e.preventDefault();
27134         this.tabPanel.activate(this.id);
27135     },
27136
27137     onTabMouseDown : function(e){
27138         e.preventDefault();
27139         this.tabPanel.activate(this.id);
27140     },
27141
27142     getWidth : function(){
27143         return this.inner.getWidth();
27144     },
27145
27146     setWidth : function(width){
27147         var iwidth = width - this.pnode.getPadding("lr");
27148         this.inner.setWidth(iwidth);
27149         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27150         this.pnode.setWidth(width);
27151     },
27152
27153     /**
27154      * Show or hide the tab
27155      * @param {Boolean} hidden True to hide or false to show.
27156      */
27157     setHidden : function(hidden){
27158         this.hidden = hidden;
27159         this.pnode.setStyle("display", hidden ? "none" : "");
27160     },
27161
27162     /**
27163      * Returns true if this tab is "hidden"
27164      * @return {Boolean}
27165      */
27166     isHidden : function(){
27167         return this.hidden;
27168     },
27169
27170     /**
27171      * Returns the text for this tab
27172      * @return {String}
27173      */
27174     getText : function(){
27175         return this.text;
27176     },
27177
27178     autoSize : function(){
27179         //this.el.beginMeasure();
27180         this.textEl.setWidth(1);
27181         /*
27182          *  #2804 [new] Tabs in Roojs
27183          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27184          */
27185         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27186         //this.el.endMeasure();
27187     },
27188
27189     /**
27190      * Sets the text for the tab (Note: this also sets the tooltip text)
27191      * @param {String} text The tab's text and tooltip
27192      */
27193     setText : function(text){
27194         this.text = text;
27195         this.textEl.update(text);
27196         this.setTooltip(text);
27197         if(!this.tabPanel.resizeTabs){
27198             this.autoSize();
27199         }
27200     },
27201     /**
27202      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27203      */
27204     activate : function(){
27205         this.tabPanel.activate(this.id);
27206     },
27207
27208     /**
27209      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27210      */
27211     disable : function(){
27212         if(this.tabPanel.active != this){
27213             this.disabled = true;
27214             this.pnode.addClass("disabled");
27215         }
27216     },
27217
27218     /**
27219      * Enables this TabPanelItem if it was previously disabled.
27220      */
27221     enable : function(){
27222         this.disabled = false;
27223         this.pnode.removeClass("disabled");
27224     },
27225
27226     /**
27227      * Sets the content for this TabPanelItem.
27228      * @param {String} content The content
27229      * @param {Boolean} loadScripts true to look for and load scripts
27230      */
27231     setContent : function(content, loadScripts){
27232         this.bodyEl.update(content, loadScripts);
27233     },
27234
27235     /**
27236      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27237      * @return {Roo.UpdateManager} The UpdateManager
27238      */
27239     getUpdateManager : function(){
27240         return this.bodyEl.getUpdateManager();
27241     },
27242
27243     /**
27244      * Set a URL to be used to load the content for this TabPanelItem.
27245      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27246      * @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)
27247      * @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)
27248      * @return {Roo.UpdateManager} The UpdateManager
27249      */
27250     setUrl : function(url, params, loadOnce){
27251         if(this.refreshDelegate){
27252             this.un('activate', this.refreshDelegate);
27253         }
27254         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27255         this.on("activate", this.refreshDelegate);
27256         return this.bodyEl.getUpdateManager();
27257     },
27258
27259     /** @private */
27260     _handleRefresh : function(url, params, loadOnce){
27261         if(!loadOnce || !this.loaded){
27262             var updater = this.bodyEl.getUpdateManager();
27263             updater.update(url, params, this._setLoaded.createDelegate(this));
27264         }
27265     },
27266
27267     /**
27268      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27269      *   Will fail silently if the setUrl method has not been called.
27270      *   This does not activate the panel, just updates its content.
27271      */
27272     refresh : function(){
27273         if(this.refreshDelegate){
27274            this.loaded = false;
27275            this.refreshDelegate();
27276         }
27277     },
27278
27279     /** @private */
27280     _setLoaded : function(){
27281         this.loaded = true;
27282     },
27283
27284     /** @private */
27285     closeClick : function(e){
27286         var o = {};
27287         e.stopEvent();
27288         this.fireEvent("beforeclose", this, o);
27289         if(o.cancel !== true){
27290             this.tabPanel.removeTab(this.id);
27291         }
27292     },
27293     /**
27294      * The text displayed in the tooltip for the close icon.
27295      * @type String
27296      */
27297     closeText : "Close this tab"
27298 });
27299
27300 /** @private */
27301 Roo.TabPanel.prototype.createStrip = function(container){
27302     var strip = document.createElement("div");
27303     strip.className = "x-tabs-wrap";
27304     container.appendChild(strip);
27305     return strip;
27306 };
27307 /** @private */
27308 Roo.TabPanel.prototype.createStripList = function(strip){
27309     // div wrapper for retard IE
27310     // returns the "tr" element.
27311     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27312         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27313         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27314     return strip.firstChild.firstChild.firstChild.firstChild;
27315 };
27316 /** @private */
27317 Roo.TabPanel.prototype.createBody = function(container){
27318     var body = document.createElement("div");
27319     Roo.id(body, "tab-body");
27320     Roo.fly(body).addClass("x-tabs-body");
27321     container.appendChild(body);
27322     return body;
27323 };
27324 /** @private */
27325 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27326     var body = Roo.getDom(id);
27327     if(!body){
27328         body = document.createElement("div");
27329         body.id = id;
27330     }
27331     Roo.fly(body).addClass("x-tabs-item-body");
27332     bodyEl.insertBefore(body, bodyEl.firstChild);
27333     return body;
27334 };
27335 /** @private */
27336 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27337     var td = document.createElement("td");
27338     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27339     //stripEl.appendChild(td);
27340     if(closable){
27341         td.className = "x-tabs-closable";
27342         if(!this.closeTpl){
27343             this.closeTpl = new Roo.Template(
27344                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27345                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27346                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27347             );
27348         }
27349         var el = this.closeTpl.overwrite(td, {"text": text});
27350         var close = el.getElementsByTagName("div")[0];
27351         var inner = el.getElementsByTagName("em")[0];
27352         return {"el": el, "close": close, "inner": inner};
27353     } else {
27354         if(!this.tabTpl){
27355             this.tabTpl = new Roo.Template(
27356                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27357                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27358             );
27359         }
27360         var el = this.tabTpl.overwrite(td, {"text": text});
27361         var inner = el.getElementsByTagName("em")[0];
27362         return {"el": el, "inner": inner};
27363     }
27364 };/*
27365  * Based on:
27366  * Ext JS Library 1.1.1
27367  * Copyright(c) 2006-2007, Ext JS, LLC.
27368  *
27369  * Originally Released Under LGPL - original licence link has changed is not relivant.
27370  *
27371  * Fork - LGPL
27372  * <script type="text/javascript">
27373  */
27374
27375 /**
27376  * @class Roo.Button
27377  * @extends Roo.util.Observable
27378  * Simple Button class
27379  * @cfg {String} text The button text
27380  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27381  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27382  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27383  * @cfg {Object} scope The scope of the handler
27384  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27385  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27386  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27387  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27388  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27389  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27390    applies if enableToggle = true)
27391  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27392  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27393   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27394  * @constructor
27395  * Create a new button
27396  * @param {Object} config The config object
27397  */
27398 Roo.Button = function(renderTo, config)
27399 {
27400     if (!config) {
27401         config = renderTo;
27402         renderTo = config.renderTo || false;
27403     }
27404     
27405     Roo.apply(this, config);
27406     this.addEvents({
27407         /**
27408              * @event click
27409              * Fires when this button is clicked
27410              * @param {Button} this
27411              * @param {EventObject} e The click event
27412              */
27413             "click" : true,
27414         /**
27415              * @event toggle
27416              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27417              * @param {Button} this
27418              * @param {Boolean} pressed
27419              */
27420             "toggle" : true,
27421         /**
27422              * @event mouseover
27423              * Fires when the mouse hovers over the button
27424              * @param {Button} this
27425              * @param {Event} e The event object
27426              */
27427         'mouseover' : true,
27428         /**
27429              * @event mouseout
27430              * Fires when the mouse exits the button
27431              * @param {Button} this
27432              * @param {Event} e The event object
27433              */
27434         'mouseout': true,
27435          /**
27436              * @event render
27437              * Fires when the button is rendered
27438              * @param {Button} this
27439              */
27440         'render': true
27441     });
27442     if(this.menu){
27443         this.menu = Roo.menu.MenuMgr.get(this.menu);
27444     }
27445     // register listeners first!!  - so render can be captured..
27446     Roo.util.Observable.call(this);
27447     if(renderTo){
27448         this.render(renderTo);
27449     }
27450     
27451   
27452 };
27453
27454 Roo.extend(Roo.Button, Roo.util.Observable, {
27455     /**
27456      * 
27457      */
27458     
27459     /**
27460      * Read-only. True if this button is hidden
27461      * @type Boolean
27462      */
27463     hidden : false,
27464     /**
27465      * Read-only. True if this button is disabled
27466      * @type Boolean
27467      */
27468     disabled : false,
27469     /**
27470      * Read-only. True if this button is pressed (only if enableToggle = true)
27471      * @type Boolean
27472      */
27473     pressed : false,
27474
27475     /**
27476      * @cfg {Number} tabIndex 
27477      * The DOM tabIndex for this button (defaults to undefined)
27478      */
27479     tabIndex : undefined,
27480
27481     /**
27482      * @cfg {Boolean} enableToggle
27483      * True to enable pressed/not pressed toggling (defaults to false)
27484      */
27485     enableToggle: false,
27486     /**
27487      * @cfg {Mixed} menu
27488      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27489      */
27490     menu : undefined,
27491     /**
27492      * @cfg {String} menuAlign
27493      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27494      */
27495     menuAlign : "tl-bl?",
27496
27497     /**
27498      * @cfg {String} iconCls
27499      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27500      */
27501     iconCls : undefined,
27502     /**
27503      * @cfg {String} type
27504      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27505      */
27506     type : 'button',
27507
27508     // private
27509     menuClassTarget: 'tr',
27510
27511     /**
27512      * @cfg {String} clickEvent
27513      * The type of event to map to the button's event handler (defaults to 'click')
27514      */
27515     clickEvent : 'click',
27516
27517     /**
27518      * @cfg {Boolean} handleMouseEvents
27519      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27520      */
27521     handleMouseEvents : true,
27522
27523     /**
27524      * @cfg {String} tooltipType
27525      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27526      */
27527     tooltipType : 'qtip',
27528
27529     /**
27530      * @cfg {String} cls
27531      * A CSS class to apply to the button's main element.
27532      */
27533     
27534     /**
27535      * @cfg {Roo.Template} template (Optional)
27536      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27537      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27538      * require code modifications if required elements (e.g. a button) aren't present.
27539      */
27540
27541     // private
27542     render : function(renderTo){
27543         var btn;
27544         if(this.hideParent){
27545             this.parentEl = Roo.get(renderTo);
27546         }
27547         if(!this.dhconfig){
27548             if(!this.template){
27549                 if(!Roo.Button.buttonTemplate){
27550                     // hideous table template
27551                     Roo.Button.buttonTemplate = new Roo.Template(
27552                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27553                         '<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>',
27554                         "</tr></tbody></table>");
27555                 }
27556                 this.template = Roo.Button.buttonTemplate;
27557             }
27558             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27559             var btnEl = btn.child("button:first");
27560             btnEl.on('focus', this.onFocus, this);
27561             btnEl.on('blur', this.onBlur, this);
27562             if(this.cls){
27563                 btn.addClass(this.cls);
27564             }
27565             if(this.icon){
27566                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27567             }
27568             if(this.iconCls){
27569                 btnEl.addClass(this.iconCls);
27570                 if(!this.cls){
27571                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27572                 }
27573             }
27574             if(this.tabIndex !== undefined){
27575                 btnEl.dom.tabIndex = this.tabIndex;
27576             }
27577             if(this.tooltip){
27578                 if(typeof this.tooltip == 'object'){
27579                     Roo.QuickTips.tips(Roo.apply({
27580                           target: btnEl.id
27581                     }, this.tooltip));
27582                 } else {
27583                     btnEl.dom[this.tooltipType] = this.tooltip;
27584                 }
27585             }
27586         }else{
27587             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27588         }
27589         this.el = btn;
27590         if(this.id){
27591             this.el.dom.id = this.el.id = this.id;
27592         }
27593         if(this.menu){
27594             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27595             this.menu.on("show", this.onMenuShow, this);
27596             this.menu.on("hide", this.onMenuHide, this);
27597         }
27598         btn.addClass("x-btn");
27599         if(Roo.isIE && !Roo.isIE7){
27600             this.autoWidth.defer(1, this);
27601         }else{
27602             this.autoWidth();
27603         }
27604         if(this.handleMouseEvents){
27605             btn.on("mouseover", this.onMouseOver, this);
27606             btn.on("mouseout", this.onMouseOut, this);
27607             btn.on("mousedown", this.onMouseDown, this);
27608         }
27609         btn.on(this.clickEvent, this.onClick, this);
27610         //btn.on("mouseup", this.onMouseUp, this);
27611         if(this.hidden){
27612             this.hide();
27613         }
27614         if(this.disabled){
27615             this.disable();
27616         }
27617         Roo.ButtonToggleMgr.register(this);
27618         if(this.pressed){
27619             this.el.addClass("x-btn-pressed");
27620         }
27621         if(this.repeat){
27622             var repeater = new Roo.util.ClickRepeater(btn,
27623                 typeof this.repeat == "object" ? this.repeat : {}
27624             );
27625             repeater.on("click", this.onClick,  this);
27626         }
27627         
27628         this.fireEvent('render', this);
27629         
27630     },
27631     /**
27632      * Returns the button's underlying element
27633      * @return {Roo.Element} The element
27634      */
27635     getEl : function(){
27636         return this.el;  
27637     },
27638     
27639     /**
27640      * Destroys this Button and removes any listeners.
27641      */
27642     destroy : function(){
27643         Roo.ButtonToggleMgr.unregister(this);
27644         this.el.removeAllListeners();
27645         this.purgeListeners();
27646         this.el.remove();
27647     },
27648
27649     // private
27650     autoWidth : function(){
27651         if(this.el){
27652             this.el.setWidth("auto");
27653             if(Roo.isIE7 && Roo.isStrict){
27654                 var ib = this.el.child('button');
27655                 if(ib && ib.getWidth() > 20){
27656                     ib.clip();
27657                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27658                 }
27659             }
27660             if(this.minWidth){
27661                 if(this.hidden){
27662                     this.el.beginMeasure();
27663                 }
27664                 if(this.el.getWidth() < this.minWidth){
27665                     this.el.setWidth(this.minWidth);
27666                 }
27667                 if(this.hidden){
27668                     this.el.endMeasure();
27669                 }
27670             }
27671         }
27672     },
27673
27674     /**
27675      * Assigns this button's click handler
27676      * @param {Function} handler The function to call when the button is clicked
27677      * @param {Object} scope (optional) Scope for the function passed in
27678      */
27679     setHandler : function(handler, scope){
27680         this.handler = handler;
27681         this.scope = scope;  
27682     },
27683     
27684     /**
27685      * Sets this button's text
27686      * @param {String} text The button text
27687      */
27688     setText : function(text){
27689         this.text = text;
27690         if(this.el){
27691             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27692         }
27693         this.autoWidth();
27694     },
27695     
27696     /**
27697      * Gets the text for this button
27698      * @return {String} The button text
27699      */
27700     getText : function(){
27701         return this.text;  
27702     },
27703     
27704     /**
27705      * Show this button
27706      */
27707     show: function(){
27708         this.hidden = false;
27709         if(this.el){
27710             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27711         }
27712     },
27713     
27714     /**
27715      * Hide this button
27716      */
27717     hide: function(){
27718         this.hidden = true;
27719         if(this.el){
27720             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27721         }
27722     },
27723     
27724     /**
27725      * Convenience function for boolean show/hide
27726      * @param {Boolean} visible True to show, false to hide
27727      */
27728     setVisible: function(visible){
27729         if(visible) {
27730             this.show();
27731         }else{
27732             this.hide();
27733         }
27734     },
27735     
27736     /**
27737      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27738      * @param {Boolean} state (optional) Force a particular state
27739      */
27740     toggle : function(state){
27741         state = state === undefined ? !this.pressed : state;
27742         if(state != this.pressed){
27743             if(state){
27744                 this.el.addClass("x-btn-pressed");
27745                 this.pressed = true;
27746                 this.fireEvent("toggle", this, true);
27747             }else{
27748                 this.el.removeClass("x-btn-pressed");
27749                 this.pressed = false;
27750                 this.fireEvent("toggle", this, false);
27751             }
27752             if(this.toggleHandler){
27753                 this.toggleHandler.call(this.scope || this, this, state);
27754             }
27755         }
27756     },
27757     
27758     /**
27759      * Focus the button
27760      */
27761     focus : function(){
27762         this.el.child('button:first').focus();
27763     },
27764     
27765     /**
27766      * Disable this button
27767      */
27768     disable : function(){
27769         if(this.el){
27770             this.el.addClass("x-btn-disabled");
27771         }
27772         this.disabled = true;
27773     },
27774     
27775     /**
27776      * Enable this button
27777      */
27778     enable : function(){
27779         if(this.el){
27780             this.el.removeClass("x-btn-disabled");
27781         }
27782         this.disabled = false;
27783     },
27784
27785     /**
27786      * Convenience function for boolean enable/disable
27787      * @param {Boolean} enabled True to enable, false to disable
27788      */
27789     setDisabled : function(v){
27790         this[v !== true ? "enable" : "disable"]();
27791     },
27792
27793     // private
27794     onClick : function(e){
27795         if(e){
27796             e.preventDefault();
27797         }
27798         if(e.button != 0){
27799             return;
27800         }
27801         if(!this.disabled){
27802             if(this.enableToggle){
27803                 this.toggle();
27804             }
27805             if(this.menu && !this.menu.isVisible()){
27806                 this.menu.show(this.el, this.menuAlign);
27807             }
27808             this.fireEvent("click", this, e);
27809             if(this.handler){
27810                 this.el.removeClass("x-btn-over");
27811                 this.handler.call(this.scope || this, this, e);
27812             }
27813         }
27814     },
27815     // private
27816     onMouseOver : function(e){
27817         if(!this.disabled){
27818             this.el.addClass("x-btn-over");
27819             this.fireEvent('mouseover', this, e);
27820         }
27821     },
27822     // private
27823     onMouseOut : function(e){
27824         if(!e.within(this.el,  true)){
27825             this.el.removeClass("x-btn-over");
27826             this.fireEvent('mouseout', this, e);
27827         }
27828     },
27829     // private
27830     onFocus : function(e){
27831         if(!this.disabled){
27832             this.el.addClass("x-btn-focus");
27833         }
27834     },
27835     // private
27836     onBlur : function(e){
27837         this.el.removeClass("x-btn-focus");
27838     },
27839     // private
27840     onMouseDown : function(e){
27841         if(!this.disabled && e.button == 0){
27842             this.el.addClass("x-btn-click");
27843             Roo.get(document).on('mouseup', this.onMouseUp, this);
27844         }
27845     },
27846     // private
27847     onMouseUp : function(e){
27848         if(e.button == 0){
27849             this.el.removeClass("x-btn-click");
27850             Roo.get(document).un('mouseup', this.onMouseUp, this);
27851         }
27852     },
27853     // private
27854     onMenuShow : function(e){
27855         this.el.addClass("x-btn-menu-active");
27856     },
27857     // private
27858     onMenuHide : function(e){
27859         this.el.removeClass("x-btn-menu-active");
27860     }   
27861 });
27862
27863 // Private utility class used by Button
27864 Roo.ButtonToggleMgr = function(){
27865    var groups = {};
27866    
27867    function toggleGroup(btn, state){
27868        if(state){
27869            var g = groups[btn.toggleGroup];
27870            for(var i = 0, l = g.length; i < l; i++){
27871                if(g[i] != btn){
27872                    g[i].toggle(false);
27873                }
27874            }
27875        }
27876    }
27877    
27878    return {
27879        register : function(btn){
27880            if(!btn.toggleGroup){
27881                return;
27882            }
27883            var g = groups[btn.toggleGroup];
27884            if(!g){
27885                g = groups[btn.toggleGroup] = [];
27886            }
27887            g.push(btn);
27888            btn.on("toggle", toggleGroup);
27889        },
27890        
27891        unregister : function(btn){
27892            if(!btn.toggleGroup){
27893                return;
27894            }
27895            var g = groups[btn.toggleGroup];
27896            if(g){
27897                g.remove(btn);
27898                btn.un("toggle", toggleGroup);
27899            }
27900        }
27901    };
27902 }();/*
27903  * Based on:
27904  * Ext JS Library 1.1.1
27905  * Copyright(c) 2006-2007, Ext JS, LLC.
27906  *
27907  * Originally Released Under LGPL - original licence link has changed is not relivant.
27908  *
27909  * Fork - LGPL
27910  * <script type="text/javascript">
27911  */
27912  
27913 /**
27914  * @class Roo.SplitButton
27915  * @extends Roo.Button
27916  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27917  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27918  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27919  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27920  * @cfg {String} arrowTooltip The title attribute of the arrow
27921  * @constructor
27922  * Create a new menu button
27923  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27924  * @param {Object} config The config object
27925  */
27926 Roo.SplitButton = function(renderTo, config){
27927     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27928     /**
27929      * @event arrowclick
27930      * Fires when this button's arrow is clicked
27931      * @param {SplitButton} this
27932      * @param {EventObject} e The click event
27933      */
27934     this.addEvents({"arrowclick":true});
27935 };
27936
27937 Roo.extend(Roo.SplitButton, Roo.Button, {
27938     render : function(renderTo){
27939         // this is one sweet looking template!
27940         var tpl = new Roo.Template(
27941             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27942             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27943             '<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>',
27944             "</tbody></table></td><td>",
27945             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27946             '<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>',
27947             "</tbody></table></td></tr></table>"
27948         );
27949         var btn = tpl.append(renderTo, [this.text, this.type], true);
27950         var btnEl = btn.child("button");
27951         if(this.cls){
27952             btn.addClass(this.cls);
27953         }
27954         if(this.icon){
27955             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27956         }
27957         if(this.iconCls){
27958             btnEl.addClass(this.iconCls);
27959             if(!this.cls){
27960                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27961             }
27962         }
27963         this.el = btn;
27964         if(this.handleMouseEvents){
27965             btn.on("mouseover", this.onMouseOver, this);
27966             btn.on("mouseout", this.onMouseOut, this);
27967             btn.on("mousedown", this.onMouseDown, this);
27968             btn.on("mouseup", this.onMouseUp, this);
27969         }
27970         btn.on(this.clickEvent, this.onClick, this);
27971         if(this.tooltip){
27972             if(typeof this.tooltip == 'object'){
27973                 Roo.QuickTips.tips(Roo.apply({
27974                       target: btnEl.id
27975                 }, this.tooltip));
27976             } else {
27977                 btnEl.dom[this.tooltipType] = this.tooltip;
27978             }
27979         }
27980         if(this.arrowTooltip){
27981             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27982         }
27983         if(this.hidden){
27984             this.hide();
27985         }
27986         if(this.disabled){
27987             this.disable();
27988         }
27989         if(this.pressed){
27990             this.el.addClass("x-btn-pressed");
27991         }
27992         if(Roo.isIE && !Roo.isIE7){
27993             this.autoWidth.defer(1, this);
27994         }else{
27995             this.autoWidth();
27996         }
27997         if(this.menu){
27998             this.menu.on("show", this.onMenuShow, this);
27999             this.menu.on("hide", this.onMenuHide, this);
28000         }
28001         this.fireEvent('render', this);
28002     },
28003
28004     // private
28005     autoWidth : function(){
28006         if(this.el){
28007             var tbl = this.el.child("table:first");
28008             var tbl2 = this.el.child("table:last");
28009             this.el.setWidth("auto");
28010             tbl.setWidth("auto");
28011             if(Roo.isIE7 && Roo.isStrict){
28012                 var ib = this.el.child('button:first');
28013                 if(ib && ib.getWidth() > 20){
28014                     ib.clip();
28015                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28016                 }
28017             }
28018             if(this.minWidth){
28019                 if(this.hidden){
28020                     this.el.beginMeasure();
28021                 }
28022                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28023                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28024                 }
28025                 if(this.hidden){
28026                     this.el.endMeasure();
28027                 }
28028             }
28029             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28030         } 
28031     },
28032     /**
28033      * Sets this button's click handler
28034      * @param {Function} handler The function to call when the button is clicked
28035      * @param {Object} scope (optional) Scope for the function passed above
28036      */
28037     setHandler : function(handler, scope){
28038         this.handler = handler;
28039         this.scope = scope;  
28040     },
28041     
28042     /**
28043      * Sets this button's arrow click handler
28044      * @param {Function} handler The function to call when the arrow is clicked
28045      * @param {Object} scope (optional) Scope for the function passed above
28046      */
28047     setArrowHandler : function(handler, scope){
28048         this.arrowHandler = handler;
28049         this.scope = scope;  
28050     },
28051     
28052     /**
28053      * Focus the button
28054      */
28055     focus : function(){
28056         if(this.el){
28057             this.el.child("button:first").focus();
28058         }
28059     },
28060
28061     // private
28062     onClick : function(e){
28063         e.preventDefault();
28064         if(!this.disabled){
28065             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28066                 if(this.menu && !this.menu.isVisible()){
28067                     this.menu.show(this.el, this.menuAlign);
28068                 }
28069                 this.fireEvent("arrowclick", this, e);
28070                 if(this.arrowHandler){
28071                     this.arrowHandler.call(this.scope || this, this, e);
28072                 }
28073             }else{
28074                 this.fireEvent("click", this, e);
28075                 if(this.handler){
28076                     this.handler.call(this.scope || this, this, e);
28077                 }
28078             }
28079         }
28080     },
28081     // private
28082     onMouseDown : function(e){
28083         if(!this.disabled){
28084             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28085         }
28086     },
28087     // private
28088     onMouseUp : function(e){
28089         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28090     }   
28091 });
28092
28093
28094 // backwards compat
28095 Roo.MenuButton = Roo.SplitButton;/*
28096  * Based on:
28097  * Ext JS Library 1.1.1
28098  * Copyright(c) 2006-2007, Ext JS, LLC.
28099  *
28100  * Originally Released Under LGPL - original licence link has changed is not relivant.
28101  *
28102  * Fork - LGPL
28103  * <script type="text/javascript">
28104  */
28105
28106 /**
28107  * @class Roo.Toolbar
28108  * Basic Toolbar class.
28109  * @constructor
28110  * Creates a new Toolbar
28111  * @param {Object} container The config object
28112  */ 
28113 Roo.Toolbar = function(container, buttons, config)
28114 {
28115     /// old consturctor format still supported..
28116     if(container instanceof Array){ // omit the container for later rendering
28117         buttons = container;
28118         config = buttons;
28119         container = null;
28120     }
28121     if (typeof(container) == 'object' && container.xtype) {
28122         config = container;
28123         container = config.container;
28124         buttons = config.buttons || []; // not really - use items!!
28125     }
28126     var xitems = [];
28127     if (config && config.items) {
28128         xitems = config.items;
28129         delete config.items;
28130     }
28131     Roo.apply(this, config);
28132     this.buttons = buttons;
28133     
28134     if(container){
28135         this.render(container);
28136     }
28137     this.xitems = xitems;
28138     Roo.each(xitems, function(b) {
28139         this.add(b);
28140     }, this);
28141     
28142 };
28143
28144 Roo.Toolbar.prototype = {
28145     /**
28146      * @cfg {Array} items
28147      * array of button configs or elements to add (will be converted to a MixedCollection)
28148      */
28149     
28150     /**
28151      * @cfg {String/HTMLElement/Element} container
28152      * The id or element that will contain the toolbar
28153      */
28154     // private
28155     render : function(ct){
28156         this.el = Roo.get(ct);
28157         if(this.cls){
28158             this.el.addClass(this.cls);
28159         }
28160         // using a table allows for vertical alignment
28161         // 100% width is needed by Safari...
28162         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28163         this.tr = this.el.child("tr", true);
28164         var autoId = 0;
28165         this.items = new Roo.util.MixedCollection(false, function(o){
28166             return o.id || ("item" + (++autoId));
28167         });
28168         if(this.buttons){
28169             this.add.apply(this, this.buttons);
28170             delete this.buttons;
28171         }
28172     },
28173
28174     /**
28175      * Adds element(s) to the toolbar -- this function takes a variable number of 
28176      * arguments of mixed type and adds them to the toolbar.
28177      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28178      * <ul>
28179      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28180      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28181      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28182      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28183      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28184      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28185      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28186      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28187      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28188      * </ul>
28189      * @param {Mixed} arg2
28190      * @param {Mixed} etc.
28191      */
28192     add : function(){
28193         var a = arguments, l = a.length;
28194         for(var i = 0; i < l; i++){
28195             this._add(a[i]);
28196         }
28197     },
28198     // private..
28199     _add : function(el) {
28200         
28201         if (el.xtype) {
28202             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28203         }
28204         
28205         if (el.applyTo){ // some kind of form field
28206             return this.addField(el);
28207         } 
28208         if (el.render){ // some kind of Toolbar.Item
28209             return this.addItem(el);
28210         }
28211         if (typeof el == "string"){ // string
28212             if(el == "separator" || el == "-"){
28213                 return this.addSeparator();
28214             }
28215             if (el == " "){
28216                 return this.addSpacer();
28217             }
28218             if(el == "->"){
28219                 return this.addFill();
28220             }
28221             return this.addText(el);
28222             
28223         }
28224         if(el.tagName){ // element
28225             return this.addElement(el);
28226         }
28227         if(typeof el == "object"){ // must be button config?
28228             return this.addButton(el);
28229         }
28230         // and now what?!?!
28231         return false;
28232         
28233     },
28234     
28235     /**
28236      * Add an Xtype element
28237      * @param {Object} xtype Xtype Object
28238      * @return {Object} created Object
28239      */
28240     addxtype : function(e){
28241         return this.add(e);  
28242     },
28243     
28244     /**
28245      * Returns the Element for this toolbar.
28246      * @return {Roo.Element}
28247      */
28248     getEl : function(){
28249         return this.el;  
28250     },
28251     
28252     /**
28253      * Adds a separator
28254      * @return {Roo.Toolbar.Item} The separator item
28255      */
28256     addSeparator : function(){
28257         return this.addItem(new Roo.Toolbar.Separator());
28258     },
28259
28260     /**
28261      * Adds a spacer element
28262      * @return {Roo.Toolbar.Spacer} The spacer item
28263      */
28264     addSpacer : function(){
28265         return this.addItem(new Roo.Toolbar.Spacer());
28266     },
28267
28268     /**
28269      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28270      * @return {Roo.Toolbar.Fill} The fill item
28271      */
28272     addFill : function(){
28273         return this.addItem(new Roo.Toolbar.Fill());
28274     },
28275
28276     /**
28277      * Adds any standard HTML element to the toolbar
28278      * @param {String/HTMLElement/Element} el The element or id of the element to add
28279      * @return {Roo.Toolbar.Item} The element's item
28280      */
28281     addElement : function(el){
28282         return this.addItem(new Roo.Toolbar.Item(el));
28283     },
28284     /**
28285      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28286      * @type Roo.util.MixedCollection  
28287      */
28288     items : false,
28289      
28290     /**
28291      * Adds any Toolbar.Item or subclass
28292      * @param {Roo.Toolbar.Item} item
28293      * @return {Roo.Toolbar.Item} The item
28294      */
28295     addItem : function(item){
28296         var td = this.nextBlock();
28297         item.render(td);
28298         this.items.add(item);
28299         return item;
28300     },
28301     
28302     /**
28303      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28304      * @param {Object/Array} config A button config or array of configs
28305      * @return {Roo.Toolbar.Button/Array}
28306      */
28307     addButton : function(config){
28308         if(config instanceof Array){
28309             var buttons = [];
28310             for(var i = 0, len = config.length; i < len; i++) {
28311                 buttons.push(this.addButton(config[i]));
28312             }
28313             return buttons;
28314         }
28315         var b = config;
28316         if(!(config instanceof Roo.Toolbar.Button)){
28317             b = config.split ?
28318                 new Roo.Toolbar.SplitButton(config) :
28319                 new Roo.Toolbar.Button(config);
28320         }
28321         var td = this.nextBlock();
28322         b.render(td);
28323         this.items.add(b);
28324         return b;
28325     },
28326     
28327     /**
28328      * Adds text to the toolbar
28329      * @param {String} text The text to add
28330      * @return {Roo.Toolbar.Item} The element's item
28331      */
28332     addText : function(text){
28333         return this.addItem(new Roo.Toolbar.TextItem(text));
28334     },
28335     
28336     /**
28337      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28338      * @param {Number} index The index where the item is to be inserted
28339      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28340      * @return {Roo.Toolbar.Button/Item}
28341      */
28342     insertButton : function(index, item){
28343         if(item instanceof Array){
28344             var buttons = [];
28345             for(var i = 0, len = item.length; i < len; i++) {
28346                buttons.push(this.insertButton(index + i, item[i]));
28347             }
28348             return buttons;
28349         }
28350         if (!(item instanceof Roo.Toolbar.Button)){
28351            item = new Roo.Toolbar.Button(item);
28352         }
28353         var td = document.createElement("td");
28354         this.tr.insertBefore(td, this.tr.childNodes[index]);
28355         item.render(td);
28356         this.items.insert(index, item);
28357         return item;
28358     },
28359     
28360     /**
28361      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28362      * @param {Object} config
28363      * @return {Roo.Toolbar.Item} The element's item
28364      */
28365     addDom : function(config, returnEl){
28366         var td = this.nextBlock();
28367         Roo.DomHelper.overwrite(td, config);
28368         var ti = new Roo.Toolbar.Item(td.firstChild);
28369         ti.render(td);
28370         this.items.add(ti);
28371         return ti;
28372     },
28373
28374     /**
28375      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28376      * @type Roo.util.MixedCollection  
28377      */
28378     fields : false,
28379     
28380     /**
28381      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28382      * Note: the field should not have been rendered yet. For a field that has already been
28383      * rendered, use {@link #addElement}.
28384      * @param {Roo.form.Field} field
28385      * @return {Roo.ToolbarItem}
28386      */
28387      
28388       
28389     addField : function(field) {
28390         if (!this.fields) {
28391             var autoId = 0;
28392             this.fields = new Roo.util.MixedCollection(false, function(o){
28393                 return o.id || ("item" + (++autoId));
28394             });
28395
28396         }
28397         
28398         var td = this.nextBlock();
28399         field.render(td);
28400         var ti = new Roo.Toolbar.Item(td.firstChild);
28401         ti.render(td);
28402         this.items.add(ti);
28403         this.fields.add(field);
28404         return ti;
28405     },
28406     /**
28407      * Hide the toolbar
28408      * @method hide
28409      */
28410      
28411       
28412     hide : function()
28413     {
28414         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28415         this.el.child('div').hide();
28416     },
28417     /**
28418      * Show the toolbar
28419      * @method show
28420      */
28421     show : function()
28422     {
28423         this.el.child('div').show();
28424     },
28425       
28426     // private
28427     nextBlock : function(){
28428         var td = document.createElement("td");
28429         this.tr.appendChild(td);
28430         return td;
28431     },
28432
28433     // private
28434     destroy : function(){
28435         if(this.items){ // rendered?
28436             Roo.destroy.apply(Roo, this.items.items);
28437         }
28438         if(this.fields){ // rendered?
28439             Roo.destroy.apply(Roo, this.fields.items);
28440         }
28441         Roo.Element.uncache(this.el, this.tr);
28442     }
28443 };
28444
28445 /**
28446  * @class Roo.Toolbar.Item
28447  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28448  * @constructor
28449  * Creates a new Item
28450  * @param {HTMLElement} el 
28451  */
28452 Roo.Toolbar.Item = function(el){
28453     this.el = Roo.getDom(el);
28454     this.id = Roo.id(this.el);
28455     this.hidden = false;
28456 };
28457
28458 Roo.Toolbar.Item.prototype = {
28459     
28460     /**
28461      * Get this item's HTML Element
28462      * @return {HTMLElement}
28463      */
28464     getEl : function(){
28465        return this.el;  
28466     },
28467
28468     // private
28469     render : function(td){
28470         this.td = td;
28471         td.appendChild(this.el);
28472     },
28473     
28474     /**
28475      * Removes and destroys this item.
28476      */
28477     destroy : function(){
28478         this.td.parentNode.removeChild(this.td);
28479     },
28480     
28481     /**
28482      * Shows this item.
28483      */
28484     show: function(){
28485         this.hidden = false;
28486         this.td.style.display = "";
28487     },
28488     
28489     /**
28490      * Hides this item.
28491      */
28492     hide: function(){
28493         this.hidden = true;
28494         this.td.style.display = "none";
28495     },
28496     
28497     /**
28498      * Convenience function for boolean show/hide.
28499      * @param {Boolean} visible true to show/false to hide
28500      */
28501     setVisible: function(visible){
28502         if(visible) {
28503             this.show();
28504         }else{
28505             this.hide();
28506         }
28507     },
28508     
28509     /**
28510      * Try to focus this item.
28511      */
28512     focus : function(){
28513         Roo.fly(this.el).focus();
28514     },
28515     
28516     /**
28517      * Disables this item.
28518      */
28519     disable : function(){
28520         Roo.fly(this.td).addClass("x-item-disabled");
28521         this.disabled = true;
28522         this.el.disabled = true;
28523     },
28524     
28525     /**
28526      * Enables this item.
28527      */
28528     enable : function(){
28529         Roo.fly(this.td).removeClass("x-item-disabled");
28530         this.disabled = false;
28531         this.el.disabled = false;
28532     }
28533 };
28534
28535
28536 /**
28537  * @class Roo.Toolbar.Separator
28538  * @extends Roo.Toolbar.Item
28539  * A simple toolbar separator class
28540  * @constructor
28541  * Creates a new Separator
28542  */
28543 Roo.Toolbar.Separator = function(){
28544     var s = document.createElement("span");
28545     s.className = "ytb-sep";
28546     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28547 };
28548 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28549     enable:Roo.emptyFn,
28550     disable:Roo.emptyFn,
28551     focus:Roo.emptyFn
28552 });
28553
28554 /**
28555  * @class Roo.Toolbar.Spacer
28556  * @extends Roo.Toolbar.Item
28557  * A simple element that adds extra horizontal space to a toolbar.
28558  * @constructor
28559  * Creates a new Spacer
28560  */
28561 Roo.Toolbar.Spacer = function(){
28562     var s = document.createElement("div");
28563     s.className = "ytb-spacer";
28564     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28565 };
28566 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28567     enable:Roo.emptyFn,
28568     disable:Roo.emptyFn,
28569     focus:Roo.emptyFn
28570 });
28571
28572 /**
28573  * @class Roo.Toolbar.Fill
28574  * @extends Roo.Toolbar.Spacer
28575  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28576  * @constructor
28577  * Creates a new Spacer
28578  */
28579 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28580     // private
28581     render : function(td){
28582         td.style.width = '100%';
28583         Roo.Toolbar.Fill.superclass.render.call(this, td);
28584     }
28585 });
28586
28587 /**
28588  * @class Roo.Toolbar.TextItem
28589  * @extends Roo.Toolbar.Item
28590  * A simple class that renders text directly into a toolbar.
28591  * @constructor
28592  * Creates a new TextItem
28593  * @param {String} text
28594  */
28595 Roo.Toolbar.TextItem = function(text){
28596     if (typeof(text) == 'object') {
28597         text = text.text;
28598     }
28599     var s = document.createElement("span");
28600     s.className = "ytb-text";
28601     s.innerHTML = text;
28602     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28603 };
28604 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28605     enable:Roo.emptyFn,
28606     disable:Roo.emptyFn,
28607     focus:Roo.emptyFn
28608 });
28609
28610 /**
28611  * @class Roo.Toolbar.Button
28612  * @extends Roo.Button
28613  * A button that renders into a toolbar.
28614  * @constructor
28615  * Creates a new Button
28616  * @param {Object} config A standard {@link Roo.Button} config object
28617  */
28618 Roo.Toolbar.Button = function(config){
28619     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28620 };
28621 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28622     render : function(td){
28623         this.td = td;
28624         Roo.Toolbar.Button.superclass.render.call(this, td);
28625     },
28626     
28627     /**
28628      * Removes and destroys this button
28629      */
28630     destroy : function(){
28631         Roo.Toolbar.Button.superclass.destroy.call(this);
28632         this.td.parentNode.removeChild(this.td);
28633     },
28634     
28635     /**
28636      * Shows this button
28637      */
28638     show: function(){
28639         this.hidden = false;
28640         this.td.style.display = "";
28641     },
28642     
28643     /**
28644      * Hides this button
28645      */
28646     hide: function(){
28647         this.hidden = true;
28648         this.td.style.display = "none";
28649     },
28650
28651     /**
28652      * Disables this item
28653      */
28654     disable : function(){
28655         Roo.fly(this.td).addClass("x-item-disabled");
28656         this.disabled = true;
28657     },
28658
28659     /**
28660      * Enables this item
28661      */
28662     enable : function(){
28663         Roo.fly(this.td).removeClass("x-item-disabled");
28664         this.disabled = false;
28665     }
28666 });
28667 // backwards compat
28668 Roo.ToolbarButton = Roo.Toolbar.Button;
28669
28670 /**
28671  * @class Roo.Toolbar.SplitButton
28672  * @extends Roo.SplitButton
28673  * A menu button that renders into a toolbar.
28674  * @constructor
28675  * Creates a new SplitButton
28676  * @param {Object} config A standard {@link Roo.SplitButton} config object
28677  */
28678 Roo.Toolbar.SplitButton = function(config){
28679     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28680 };
28681 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28682     render : function(td){
28683         this.td = td;
28684         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28685     },
28686     
28687     /**
28688      * Removes and destroys this button
28689      */
28690     destroy : function(){
28691         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28692         this.td.parentNode.removeChild(this.td);
28693     },
28694     
28695     /**
28696      * Shows this button
28697      */
28698     show: function(){
28699         this.hidden = false;
28700         this.td.style.display = "";
28701     },
28702     
28703     /**
28704      * Hides this button
28705      */
28706     hide: function(){
28707         this.hidden = true;
28708         this.td.style.display = "none";
28709     }
28710 });
28711
28712 // backwards compat
28713 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28714  * Based on:
28715  * Ext JS Library 1.1.1
28716  * Copyright(c) 2006-2007, Ext JS, LLC.
28717  *
28718  * Originally Released Under LGPL - original licence link has changed is not relivant.
28719  *
28720  * Fork - LGPL
28721  * <script type="text/javascript">
28722  */
28723  
28724 /**
28725  * @class Roo.PagingToolbar
28726  * @extends Roo.Toolbar
28727  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28728  * @constructor
28729  * Create a new PagingToolbar
28730  * @param {Object} config The config object
28731  */
28732 Roo.PagingToolbar = function(el, ds, config)
28733 {
28734     // old args format still supported... - xtype is prefered..
28735     if (typeof(el) == 'object' && el.xtype) {
28736         // created from xtype...
28737         config = el;
28738         ds = el.dataSource;
28739         el = config.container;
28740     }
28741     var items = [];
28742     if (config.items) {
28743         items = config.items;
28744         config.items = [];
28745     }
28746     
28747     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28748     this.ds = ds;
28749     this.cursor = 0;
28750     this.renderButtons(this.el);
28751     this.bind(ds);
28752     
28753     // supprot items array.
28754    
28755     Roo.each(items, function(e) {
28756         this.add(Roo.factory(e));
28757     },this);
28758     
28759 };
28760
28761 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28762     /**
28763      * @cfg {Roo.data.Store} dataSource
28764      * The underlying data store providing the paged data
28765      */
28766     /**
28767      * @cfg {String/HTMLElement/Element} container
28768      * container The id or element that will contain the toolbar
28769      */
28770     /**
28771      * @cfg {Boolean} displayInfo
28772      * True to display the displayMsg (defaults to false)
28773      */
28774     /**
28775      * @cfg {Number} pageSize
28776      * The number of records to display per page (defaults to 20)
28777      */
28778     pageSize: 20,
28779     /**
28780      * @cfg {String} displayMsg
28781      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28782      */
28783     displayMsg : 'Displaying {0} - {1} of {2}',
28784     /**
28785      * @cfg {String} emptyMsg
28786      * The message to display when no records are found (defaults to "No data to display")
28787      */
28788     emptyMsg : 'No data to display',
28789     /**
28790      * Customizable piece of the default paging text (defaults to "Page")
28791      * @type String
28792      */
28793     beforePageText : "Page",
28794     /**
28795      * Customizable piece of the default paging text (defaults to "of %0")
28796      * @type String
28797      */
28798     afterPageText : "of {0}",
28799     /**
28800      * Customizable piece of the default paging text (defaults to "First Page")
28801      * @type String
28802      */
28803     firstText : "First Page",
28804     /**
28805      * Customizable piece of the default paging text (defaults to "Previous Page")
28806      * @type String
28807      */
28808     prevText : "Previous Page",
28809     /**
28810      * Customizable piece of the default paging text (defaults to "Next Page")
28811      * @type String
28812      */
28813     nextText : "Next Page",
28814     /**
28815      * Customizable piece of the default paging text (defaults to "Last Page")
28816      * @type String
28817      */
28818     lastText : "Last Page",
28819     /**
28820      * Customizable piece of the default paging text (defaults to "Refresh")
28821      * @type String
28822      */
28823     refreshText : "Refresh",
28824
28825     // private
28826     renderButtons : function(el){
28827         Roo.PagingToolbar.superclass.render.call(this, el);
28828         this.first = this.addButton({
28829             tooltip: this.firstText,
28830             cls: "x-btn-icon x-grid-page-first",
28831             disabled: true,
28832             handler: this.onClick.createDelegate(this, ["first"])
28833         });
28834         this.prev = this.addButton({
28835             tooltip: this.prevText,
28836             cls: "x-btn-icon x-grid-page-prev",
28837             disabled: true,
28838             handler: this.onClick.createDelegate(this, ["prev"])
28839         });
28840         //this.addSeparator();
28841         this.add(this.beforePageText);
28842         this.field = Roo.get(this.addDom({
28843            tag: "input",
28844            type: "text",
28845            size: "3",
28846            value: "1",
28847            cls: "x-grid-page-number"
28848         }).el);
28849         this.field.on("keydown", this.onPagingKeydown, this);
28850         this.field.on("focus", function(){this.dom.select();});
28851         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28852         this.field.setHeight(18);
28853         //this.addSeparator();
28854         this.next = this.addButton({
28855             tooltip: this.nextText,
28856             cls: "x-btn-icon x-grid-page-next",
28857             disabled: true,
28858             handler: this.onClick.createDelegate(this, ["next"])
28859         });
28860         this.last = this.addButton({
28861             tooltip: this.lastText,
28862             cls: "x-btn-icon x-grid-page-last",
28863             disabled: true,
28864             handler: this.onClick.createDelegate(this, ["last"])
28865         });
28866         //this.addSeparator();
28867         this.loading = this.addButton({
28868             tooltip: this.refreshText,
28869             cls: "x-btn-icon x-grid-loading",
28870             handler: this.onClick.createDelegate(this, ["refresh"])
28871         });
28872
28873         if(this.displayInfo){
28874             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28875         }
28876     },
28877
28878     // private
28879     updateInfo : function(){
28880         if(this.displayEl){
28881             var count = this.ds.getCount();
28882             var msg = count == 0 ?
28883                 this.emptyMsg :
28884                 String.format(
28885                     this.displayMsg,
28886                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28887                 );
28888             this.displayEl.update(msg);
28889         }
28890     },
28891
28892     // private
28893     onLoad : function(ds, r, o){
28894        this.cursor = o.params ? o.params.start : 0;
28895        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28896
28897        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28898        this.field.dom.value = ap;
28899        this.first.setDisabled(ap == 1);
28900        this.prev.setDisabled(ap == 1);
28901        this.next.setDisabled(ap == ps);
28902        this.last.setDisabled(ap == ps);
28903        this.loading.enable();
28904        this.updateInfo();
28905     },
28906
28907     // private
28908     getPageData : function(){
28909         var total = this.ds.getTotalCount();
28910         return {
28911             total : total,
28912             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28913             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28914         };
28915     },
28916
28917     // private
28918     onLoadError : function(){
28919         this.loading.enable();
28920     },
28921
28922     // private
28923     onPagingKeydown : function(e){
28924         var k = e.getKey();
28925         var d = this.getPageData();
28926         if(k == e.RETURN){
28927             var v = this.field.dom.value, pageNum;
28928             if(!v || isNaN(pageNum = parseInt(v, 10))){
28929                 this.field.dom.value = d.activePage;
28930                 return;
28931             }
28932             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28933             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28934             e.stopEvent();
28935         }
28936         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))
28937         {
28938           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28939           this.field.dom.value = pageNum;
28940           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28941           e.stopEvent();
28942         }
28943         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28944         {
28945           var v = this.field.dom.value, pageNum; 
28946           var increment = (e.shiftKey) ? 10 : 1;
28947           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28948             increment *= -1;
28949           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28950             this.field.dom.value = d.activePage;
28951             return;
28952           }
28953           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28954           {
28955             this.field.dom.value = parseInt(v, 10) + increment;
28956             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28957             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28958           }
28959           e.stopEvent();
28960         }
28961     },
28962
28963     // private
28964     beforeLoad : function(){
28965         if(this.loading){
28966             this.loading.disable();
28967         }
28968     },
28969
28970     // private
28971     onClick : function(which){
28972         var ds = this.ds;
28973         switch(which){
28974             case "first":
28975                 ds.load({params:{start: 0, limit: this.pageSize}});
28976             break;
28977             case "prev":
28978                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28979             break;
28980             case "next":
28981                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28982             break;
28983             case "last":
28984                 var total = ds.getTotalCount();
28985                 var extra = total % this.pageSize;
28986                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28987                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28988             break;
28989             case "refresh":
28990                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28991             break;
28992         }
28993     },
28994
28995     /**
28996      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28997      * @param {Roo.data.Store} store The data store to unbind
28998      */
28999     unbind : function(ds){
29000         ds.un("beforeload", this.beforeLoad, this);
29001         ds.un("load", this.onLoad, this);
29002         ds.un("loadexception", this.onLoadError, this);
29003         ds.un("remove", this.updateInfo, this);
29004         ds.un("add", this.updateInfo, this);
29005         this.ds = undefined;
29006     },
29007
29008     /**
29009      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29010      * @param {Roo.data.Store} store The data store to bind
29011      */
29012     bind : function(ds){
29013         ds.on("beforeload", this.beforeLoad, this);
29014         ds.on("load", this.onLoad, this);
29015         ds.on("loadexception", this.onLoadError, this);
29016         ds.on("remove", this.updateInfo, this);
29017         ds.on("add", this.updateInfo, this);
29018         this.ds = ds;
29019     }
29020 });/*
29021  * Based on:
29022  * Ext JS Library 1.1.1
29023  * Copyright(c) 2006-2007, Ext JS, LLC.
29024  *
29025  * Originally Released Under LGPL - original licence link has changed is not relivant.
29026  *
29027  * Fork - LGPL
29028  * <script type="text/javascript">
29029  */
29030
29031 /**
29032  * @class Roo.Resizable
29033  * @extends Roo.util.Observable
29034  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29035  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29036  * 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
29037  * the element will be wrapped for you automatically.</p>
29038  * <p>Here is the list of valid resize handles:</p>
29039  * <pre>
29040 Value   Description
29041 ------  -------------------
29042  'n'     north
29043  's'     south
29044  'e'     east
29045  'w'     west
29046  'nw'    northwest
29047  'sw'    southwest
29048  'se'    southeast
29049  'ne'    northeast
29050  'hd'    horizontal drag
29051  'all'   all
29052 </pre>
29053  * <p>Here's an example showing the creation of a typical Resizable:</p>
29054  * <pre><code>
29055 var resizer = new Roo.Resizable("element-id", {
29056     handles: 'all',
29057     minWidth: 200,
29058     minHeight: 100,
29059     maxWidth: 500,
29060     maxHeight: 400,
29061     pinned: true
29062 });
29063 resizer.on("resize", myHandler);
29064 </code></pre>
29065  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29066  * resizer.east.setDisplayed(false);</p>
29067  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29068  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29069  * resize operation's new size (defaults to [0, 0])
29070  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29071  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29072  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29073  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29074  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29075  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29076  * @cfg {Number} width The width of the element in pixels (defaults to null)
29077  * @cfg {Number} height The height of the element in pixels (defaults to null)
29078  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29079  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29080  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29081  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29082  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29083  * in favor of the handles config option (defaults to false)
29084  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29085  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29086  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29087  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29088  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29089  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29090  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29091  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29092  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29093  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29094  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29095  * @constructor
29096  * Create a new resizable component
29097  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29098  * @param {Object} config configuration options
29099   */
29100 Roo.Resizable = function(el, config)
29101 {
29102     this.el = Roo.get(el);
29103
29104     if(config && config.wrap){
29105         config.resizeChild = this.el;
29106         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29107         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29108         this.el.setStyle("overflow", "hidden");
29109         this.el.setPositioning(config.resizeChild.getPositioning());
29110         config.resizeChild.clearPositioning();
29111         if(!config.width || !config.height){
29112             var csize = config.resizeChild.getSize();
29113             this.el.setSize(csize.width, csize.height);
29114         }
29115         if(config.pinned && !config.adjustments){
29116             config.adjustments = "auto";
29117         }
29118     }
29119
29120     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29121     this.proxy.unselectable();
29122     this.proxy.enableDisplayMode('block');
29123
29124     Roo.apply(this, config);
29125
29126     if(this.pinned){
29127         this.disableTrackOver = true;
29128         this.el.addClass("x-resizable-pinned");
29129     }
29130     // if the element isn't positioned, make it relative
29131     var position = this.el.getStyle("position");
29132     if(position != "absolute" && position != "fixed"){
29133         this.el.setStyle("position", "relative");
29134     }
29135     if(!this.handles){ // no handles passed, must be legacy style
29136         this.handles = 's,e,se';
29137         if(this.multiDirectional){
29138             this.handles += ',n,w';
29139         }
29140     }
29141     if(this.handles == "all"){
29142         this.handles = "n s e w ne nw se sw";
29143     }
29144     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29145     var ps = Roo.Resizable.positions;
29146     for(var i = 0, len = hs.length; i < len; i++){
29147         if(hs[i] && ps[hs[i]]){
29148             var pos = ps[hs[i]];
29149             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29150         }
29151     }
29152     // legacy
29153     this.corner = this.southeast;
29154     
29155     // updateBox = the box can move..
29156     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29157         this.updateBox = true;
29158     }
29159
29160     this.activeHandle = null;
29161
29162     if(this.resizeChild){
29163         if(typeof this.resizeChild == "boolean"){
29164             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29165         }else{
29166             this.resizeChild = Roo.get(this.resizeChild, true);
29167         }
29168     }
29169     
29170     if(this.adjustments == "auto"){
29171         var rc = this.resizeChild;
29172         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29173         if(rc && (hw || hn)){
29174             rc.position("relative");
29175             rc.setLeft(hw ? hw.el.getWidth() : 0);
29176             rc.setTop(hn ? hn.el.getHeight() : 0);
29177         }
29178         this.adjustments = [
29179             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29180             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29181         ];
29182     }
29183
29184     if(this.draggable){
29185         this.dd = this.dynamic ?
29186             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29187         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29188     }
29189
29190     // public events
29191     this.addEvents({
29192         /**
29193          * @event beforeresize
29194          * Fired before resize is allowed. Set enabled to false to cancel resize.
29195          * @param {Roo.Resizable} this
29196          * @param {Roo.EventObject} e The mousedown event
29197          */
29198         "beforeresize" : true,
29199         /**
29200          * @event resizing
29201          * Fired a resizing.
29202          * @param {Roo.Resizable} this
29203          * @param {Number} x The new x position
29204          * @param {Number} y The new y position
29205          * @param {Number} w The new w width
29206          * @param {Number} h The new h hight
29207          * @param {Roo.EventObject} e The mouseup event
29208          */
29209         "resizing" : true,
29210         /**
29211          * @event resize
29212          * Fired after a resize.
29213          * @param {Roo.Resizable} this
29214          * @param {Number} width The new width
29215          * @param {Number} height The new height
29216          * @param {Roo.EventObject} e The mouseup event
29217          */
29218         "resize" : true
29219     });
29220
29221     if(this.width !== null && this.height !== null){
29222         this.resizeTo(this.width, this.height);
29223     }else{
29224         this.updateChildSize();
29225     }
29226     if(Roo.isIE){
29227         this.el.dom.style.zoom = 1;
29228     }
29229     Roo.Resizable.superclass.constructor.call(this);
29230 };
29231
29232 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29233         resizeChild : false,
29234         adjustments : [0, 0],
29235         minWidth : 5,
29236         minHeight : 5,
29237         maxWidth : 10000,
29238         maxHeight : 10000,
29239         enabled : true,
29240         animate : false,
29241         duration : .35,
29242         dynamic : false,
29243         handles : false,
29244         multiDirectional : false,
29245         disableTrackOver : false,
29246         easing : 'easeOutStrong',
29247         widthIncrement : 0,
29248         heightIncrement : 0,
29249         pinned : false,
29250         width : null,
29251         height : null,
29252         preserveRatio : false,
29253         transparent: false,
29254         minX: 0,
29255         minY: 0,
29256         draggable: false,
29257
29258         /**
29259          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29260          */
29261         constrainTo: undefined,
29262         /**
29263          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29264          */
29265         resizeRegion: undefined,
29266
29267
29268     /**
29269      * Perform a manual resize
29270      * @param {Number} width
29271      * @param {Number} height
29272      */
29273     resizeTo : function(width, height){
29274         this.el.setSize(width, height);
29275         this.updateChildSize();
29276         this.fireEvent("resize", this, width, height, null);
29277     },
29278
29279     // private
29280     startSizing : function(e, handle){
29281         this.fireEvent("beforeresize", this, e);
29282         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29283
29284             if(!this.overlay){
29285                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29286                 this.overlay.unselectable();
29287                 this.overlay.enableDisplayMode("block");
29288                 this.overlay.on("mousemove", this.onMouseMove, this);
29289                 this.overlay.on("mouseup", this.onMouseUp, this);
29290             }
29291             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29292
29293             this.resizing = true;
29294             this.startBox = this.el.getBox();
29295             this.startPoint = e.getXY();
29296             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29297                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29298
29299             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29300             this.overlay.show();
29301
29302             if(this.constrainTo) {
29303                 var ct = Roo.get(this.constrainTo);
29304                 this.resizeRegion = ct.getRegion().adjust(
29305                     ct.getFrameWidth('t'),
29306                     ct.getFrameWidth('l'),
29307                     -ct.getFrameWidth('b'),
29308                     -ct.getFrameWidth('r')
29309                 );
29310             }
29311
29312             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29313             this.proxy.show();
29314             this.proxy.setBox(this.startBox);
29315             if(!this.dynamic){
29316                 this.proxy.setStyle('visibility', 'visible');
29317             }
29318         }
29319     },
29320
29321     // private
29322     onMouseDown : function(handle, e){
29323         if(this.enabled){
29324             e.stopEvent();
29325             this.activeHandle = handle;
29326             this.startSizing(e, handle);
29327         }
29328     },
29329
29330     // private
29331     onMouseUp : function(e){
29332         var size = this.resizeElement();
29333         this.resizing = false;
29334         this.handleOut();
29335         this.overlay.hide();
29336         this.proxy.hide();
29337         this.fireEvent("resize", this, size.width, size.height, e);
29338     },
29339
29340     // private
29341     updateChildSize : function(){
29342         
29343         if(this.resizeChild){
29344             var el = this.el;
29345             var child = this.resizeChild;
29346             var adj = this.adjustments;
29347             if(el.dom.offsetWidth){
29348                 var b = el.getSize(true);
29349                 child.setSize(b.width+adj[0], b.height+adj[1]);
29350             }
29351             // Second call here for IE
29352             // The first call enables instant resizing and
29353             // the second call corrects scroll bars if they
29354             // exist
29355             if(Roo.isIE){
29356                 setTimeout(function(){
29357                     if(el.dom.offsetWidth){
29358                         var b = el.getSize(true);
29359                         child.setSize(b.width+adj[0], b.height+adj[1]);
29360                     }
29361                 }, 10);
29362             }
29363         }
29364     },
29365
29366     // private
29367     snap : function(value, inc, min){
29368         if(!inc || !value) return value;
29369         var newValue = value;
29370         var m = value % inc;
29371         if(m > 0){
29372             if(m > (inc/2)){
29373                 newValue = value + (inc-m);
29374             }else{
29375                 newValue = value - m;
29376             }
29377         }
29378         return Math.max(min, newValue);
29379     },
29380
29381     // private
29382     resizeElement : function(){
29383         var box = this.proxy.getBox();
29384         if(this.updateBox){
29385             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29386         }else{
29387             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29388         }
29389         this.updateChildSize();
29390         if(!this.dynamic){
29391             this.proxy.hide();
29392         }
29393         return box;
29394     },
29395
29396     // private
29397     constrain : function(v, diff, m, mx){
29398         if(v - diff < m){
29399             diff = v - m;
29400         }else if(v - diff > mx){
29401             diff = mx - v;
29402         }
29403         return diff;
29404     },
29405
29406     // private
29407     onMouseMove : function(e){
29408         
29409         if(this.enabled){
29410             try{// try catch so if something goes wrong the user doesn't get hung
29411
29412             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29413                 return;
29414             }
29415
29416             //var curXY = this.startPoint;
29417             var curSize = this.curSize || this.startBox;
29418             var x = this.startBox.x, y = this.startBox.y;
29419             var ox = x, oy = y;
29420             var w = curSize.width, h = curSize.height;
29421             var ow = w, oh = h;
29422             var mw = this.minWidth, mh = this.minHeight;
29423             var mxw = this.maxWidth, mxh = this.maxHeight;
29424             var wi = this.widthIncrement;
29425             var hi = this.heightIncrement;
29426
29427             var eventXY = e.getXY();
29428             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29429             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29430
29431             var pos = this.activeHandle.position;
29432
29433             switch(pos){
29434                 case "east":
29435                     w += diffX;
29436                     w = Math.min(Math.max(mw, w), mxw);
29437                     break;
29438              
29439                 case "south":
29440                     h += diffY;
29441                     h = Math.min(Math.max(mh, h), mxh);
29442                     break;
29443                 case "southeast":
29444                     w += diffX;
29445                     h += diffY;
29446                     w = Math.min(Math.max(mw, w), mxw);
29447                     h = Math.min(Math.max(mh, h), mxh);
29448                     break;
29449                 case "north":
29450                     diffY = this.constrain(h, diffY, mh, mxh);
29451                     y += diffY;
29452                     h -= diffY;
29453                     break;
29454                 case "hdrag":
29455                     
29456                     if (wi) {
29457                         var adiffX = Math.abs(diffX);
29458                         var sub = (adiffX % wi); // how much 
29459                         if (sub > (wi/2)) { // far enough to snap
29460                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29461                         } else {
29462                             // remove difference.. 
29463                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29464                         }
29465                     }
29466                     x += diffX;
29467                     x = Math.max(this.minX, x);
29468                     break;
29469                 case "west":
29470                     diffX = this.constrain(w, diffX, mw, mxw);
29471                     x += diffX;
29472                     w -= diffX;
29473                     break;
29474                 case "northeast":
29475                     w += diffX;
29476                     w = Math.min(Math.max(mw, w), mxw);
29477                     diffY = this.constrain(h, diffY, mh, mxh);
29478                     y += diffY;
29479                     h -= diffY;
29480                     break;
29481                 case "northwest":
29482                     diffX = this.constrain(w, diffX, mw, mxw);
29483                     diffY = this.constrain(h, diffY, mh, mxh);
29484                     y += diffY;
29485                     h -= diffY;
29486                     x += diffX;
29487                     w -= diffX;
29488                     break;
29489                case "southwest":
29490                     diffX = this.constrain(w, diffX, mw, mxw);
29491                     h += diffY;
29492                     h = Math.min(Math.max(mh, h), mxh);
29493                     x += diffX;
29494                     w -= diffX;
29495                     break;
29496             }
29497
29498             var sw = this.snap(w, wi, mw);
29499             var sh = this.snap(h, hi, mh);
29500             if(sw != w || sh != h){
29501                 switch(pos){
29502                     case "northeast":
29503                         y -= sh - h;
29504                     break;
29505                     case "north":
29506                         y -= sh - h;
29507                         break;
29508                     case "southwest":
29509                         x -= sw - w;
29510                     break;
29511                     case "west":
29512                         x -= sw - w;
29513                         break;
29514                     case "northwest":
29515                         x -= sw - w;
29516                         y -= sh - h;
29517                     break;
29518                 }
29519                 w = sw;
29520                 h = sh;
29521             }
29522
29523             if(this.preserveRatio){
29524                 switch(pos){
29525                     case "southeast":
29526                     case "east":
29527                         h = oh * (w/ow);
29528                         h = Math.min(Math.max(mh, h), mxh);
29529                         w = ow * (h/oh);
29530                        break;
29531                     case "south":
29532                         w = ow * (h/oh);
29533                         w = Math.min(Math.max(mw, w), mxw);
29534                         h = oh * (w/ow);
29535                         break;
29536                     case "northeast":
29537                         w = ow * (h/oh);
29538                         w = Math.min(Math.max(mw, w), mxw);
29539                         h = oh * (w/ow);
29540                     break;
29541                     case "north":
29542                         var tw = w;
29543                         w = ow * (h/oh);
29544                         w = Math.min(Math.max(mw, w), mxw);
29545                         h = oh * (w/ow);
29546                         x += (tw - w) / 2;
29547                         break;
29548                     case "southwest":
29549                         h = oh * (w/ow);
29550                         h = Math.min(Math.max(mh, h), mxh);
29551                         var tw = w;
29552                         w = ow * (h/oh);
29553                         x += tw - w;
29554                         break;
29555                     case "west":
29556                         var th = h;
29557                         h = oh * (w/ow);
29558                         h = Math.min(Math.max(mh, h), mxh);
29559                         y += (th - h) / 2;
29560                         var tw = w;
29561                         w = ow * (h/oh);
29562                         x += tw - w;
29563                        break;
29564                     case "northwest":
29565                         var tw = w;
29566                         var th = h;
29567                         h = oh * (w/ow);
29568                         h = Math.min(Math.max(mh, h), mxh);
29569                         w = ow * (h/oh);
29570                         y += th - h;
29571                         x += tw - w;
29572                        break;
29573
29574                 }
29575             }
29576             if (pos == 'hdrag') {
29577                 w = ow;
29578             }
29579             this.proxy.setBounds(x, y, w, h);
29580             if(this.dynamic){
29581                 this.resizeElement();
29582             }
29583             }catch(e){}
29584         }
29585         this.fireEvent("resizing", this, x, y, w, h, e);
29586     },
29587
29588     // private
29589     handleOver : function(){
29590         if(this.enabled){
29591             this.el.addClass("x-resizable-over");
29592         }
29593     },
29594
29595     // private
29596     handleOut : function(){
29597         if(!this.resizing){
29598             this.el.removeClass("x-resizable-over");
29599         }
29600     },
29601
29602     /**
29603      * Returns the element this component is bound to.
29604      * @return {Roo.Element}
29605      */
29606     getEl : function(){
29607         return this.el;
29608     },
29609
29610     /**
29611      * Returns the resizeChild element (or null).
29612      * @return {Roo.Element}
29613      */
29614     getResizeChild : function(){
29615         return this.resizeChild;
29616     },
29617     groupHandler : function()
29618     {
29619         
29620     },
29621     /**
29622      * Destroys this resizable. If the element was wrapped and
29623      * removeEl is not true then the element remains.
29624      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29625      */
29626     destroy : function(removeEl){
29627         this.proxy.remove();
29628         if(this.overlay){
29629             this.overlay.removeAllListeners();
29630             this.overlay.remove();
29631         }
29632         var ps = Roo.Resizable.positions;
29633         for(var k in ps){
29634             if(typeof ps[k] != "function" && this[ps[k]]){
29635                 var h = this[ps[k]];
29636                 h.el.removeAllListeners();
29637                 h.el.remove();
29638             }
29639         }
29640         if(removeEl){
29641             this.el.update("");
29642             this.el.remove();
29643         }
29644     }
29645 });
29646
29647 // private
29648 // hash to map config positions to true positions
29649 Roo.Resizable.positions = {
29650     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29651     hd: "hdrag"
29652 };
29653
29654 // private
29655 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29656     if(!this.tpl){
29657         // only initialize the template if resizable is used
29658         var tpl = Roo.DomHelper.createTemplate(
29659             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29660         );
29661         tpl.compile();
29662         Roo.Resizable.Handle.prototype.tpl = tpl;
29663     }
29664     this.position = pos;
29665     this.rz = rz;
29666     // show north drag fro topdra
29667     var handlepos = pos == 'hdrag' ? 'north' : pos;
29668     
29669     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29670     if (pos == 'hdrag') {
29671         this.el.setStyle('cursor', 'pointer');
29672     }
29673     this.el.unselectable();
29674     if(transparent){
29675         this.el.setOpacity(0);
29676     }
29677     this.el.on("mousedown", this.onMouseDown, this);
29678     if(!disableTrackOver){
29679         this.el.on("mouseover", this.onMouseOver, this);
29680         this.el.on("mouseout", this.onMouseOut, this);
29681     }
29682 };
29683
29684 // private
29685 Roo.Resizable.Handle.prototype = {
29686     afterResize : function(rz){
29687         Roo.log('after?');
29688         // do nothing
29689     },
29690     // private
29691     onMouseDown : function(e){
29692         this.rz.onMouseDown(this, e);
29693     },
29694     // private
29695     onMouseOver : function(e){
29696         this.rz.handleOver(this, e);
29697     },
29698     // private
29699     onMouseOut : function(e){
29700         this.rz.handleOut(this, e);
29701     }
29702 };/*
29703  * Based on:
29704  * Ext JS Library 1.1.1
29705  * Copyright(c) 2006-2007, Ext JS, LLC.
29706  *
29707  * Originally Released Under LGPL - original licence link has changed is not relivant.
29708  *
29709  * Fork - LGPL
29710  * <script type="text/javascript">
29711  */
29712
29713 /**
29714  * @class Roo.Editor
29715  * @extends Roo.Component
29716  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29717  * @constructor
29718  * Create a new Editor
29719  * @param {Roo.form.Field} field The Field object (or descendant)
29720  * @param {Object} config The config object
29721  */
29722 Roo.Editor = function(field, config){
29723     Roo.Editor.superclass.constructor.call(this, config);
29724     this.field = field;
29725     this.addEvents({
29726         /**
29727              * @event beforestartedit
29728              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29729              * false from the handler of this event.
29730              * @param {Editor} this
29731              * @param {Roo.Element} boundEl The underlying element bound to this editor
29732              * @param {Mixed} value The field value being set
29733              */
29734         "beforestartedit" : true,
29735         /**
29736              * @event startedit
29737              * Fires when this editor is displayed
29738              * @param {Roo.Element} boundEl The underlying element bound to this editor
29739              * @param {Mixed} value The starting field value
29740              */
29741         "startedit" : true,
29742         /**
29743              * @event beforecomplete
29744              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29745              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29746              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29747              * event will not fire since no edit actually occurred.
29748              * @param {Editor} this
29749              * @param {Mixed} value The current field value
29750              * @param {Mixed} startValue The original field value
29751              */
29752         "beforecomplete" : true,
29753         /**
29754              * @event complete
29755              * Fires after editing is complete and any changed value has been written to the underlying field.
29756              * @param {Editor} this
29757              * @param {Mixed} value The current field value
29758              * @param {Mixed} startValue The original field value
29759              */
29760         "complete" : true,
29761         /**
29762          * @event specialkey
29763          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29764          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29765          * @param {Roo.form.Field} this
29766          * @param {Roo.EventObject} e The event object
29767          */
29768         "specialkey" : true
29769     });
29770 };
29771
29772 Roo.extend(Roo.Editor, Roo.Component, {
29773     /**
29774      * @cfg {Boolean/String} autosize
29775      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29776      * or "height" to adopt the height only (defaults to false)
29777      */
29778     /**
29779      * @cfg {Boolean} revertInvalid
29780      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29781      * validation fails (defaults to true)
29782      */
29783     /**
29784      * @cfg {Boolean} ignoreNoChange
29785      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29786      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29787      * will never be ignored.
29788      */
29789     /**
29790      * @cfg {Boolean} hideEl
29791      * False to keep the bound element visible while the editor is displayed (defaults to true)
29792      */
29793     /**
29794      * @cfg {Mixed} value
29795      * The data value of the underlying field (defaults to "")
29796      */
29797     value : "",
29798     /**
29799      * @cfg {String} alignment
29800      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29801      */
29802     alignment: "c-c?",
29803     /**
29804      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29805      * for bottom-right shadow (defaults to "frame")
29806      */
29807     shadow : "frame",
29808     /**
29809      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29810      */
29811     constrain : false,
29812     /**
29813      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29814      */
29815     completeOnEnter : false,
29816     /**
29817      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29818      */
29819     cancelOnEsc : false,
29820     /**
29821      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29822      */
29823     updateEl : false,
29824
29825     // private
29826     onRender : function(ct, position){
29827         this.el = new Roo.Layer({
29828             shadow: this.shadow,
29829             cls: "x-editor",
29830             parentEl : ct,
29831             shim : this.shim,
29832             shadowOffset:4,
29833             id: this.id,
29834             constrain: this.constrain
29835         });
29836         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29837         if(this.field.msgTarget != 'title'){
29838             this.field.msgTarget = 'qtip';
29839         }
29840         this.field.render(this.el);
29841         if(Roo.isGecko){
29842             this.field.el.dom.setAttribute('autocomplete', 'off');
29843         }
29844         this.field.on("specialkey", this.onSpecialKey, this);
29845         if(this.swallowKeys){
29846             this.field.el.swallowEvent(['keydown','keypress']);
29847         }
29848         this.field.show();
29849         this.field.on("blur", this.onBlur, this);
29850         if(this.field.grow){
29851             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29852         }
29853     },
29854
29855     onSpecialKey : function(field, e)
29856     {
29857         //Roo.log('editor onSpecialKey');
29858         if(this.completeOnEnter && e.getKey() == e.ENTER){
29859             e.stopEvent();
29860             this.completeEdit();
29861             return;
29862         }
29863         // do not fire special key otherwise it might hide close the editor...
29864         if(e.getKey() == e.ENTER){    
29865             return;
29866         }
29867         if(this.cancelOnEsc && e.getKey() == e.ESC){
29868             this.cancelEdit();
29869             return;
29870         } 
29871         this.fireEvent('specialkey', field, e);
29872     
29873     },
29874
29875     /**
29876      * Starts the editing process and shows the editor.
29877      * @param {String/HTMLElement/Element} el The element to edit
29878      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29879       * to the innerHTML of el.
29880      */
29881     startEdit : function(el, value){
29882         if(this.editing){
29883             this.completeEdit();
29884         }
29885         this.boundEl = Roo.get(el);
29886         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29887         if(!this.rendered){
29888             this.render(this.parentEl || document.body);
29889         }
29890         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29891             return;
29892         }
29893         this.startValue = v;
29894         this.field.setValue(v);
29895         if(this.autoSize){
29896             var sz = this.boundEl.getSize();
29897             switch(this.autoSize){
29898                 case "width":
29899                 this.setSize(sz.width,  "");
29900                 break;
29901                 case "height":
29902                 this.setSize("",  sz.height);
29903                 break;
29904                 default:
29905                 this.setSize(sz.width,  sz.height);
29906             }
29907         }
29908         this.el.alignTo(this.boundEl, this.alignment);
29909         this.editing = true;
29910         if(Roo.QuickTips){
29911             Roo.QuickTips.disable();
29912         }
29913         this.show();
29914     },
29915
29916     /**
29917      * Sets the height and width of this editor.
29918      * @param {Number} width The new width
29919      * @param {Number} height The new height
29920      */
29921     setSize : function(w, h){
29922         this.field.setSize(w, h);
29923         if(this.el){
29924             this.el.sync();
29925         }
29926     },
29927
29928     /**
29929      * Realigns the editor to the bound field based on the current alignment config value.
29930      */
29931     realign : function(){
29932         this.el.alignTo(this.boundEl, this.alignment);
29933     },
29934
29935     /**
29936      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29937      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29938      */
29939     completeEdit : function(remainVisible){
29940         if(!this.editing){
29941             return;
29942         }
29943         var v = this.getValue();
29944         if(this.revertInvalid !== false && !this.field.isValid()){
29945             v = this.startValue;
29946             this.cancelEdit(true);
29947         }
29948         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29949             this.editing = false;
29950             this.hide();
29951             return;
29952         }
29953         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29954             this.editing = false;
29955             if(this.updateEl && this.boundEl){
29956                 this.boundEl.update(v);
29957             }
29958             if(remainVisible !== true){
29959                 this.hide();
29960             }
29961             this.fireEvent("complete", this, v, this.startValue);
29962         }
29963     },
29964
29965     // private
29966     onShow : function(){
29967         this.el.show();
29968         if(this.hideEl !== false){
29969             this.boundEl.hide();
29970         }
29971         this.field.show();
29972         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29973             this.fixIEFocus = true;
29974             this.deferredFocus.defer(50, this);
29975         }else{
29976             this.field.focus();
29977         }
29978         this.fireEvent("startedit", this.boundEl, this.startValue);
29979     },
29980
29981     deferredFocus : function(){
29982         if(this.editing){
29983             this.field.focus();
29984         }
29985     },
29986
29987     /**
29988      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29989      * reverted to the original starting value.
29990      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29991      * cancel (defaults to false)
29992      */
29993     cancelEdit : function(remainVisible){
29994         if(this.editing){
29995             this.setValue(this.startValue);
29996             if(remainVisible !== true){
29997                 this.hide();
29998             }
29999         }
30000     },
30001
30002     // private
30003     onBlur : function(){
30004         if(this.allowBlur !== true && this.editing){
30005             this.completeEdit();
30006         }
30007     },
30008
30009     // private
30010     onHide : function(){
30011         if(this.editing){
30012             this.completeEdit();
30013             return;
30014         }
30015         this.field.blur();
30016         if(this.field.collapse){
30017             this.field.collapse();
30018         }
30019         this.el.hide();
30020         if(this.hideEl !== false){
30021             this.boundEl.show();
30022         }
30023         if(Roo.QuickTips){
30024             Roo.QuickTips.enable();
30025         }
30026     },
30027
30028     /**
30029      * Sets the data value of the editor
30030      * @param {Mixed} value Any valid value supported by the underlying field
30031      */
30032     setValue : function(v){
30033         this.field.setValue(v);
30034     },
30035
30036     /**
30037      * Gets the data value of the editor
30038      * @return {Mixed} The data value
30039      */
30040     getValue : function(){
30041         return this.field.getValue();
30042     }
30043 });/*
30044  * Based on:
30045  * Ext JS Library 1.1.1
30046  * Copyright(c) 2006-2007, Ext JS, LLC.
30047  *
30048  * Originally Released Under LGPL - original licence link has changed is not relivant.
30049  *
30050  * Fork - LGPL
30051  * <script type="text/javascript">
30052  */
30053  
30054 /**
30055  * @class Roo.BasicDialog
30056  * @extends Roo.util.Observable
30057  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30058  * <pre><code>
30059 var dlg = new Roo.BasicDialog("my-dlg", {
30060     height: 200,
30061     width: 300,
30062     minHeight: 100,
30063     minWidth: 150,
30064     modal: true,
30065     proxyDrag: true,
30066     shadow: true
30067 });
30068 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30069 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30070 dlg.addButton('Cancel', dlg.hide, dlg);
30071 dlg.show();
30072 </code></pre>
30073   <b>A Dialog should always be a direct child of the body element.</b>
30074  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30075  * @cfg {String} title Default text to display in the title bar (defaults to null)
30076  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30077  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30078  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30079  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30080  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30081  * (defaults to null with no animation)
30082  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30083  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30084  * property for valid values (defaults to 'all')
30085  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30086  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30087  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30088  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30089  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30090  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30091  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30092  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30093  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30094  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30095  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30096  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30097  * draggable = true (defaults to false)
30098  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30099  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30100  * shadow (defaults to false)
30101  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30102  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30103  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30104  * @cfg {Array} buttons Array of buttons
30105  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30106  * @constructor
30107  * Create a new BasicDialog.
30108  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30109  * @param {Object} config Configuration options
30110  */
30111 Roo.BasicDialog = function(el, config){
30112     this.el = Roo.get(el);
30113     var dh = Roo.DomHelper;
30114     if(!this.el && config && config.autoCreate){
30115         if(typeof config.autoCreate == "object"){
30116             if(!config.autoCreate.id){
30117                 config.autoCreate.id = el;
30118             }
30119             this.el = dh.append(document.body,
30120                         config.autoCreate, true);
30121         }else{
30122             this.el = dh.append(document.body,
30123                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30124         }
30125     }
30126     el = this.el;
30127     el.setDisplayed(true);
30128     el.hide = this.hideAction;
30129     this.id = el.id;
30130     el.addClass("x-dlg");
30131
30132     Roo.apply(this, config);
30133
30134     this.proxy = el.createProxy("x-dlg-proxy");
30135     this.proxy.hide = this.hideAction;
30136     this.proxy.setOpacity(.5);
30137     this.proxy.hide();
30138
30139     if(config.width){
30140         el.setWidth(config.width);
30141     }
30142     if(config.height){
30143         el.setHeight(config.height);
30144     }
30145     this.size = el.getSize();
30146     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30147         this.xy = [config.x,config.y];
30148     }else{
30149         this.xy = el.getCenterXY(true);
30150     }
30151     /** The header element @type Roo.Element */
30152     this.header = el.child("> .x-dlg-hd");
30153     /** The body element @type Roo.Element */
30154     this.body = el.child("> .x-dlg-bd");
30155     /** The footer element @type Roo.Element */
30156     this.footer = el.child("> .x-dlg-ft");
30157
30158     if(!this.header){
30159         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30160     }
30161     if(!this.body){
30162         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30163     }
30164
30165     this.header.unselectable();
30166     if(this.title){
30167         this.header.update(this.title);
30168     }
30169     // this element allows the dialog to be focused for keyboard event
30170     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30171     this.focusEl.swallowEvent("click", true);
30172
30173     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30174
30175     // wrap the body and footer for special rendering
30176     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30177     if(this.footer){
30178         this.bwrap.dom.appendChild(this.footer.dom);
30179     }
30180
30181     this.bg = this.el.createChild({
30182         tag: "div", cls:"x-dlg-bg",
30183         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30184     });
30185     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30186
30187
30188     if(this.autoScroll !== false && !this.autoTabs){
30189         this.body.setStyle("overflow", "auto");
30190     }
30191
30192     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30193
30194     if(this.closable !== false){
30195         this.el.addClass("x-dlg-closable");
30196         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30197         this.close.on("click", this.closeClick, this);
30198         this.close.addClassOnOver("x-dlg-close-over");
30199     }
30200     if(this.collapsible !== false){
30201         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30202         this.collapseBtn.on("click", this.collapseClick, this);
30203         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30204         this.header.on("dblclick", this.collapseClick, this);
30205     }
30206     if(this.resizable !== false){
30207         this.el.addClass("x-dlg-resizable");
30208         this.resizer = new Roo.Resizable(el, {
30209             minWidth: this.minWidth || 80,
30210             minHeight:this.minHeight || 80,
30211             handles: this.resizeHandles || "all",
30212             pinned: true
30213         });
30214         this.resizer.on("beforeresize", this.beforeResize, this);
30215         this.resizer.on("resize", this.onResize, this);
30216     }
30217     if(this.draggable !== false){
30218         el.addClass("x-dlg-draggable");
30219         if (!this.proxyDrag) {
30220             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30221         }
30222         else {
30223             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30224         }
30225         dd.setHandleElId(this.header.id);
30226         dd.endDrag = this.endMove.createDelegate(this);
30227         dd.startDrag = this.startMove.createDelegate(this);
30228         dd.onDrag = this.onDrag.createDelegate(this);
30229         dd.scroll = false;
30230         this.dd = dd;
30231     }
30232     if(this.modal){
30233         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30234         this.mask.enableDisplayMode("block");
30235         this.mask.hide();
30236         this.el.addClass("x-dlg-modal");
30237     }
30238     if(this.shadow){
30239         this.shadow = new Roo.Shadow({
30240             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30241             offset : this.shadowOffset
30242         });
30243     }else{
30244         this.shadowOffset = 0;
30245     }
30246     if(Roo.useShims && this.shim !== false){
30247         this.shim = this.el.createShim();
30248         this.shim.hide = this.hideAction;
30249         this.shim.hide();
30250     }else{
30251         this.shim = false;
30252     }
30253     if(this.autoTabs){
30254         this.initTabs();
30255     }
30256     if (this.buttons) { 
30257         var bts= this.buttons;
30258         this.buttons = [];
30259         Roo.each(bts, function(b) {
30260             this.addButton(b);
30261         }, this);
30262     }
30263     
30264     
30265     this.addEvents({
30266         /**
30267          * @event keydown
30268          * Fires when a key is pressed
30269          * @param {Roo.BasicDialog} this
30270          * @param {Roo.EventObject} e
30271          */
30272         "keydown" : true,
30273         /**
30274          * @event move
30275          * Fires when this dialog is moved by the user.
30276          * @param {Roo.BasicDialog} this
30277          * @param {Number} x The new page X
30278          * @param {Number} y The new page Y
30279          */
30280         "move" : true,
30281         /**
30282          * @event resize
30283          * Fires when this dialog is resized by the user.
30284          * @param {Roo.BasicDialog} this
30285          * @param {Number} width The new width
30286          * @param {Number} height The new height
30287          */
30288         "resize" : true,
30289         /**
30290          * @event beforehide
30291          * Fires before this dialog is hidden.
30292          * @param {Roo.BasicDialog} this
30293          */
30294         "beforehide" : true,
30295         /**
30296          * @event hide
30297          * Fires when this dialog is hidden.
30298          * @param {Roo.BasicDialog} this
30299          */
30300         "hide" : true,
30301         /**
30302          * @event beforeshow
30303          * Fires before this dialog is shown.
30304          * @param {Roo.BasicDialog} this
30305          */
30306         "beforeshow" : true,
30307         /**
30308          * @event show
30309          * Fires when this dialog is shown.
30310          * @param {Roo.BasicDialog} this
30311          */
30312         "show" : true
30313     });
30314     el.on("keydown", this.onKeyDown, this);
30315     el.on("mousedown", this.toFront, this);
30316     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30317     this.el.hide();
30318     Roo.DialogManager.register(this);
30319     Roo.BasicDialog.superclass.constructor.call(this);
30320 };
30321
30322 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30323     shadowOffset: Roo.isIE ? 6 : 5,
30324     minHeight: 80,
30325     minWidth: 200,
30326     minButtonWidth: 75,
30327     defaultButton: null,
30328     buttonAlign: "right",
30329     tabTag: 'div',
30330     firstShow: true,
30331
30332     /**
30333      * Sets the dialog title text
30334      * @param {String} text The title text to display
30335      * @return {Roo.BasicDialog} this
30336      */
30337     setTitle : function(text){
30338         this.header.update(text);
30339         return this;
30340     },
30341
30342     // private
30343     closeClick : function(){
30344         this.hide();
30345     },
30346
30347     // private
30348     collapseClick : function(){
30349         this[this.collapsed ? "expand" : "collapse"]();
30350     },
30351
30352     /**
30353      * Collapses the dialog to its minimized state (only the title bar is visible).
30354      * Equivalent to the user clicking the collapse dialog button.
30355      */
30356     collapse : function(){
30357         if(!this.collapsed){
30358             this.collapsed = true;
30359             this.el.addClass("x-dlg-collapsed");
30360             this.restoreHeight = this.el.getHeight();
30361             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30362         }
30363     },
30364
30365     /**
30366      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30367      * clicking the expand dialog button.
30368      */
30369     expand : function(){
30370         if(this.collapsed){
30371             this.collapsed = false;
30372             this.el.removeClass("x-dlg-collapsed");
30373             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30374         }
30375     },
30376
30377     /**
30378      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30379      * @return {Roo.TabPanel} The tabs component
30380      */
30381     initTabs : function(){
30382         var tabs = this.getTabs();
30383         while(tabs.getTab(0)){
30384             tabs.removeTab(0);
30385         }
30386         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30387             var dom = el.dom;
30388             tabs.addTab(Roo.id(dom), dom.title);
30389             dom.title = "";
30390         });
30391         tabs.activate(0);
30392         return tabs;
30393     },
30394
30395     // private
30396     beforeResize : function(){
30397         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30398     },
30399
30400     // private
30401     onResize : function(){
30402         this.refreshSize();
30403         this.syncBodyHeight();
30404         this.adjustAssets();
30405         this.focus();
30406         this.fireEvent("resize", this, this.size.width, this.size.height);
30407     },
30408
30409     // private
30410     onKeyDown : function(e){
30411         if(this.isVisible()){
30412             this.fireEvent("keydown", this, e);
30413         }
30414     },
30415
30416     /**
30417      * Resizes the dialog.
30418      * @param {Number} width
30419      * @param {Number} height
30420      * @return {Roo.BasicDialog} this
30421      */
30422     resizeTo : function(width, height){
30423         this.el.setSize(width, height);
30424         this.size = {width: width, height: height};
30425         this.syncBodyHeight();
30426         if(this.fixedcenter){
30427             this.center();
30428         }
30429         if(this.isVisible()){
30430             this.constrainXY();
30431             this.adjustAssets();
30432         }
30433         this.fireEvent("resize", this, width, height);
30434         return this;
30435     },
30436
30437
30438     /**
30439      * Resizes the dialog to fit the specified content size.
30440      * @param {Number} width
30441      * @param {Number} height
30442      * @return {Roo.BasicDialog} this
30443      */
30444     setContentSize : function(w, h){
30445         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30446         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30447         //if(!this.el.isBorderBox()){
30448             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30449             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30450         //}
30451         if(this.tabs){
30452             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30453             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30454         }
30455         this.resizeTo(w, h);
30456         return this;
30457     },
30458
30459     /**
30460      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30461      * executed in response to a particular key being pressed while the dialog is active.
30462      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30463      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30464      * @param {Function} fn The function to call
30465      * @param {Object} scope (optional) The scope of the function
30466      * @return {Roo.BasicDialog} this
30467      */
30468     addKeyListener : function(key, fn, scope){
30469         var keyCode, shift, ctrl, alt;
30470         if(typeof key == "object" && !(key instanceof Array)){
30471             keyCode = key["key"];
30472             shift = key["shift"];
30473             ctrl = key["ctrl"];
30474             alt = key["alt"];
30475         }else{
30476             keyCode = key;
30477         }
30478         var handler = function(dlg, e){
30479             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30480                 var k = e.getKey();
30481                 if(keyCode instanceof Array){
30482                     for(var i = 0, len = keyCode.length; i < len; i++){
30483                         if(keyCode[i] == k){
30484                           fn.call(scope || window, dlg, k, e);
30485                           return;
30486                         }
30487                     }
30488                 }else{
30489                     if(k == keyCode){
30490                         fn.call(scope || window, dlg, k, e);
30491                     }
30492                 }
30493             }
30494         };
30495         this.on("keydown", handler);
30496         return this;
30497     },
30498
30499     /**
30500      * Returns the TabPanel component (creates it if it doesn't exist).
30501      * Note: If you wish to simply check for the existence of tabs without creating them,
30502      * check for a null 'tabs' property.
30503      * @return {Roo.TabPanel} The tabs component
30504      */
30505     getTabs : function(){
30506         if(!this.tabs){
30507             this.el.addClass("x-dlg-auto-tabs");
30508             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30509             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30510         }
30511         return this.tabs;
30512     },
30513
30514     /**
30515      * Adds a button to the footer section of the dialog.
30516      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30517      * object or a valid Roo.DomHelper element config
30518      * @param {Function} handler The function called when the button is clicked
30519      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30520      * @return {Roo.Button} The new button
30521      */
30522     addButton : function(config, handler, scope){
30523         var dh = Roo.DomHelper;
30524         if(!this.footer){
30525             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30526         }
30527         if(!this.btnContainer){
30528             var tb = this.footer.createChild({
30529
30530                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30531                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30532             }, null, true);
30533             this.btnContainer = tb.firstChild.firstChild.firstChild;
30534         }
30535         var bconfig = {
30536             handler: handler,
30537             scope: scope,
30538             minWidth: this.minButtonWidth,
30539             hideParent:true
30540         };
30541         if(typeof config == "string"){
30542             bconfig.text = config;
30543         }else{
30544             if(config.tag){
30545                 bconfig.dhconfig = config;
30546             }else{
30547                 Roo.apply(bconfig, config);
30548             }
30549         }
30550         var fc = false;
30551         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30552             bconfig.position = Math.max(0, bconfig.position);
30553             fc = this.btnContainer.childNodes[bconfig.position];
30554         }
30555          
30556         var btn = new Roo.Button(
30557             fc ? 
30558                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30559                 : this.btnContainer.appendChild(document.createElement("td")),
30560             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30561             bconfig
30562         );
30563         this.syncBodyHeight();
30564         if(!this.buttons){
30565             /**
30566              * Array of all the buttons that have been added to this dialog via addButton
30567              * @type Array
30568              */
30569             this.buttons = [];
30570         }
30571         this.buttons.push(btn);
30572         return btn;
30573     },
30574
30575     /**
30576      * Sets the default button to be focused when the dialog is displayed.
30577      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30578      * @return {Roo.BasicDialog} this
30579      */
30580     setDefaultButton : function(btn){
30581         this.defaultButton = btn;
30582         return this;
30583     },
30584
30585     // private
30586     getHeaderFooterHeight : function(safe){
30587         var height = 0;
30588         if(this.header){
30589            height += this.header.getHeight();
30590         }
30591         if(this.footer){
30592            var fm = this.footer.getMargins();
30593             height += (this.footer.getHeight()+fm.top+fm.bottom);
30594         }
30595         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30596         height += this.centerBg.getPadding("tb");
30597         return height;
30598     },
30599
30600     // private
30601     syncBodyHeight : function()
30602     {
30603         var bd = this.body, // the text
30604             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30605             bw = this.bwrap;
30606         var height = this.size.height - this.getHeaderFooterHeight(false);
30607         bd.setHeight(height-bd.getMargins("tb"));
30608         var hh = this.header.getHeight();
30609         var h = this.size.height-hh;
30610         cb.setHeight(h);
30611         
30612         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30613         bw.setHeight(h-cb.getPadding("tb"));
30614         
30615         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30616         bd.setWidth(bw.getWidth(true));
30617         if(this.tabs){
30618             this.tabs.syncHeight();
30619             if(Roo.isIE){
30620                 this.tabs.el.repaint();
30621             }
30622         }
30623     },
30624
30625     /**
30626      * Restores the previous state of the dialog if Roo.state is configured.
30627      * @return {Roo.BasicDialog} this
30628      */
30629     restoreState : function(){
30630         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30631         if(box && box.width){
30632             this.xy = [box.x, box.y];
30633             this.resizeTo(box.width, box.height);
30634         }
30635         return this;
30636     },
30637
30638     // private
30639     beforeShow : function(){
30640         this.expand();
30641         if(this.fixedcenter){
30642             this.xy = this.el.getCenterXY(true);
30643         }
30644         if(this.modal){
30645             Roo.get(document.body).addClass("x-body-masked");
30646             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30647             this.mask.show();
30648         }
30649         this.constrainXY();
30650     },
30651
30652     // private
30653     animShow : function(){
30654         var b = Roo.get(this.animateTarget).getBox();
30655         this.proxy.setSize(b.width, b.height);
30656         this.proxy.setLocation(b.x, b.y);
30657         this.proxy.show();
30658         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30659                     true, .35, this.showEl.createDelegate(this));
30660     },
30661
30662     /**
30663      * Shows the dialog.
30664      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30665      * @return {Roo.BasicDialog} this
30666      */
30667     show : function(animateTarget){
30668         if (this.fireEvent("beforeshow", this) === false){
30669             return;
30670         }
30671         if(this.syncHeightBeforeShow){
30672             this.syncBodyHeight();
30673         }else if(this.firstShow){
30674             this.firstShow = false;
30675             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30676         }
30677         this.animateTarget = animateTarget || this.animateTarget;
30678         if(!this.el.isVisible()){
30679             this.beforeShow();
30680             if(this.animateTarget && Roo.get(this.animateTarget)){
30681                 this.animShow();
30682             }else{
30683                 this.showEl();
30684             }
30685         }
30686         return this;
30687     },
30688
30689     // private
30690     showEl : function(){
30691         this.proxy.hide();
30692         this.el.setXY(this.xy);
30693         this.el.show();
30694         this.adjustAssets(true);
30695         this.toFront();
30696         this.focus();
30697         // IE peekaboo bug - fix found by Dave Fenwick
30698         if(Roo.isIE){
30699             this.el.repaint();
30700         }
30701         this.fireEvent("show", this);
30702     },
30703
30704     /**
30705      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30706      * dialog itself will receive focus.
30707      */
30708     focus : function(){
30709         if(this.defaultButton){
30710             this.defaultButton.focus();
30711         }else{
30712             this.focusEl.focus();
30713         }
30714     },
30715
30716     // private
30717     constrainXY : function(){
30718         if(this.constraintoviewport !== false){
30719             if(!this.viewSize){
30720                 if(this.container){
30721                     var s = this.container.getSize();
30722                     this.viewSize = [s.width, s.height];
30723                 }else{
30724                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30725                 }
30726             }
30727             var s = Roo.get(this.container||document).getScroll();
30728
30729             var x = this.xy[0], y = this.xy[1];
30730             var w = this.size.width, h = this.size.height;
30731             var vw = this.viewSize[0], vh = this.viewSize[1];
30732             // only move it if it needs it
30733             var moved = false;
30734             // first validate right/bottom
30735             if(x + w > vw+s.left){
30736                 x = vw - w;
30737                 moved = true;
30738             }
30739             if(y + h > vh+s.top){
30740                 y = vh - h;
30741                 moved = true;
30742             }
30743             // then make sure top/left isn't negative
30744             if(x < s.left){
30745                 x = s.left;
30746                 moved = true;
30747             }
30748             if(y < s.top){
30749                 y = s.top;
30750                 moved = true;
30751             }
30752             if(moved){
30753                 // cache xy
30754                 this.xy = [x, y];
30755                 if(this.isVisible()){
30756                     this.el.setLocation(x, y);
30757                     this.adjustAssets();
30758                 }
30759             }
30760         }
30761     },
30762
30763     // private
30764     onDrag : function(){
30765         if(!this.proxyDrag){
30766             this.xy = this.el.getXY();
30767             this.adjustAssets();
30768         }
30769     },
30770
30771     // private
30772     adjustAssets : function(doShow){
30773         var x = this.xy[0], y = this.xy[1];
30774         var w = this.size.width, h = this.size.height;
30775         if(doShow === true){
30776             if(this.shadow){
30777                 this.shadow.show(this.el);
30778             }
30779             if(this.shim){
30780                 this.shim.show();
30781             }
30782         }
30783         if(this.shadow && this.shadow.isVisible()){
30784             this.shadow.show(this.el);
30785         }
30786         if(this.shim && this.shim.isVisible()){
30787             this.shim.setBounds(x, y, w, h);
30788         }
30789     },
30790
30791     // private
30792     adjustViewport : function(w, h){
30793         if(!w || !h){
30794             w = Roo.lib.Dom.getViewWidth();
30795             h = Roo.lib.Dom.getViewHeight();
30796         }
30797         // cache the size
30798         this.viewSize = [w, h];
30799         if(this.modal && this.mask.isVisible()){
30800             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30801             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30802         }
30803         if(this.isVisible()){
30804             this.constrainXY();
30805         }
30806     },
30807
30808     /**
30809      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30810      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30811      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30812      */
30813     destroy : function(removeEl){
30814         if(this.isVisible()){
30815             this.animateTarget = null;
30816             this.hide();
30817         }
30818         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30819         if(this.tabs){
30820             this.tabs.destroy(removeEl);
30821         }
30822         Roo.destroy(
30823              this.shim,
30824              this.proxy,
30825              this.resizer,
30826              this.close,
30827              this.mask
30828         );
30829         if(this.dd){
30830             this.dd.unreg();
30831         }
30832         if(this.buttons){
30833            for(var i = 0, len = this.buttons.length; i < len; i++){
30834                this.buttons[i].destroy();
30835            }
30836         }
30837         this.el.removeAllListeners();
30838         if(removeEl === true){
30839             this.el.update("");
30840             this.el.remove();
30841         }
30842         Roo.DialogManager.unregister(this);
30843     },
30844
30845     // private
30846     startMove : function(){
30847         if(this.proxyDrag){
30848             this.proxy.show();
30849         }
30850         if(this.constraintoviewport !== false){
30851             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30852         }
30853     },
30854
30855     // private
30856     endMove : function(){
30857         if(!this.proxyDrag){
30858             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30859         }else{
30860             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30861             this.proxy.hide();
30862         }
30863         this.refreshSize();
30864         this.adjustAssets();
30865         this.focus();
30866         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30867     },
30868
30869     /**
30870      * Brings this dialog to the front of any other visible dialogs
30871      * @return {Roo.BasicDialog} this
30872      */
30873     toFront : function(){
30874         Roo.DialogManager.bringToFront(this);
30875         return this;
30876     },
30877
30878     /**
30879      * Sends this dialog to the back (under) of any other visible dialogs
30880      * @return {Roo.BasicDialog} this
30881      */
30882     toBack : function(){
30883         Roo.DialogManager.sendToBack(this);
30884         return this;
30885     },
30886
30887     /**
30888      * Centers this dialog in the viewport
30889      * @return {Roo.BasicDialog} this
30890      */
30891     center : function(){
30892         var xy = this.el.getCenterXY(true);
30893         this.moveTo(xy[0], xy[1]);
30894         return this;
30895     },
30896
30897     /**
30898      * Moves the dialog's top-left corner to the specified point
30899      * @param {Number} x
30900      * @param {Number} y
30901      * @return {Roo.BasicDialog} this
30902      */
30903     moveTo : function(x, y){
30904         this.xy = [x,y];
30905         if(this.isVisible()){
30906             this.el.setXY(this.xy);
30907             this.adjustAssets();
30908         }
30909         return this;
30910     },
30911
30912     /**
30913      * Aligns the dialog to the specified element
30914      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30915      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30916      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30917      * @return {Roo.BasicDialog} this
30918      */
30919     alignTo : function(element, position, offsets){
30920         this.xy = this.el.getAlignToXY(element, position, offsets);
30921         if(this.isVisible()){
30922             this.el.setXY(this.xy);
30923             this.adjustAssets();
30924         }
30925         return this;
30926     },
30927
30928     /**
30929      * Anchors an element to another element and realigns it when the window is resized.
30930      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30931      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30932      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30933      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30934      * is a number, it is used as the buffer delay (defaults to 50ms).
30935      * @return {Roo.BasicDialog} this
30936      */
30937     anchorTo : function(el, alignment, offsets, monitorScroll){
30938         var action = function(){
30939             this.alignTo(el, alignment, offsets);
30940         };
30941         Roo.EventManager.onWindowResize(action, this);
30942         var tm = typeof monitorScroll;
30943         if(tm != 'undefined'){
30944             Roo.EventManager.on(window, 'scroll', action, this,
30945                 {buffer: tm == 'number' ? monitorScroll : 50});
30946         }
30947         action.call(this);
30948         return this;
30949     },
30950
30951     /**
30952      * Returns true if the dialog is visible
30953      * @return {Boolean}
30954      */
30955     isVisible : function(){
30956         return this.el.isVisible();
30957     },
30958
30959     // private
30960     animHide : function(callback){
30961         var b = Roo.get(this.animateTarget).getBox();
30962         this.proxy.show();
30963         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30964         this.el.hide();
30965         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30966                     this.hideEl.createDelegate(this, [callback]));
30967     },
30968
30969     /**
30970      * Hides the dialog.
30971      * @param {Function} callback (optional) Function to call when the dialog is hidden
30972      * @return {Roo.BasicDialog} this
30973      */
30974     hide : function(callback){
30975         if (this.fireEvent("beforehide", this) === false){
30976             return;
30977         }
30978         if(this.shadow){
30979             this.shadow.hide();
30980         }
30981         if(this.shim) {
30982           this.shim.hide();
30983         }
30984         // sometimes animateTarget seems to get set.. causing problems...
30985         // this just double checks..
30986         if(this.animateTarget && Roo.get(this.animateTarget)) {
30987            this.animHide(callback);
30988         }else{
30989             this.el.hide();
30990             this.hideEl(callback);
30991         }
30992         return this;
30993     },
30994
30995     // private
30996     hideEl : function(callback){
30997         this.proxy.hide();
30998         if(this.modal){
30999             this.mask.hide();
31000             Roo.get(document.body).removeClass("x-body-masked");
31001         }
31002         this.fireEvent("hide", this);
31003         if(typeof callback == "function"){
31004             callback();
31005         }
31006     },
31007
31008     // private
31009     hideAction : function(){
31010         this.setLeft("-10000px");
31011         this.setTop("-10000px");
31012         this.setStyle("visibility", "hidden");
31013     },
31014
31015     // private
31016     refreshSize : function(){
31017         this.size = this.el.getSize();
31018         this.xy = this.el.getXY();
31019         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31020     },
31021
31022     // private
31023     // z-index is managed by the DialogManager and may be overwritten at any time
31024     setZIndex : function(index){
31025         if(this.modal){
31026             this.mask.setStyle("z-index", index);
31027         }
31028         if(this.shim){
31029             this.shim.setStyle("z-index", ++index);
31030         }
31031         if(this.shadow){
31032             this.shadow.setZIndex(++index);
31033         }
31034         this.el.setStyle("z-index", ++index);
31035         if(this.proxy){
31036             this.proxy.setStyle("z-index", ++index);
31037         }
31038         if(this.resizer){
31039             this.resizer.proxy.setStyle("z-index", ++index);
31040         }
31041
31042         this.lastZIndex = index;
31043     },
31044
31045     /**
31046      * Returns the element for this dialog
31047      * @return {Roo.Element} The underlying dialog Element
31048      */
31049     getEl : function(){
31050         return this.el;
31051     }
31052 });
31053
31054 /**
31055  * @class Roo.DialogManager
31056  * Provides global access to BasicDialogs that have been created and
31057  * support for z-indexing (layering) multiple open dialogs.
31058  */
31059 Roo.DialogManager = function(){
31060     var list = {};
31061     var accessList = [];
31062     var front = null;
31063
31064     // private
31065     var sortDialogs = function(d1, d2){
31066         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31067     };
31068
31069     // private
31070     var orderDialogs = function(){
31071         accessList.sort(sortDialogs);
31072         var seed = Roo.DialogManager.zseed;
31073         for(var i = 0, len = accessList.length; i < len; i++){
31074             var dlg = accessList[i];
31075             if(dlg){
31076                 dlg.setZIndex(seed + (i*10));
31077             }
31078         }
31079     };
31080
31081     return {
31082         /**
31083          * The starting z-index for BasicDialogs (defaults to 9000)
31084          * @type Number The z-index value
31085          */
31086         zseed : 9000,
31087
31088         // private
31089         register : function(dlg){
31090             list[dlg.id] = dlg;
31091             accessList.push(dlg);
31092         },
31093
31094         // private
31095         unregister : function(dlg){
31096             delete list[dlg.id];
31097             var i=0;
31098             var len=0;
31099             if(!accessList.indexOf){
31100                 for(  i = 0, len = accessList.length; i < len; i++){
31101                     if(accessList[i] == dlg){
31102                         accessList.splice(i, 1);
31103                         return;
31104                     }
31105                 }
31106             }else{
31107                  i = accessList.indexOf(dlg);
31108                 if(i != -1){
31109                     accessList.splice(i, 1);
31110                 }
31111             }
31112         },
31113
31114         /**
31115          * Gets a registered dialog by id
31116          * @param {String/Object} id The id of the dialog or a dialog
31117          * @return {Roo.BasicDialog} this
31118          */
31119         get : function(id){
31120             return typeof id == "object" ? id : list[id];
31121         },
31122
31123         /**
31124          * Brings the specified dialog to the front
31125          * @param {String/Object} dlg The id of the dialog or a dialog
31126          * @return {Roo.BasicDialog} this
31127          */
31128         bringToFront : function(dlg){
31129             dlg = this.get(dlg);
31130             if(dlg != front){
31131                 front = dlg;
31132                 dlg._lastAccess = new Date().getTime();
31133                 orderDialogs();
31134             }
31135             return dlg;
31136         },
31137
31138         /**
31139          * Sends the specified dialog to the back
31140          * @param {String/Object} dlg The id of the dialog or a dialog
31141          * @return {Roo.BasicDialog} this
31142          */
31143         sendToBack : function(dlg){
31144             dlg = this.get(dlg);
31145             dlg._lastAccess = -(new Date().getTime());
31146             orderDialogs();
31147             return dlg;
31148         },
31149
31150         /**
31151          * Hides all dialogs
31152          */
31153         hideAll : function(){
31154             for(var id in list){
31155                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31156                     list[id].hide();
31157                 }
31158             }
31159         }
31160     };
31161 }();
31162
31163 /**
31164  * @class Roo.LayoutDialog
31165  * @extends Roo.BasicDialog
31166  * Dialog which provides adjustments for working with a layout in a Dialog.
31167  * Add your necessary layout config options to the dialog's config.<br>
31168  * Example usage (including a nested layout):
31169  * <pre><code>
31170 if(!dialog){
31171     dialog = new Roo.LayoutDialog("download-dlg", {
31172         modal: true,
31173         width:600,
31174         height:450,
31175         shadow:true,
31176         minWidth:500,
31177         minHeight:350,
31178         autoTabs:true,
31179         proxyDrag:true,
31180         // layout config merges with the dialog config
31181         center:{
31182             tabPosition: "top",
31183             alwaysShowTabs: true
31184         }
31185     });
31186     dialog.addKeyListener(27, dialog.hide, dialog);
31187     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31188     dialog.addButton("Build It!", this.getDownload, this);
31189
31190     // we can even add nested layouts
31191     var innerLayout = new Roo.BorderLayout("dl-inner", {
31192         east: {
31193             initialSize: 200,
31194             autoScroll:true,
31195             split:true
31196         },
31197         center: {
31198             autoScroll:true
31199         }
31200     });
31201     innerLayout.beginUpdate();
31202     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31203     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31204     innerLayout.endUpdate(true);
31205
31206     var layout = dialog.getLayout();
31207     layout.beginUpdate();
31208     layout.add("center", new Roo.ContentPanel("standard-panel",
31209                         {title: "Download the Source", fitToFrame:true}));
31210     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31211                {title: "Build your own roo.js"}));
31212     layout.getRegion("center").showPanel(sp);
31213     layout.endUpdate();
31214 }
31215 </code></pre>
31216     * @constructor
31217     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31218     * @param {Object} config configuration options
31219   */
31220 Roo.LayoutDialog = function(el, cfg){
31221     
31222     var config=  cfg;
31223     if (typeof(cfg) == 'undefined') {
31224         config = Roo.apply({}, el);
31225         // not sure why we use documentElement here.. - it should always be body.
31226         // IE7 borks horribly if we use documentElement.
31227         // webkit also does not like documentElement - it creates a body element...
31228         el = Roo.get( document.body || document.documentElement ).createChild();
31229         //config.autoCreate = true;
31230     }
31231     
31232     
31233     config.autoTabs = false;
31234     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31235     this.body.setStyle({overflow:"hidden", position:"relative"});
31236     this.layout = new Roo.BorderLayout(this.body.dom, config);
31237     this.layout.monitorWindowResize = false;
31238     this.el.addClass("x-dlg-auto-layout");
31239     // fix case when center region overwrites center function
31240     this.center = Roo.BasicDialog.prototype.center;
31241     this.on("show", this.layout.layout, this.layout, true);
31242     if (config.items) {
31243         var xitems = config.items;
31244         delete config.items;
31245         Roo.each(xitems, this.addxtype, this);
31246     }
31247     
31248     
31249 };
31250 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31251     /**
31252      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31253      * @deprecated
31254      */
31255     endUpdate : function(){
31256         this.layout.endUpdate();
31257     },
31258
31259     /**
31260      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31261      *  @deprecated
31262      */
31263     beginUpdate : function(){
31264         this.layout.beginUpdate();
31265     },
31266
31267     /**
31268      * Get the BorderLayout for this dialog
31269      * @return {Roo.BorderLayout}
31270      */
31271     getLayout : function(){
31272         return this.layout;
31273     },
31274
31275     showEl : function(){
31276         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31277         if(Roo.isIE7){
31278             this.layout.layout();
31279         }
31280     },
31281
31282     // private
31283     // Use the syncHeightBeforeShow config option to control this automatically
31284     syncBodyHeight : function(){
31285         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31286         if(this.layout){this.layout.layout();}
31287     },
31288     
31289       /**
31290      * Add an xtype element (actually adds to the layout.)
31291      * @return {Object} xdata xtype object data.
31292      */
31293     
31294     addxtype : function(c) {
31295         return this.layout.addxtype(c);
31296     }
31297 });/*
31298  * Based on:
31299  * Ext JS Library 1.1.1
31300  * Copyright(c) 2006-2007, Ext JS, LLC.
31301  *
31302  * Originally Released Under LGPL - original licence link has changed is not relivant.
31303  *
31304  * Fork - LGPL
31305  * <script type="text/javascript">
31306  */
31307  
31308 /**
31309  * @class Roo.MessageBox
31310  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31311  * Example usage:
31312  *<pre><code>
31313 // Basic alert:
31314 Roo.Msg.alert('Status', 'Changes saved successfully.');
31315
31316 // Prompt for user data:
31317 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31318     if (btn == 'ok'){
31319         // process text value...
31320     }
31321 });
31322
31323 // Show a dialog using config options:
31324 Roo.Msg.show({
31325    title:'Save Changes?',
31326    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31327    buttons: Roo.Msg.YESNOCANCEL,
31328    fn: processResult,
31329    animEl: 'elId'
31330 });
31331 </code></pre>
31332  * @singleton
31333  */
31334 Roo.MessageBox = function(){
31335     var dlg, opt, mask, waitTimer;
31336     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31337     var buttons, activeTextEl, bwidth;
31338
31339     // private
31340     var handleButton = function(button){
31341         dlg.hide();
31342         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31343     };
31344
31345     // private
31346     var handleHide = function(){
31347         if(opt && opt.cls){
31348             dlg.el.removeClass(opt.cls);
31349         }
31350         if(waitTimer){
31351             Roo.TaskMgr.stop(waitTimer);
31352             waitTimer = null;
31353         }
31354     };
31355
31356     // private
31357     var updateButtons = function(b){
31358         var width = 0;
31359         if(!b){
31360             buttons["ok"].hide();
31361             buttons["cancel"].hide();
31362             buttons["yes"].hide();
31363             buttons["no"].hide();
31364             dlg.footer.dom.style.display = 'none';
31365             return width;
31366         }
31367         dlg.footer.dom.style.display = '';
31368         for(var k in buttons){
31369             if(typeof buttons[k] != "function"){
31370                 if(b[k]){
31371                     buttons[k].show();
31372                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31373                     width += buttons[k].el.getWidth()+15;
31374                 }else{
31375                     buttons[k].hide();
31376                 }
31377             }
31378         }
31379         return width;
31380     };
31381
31382     // private
31383     var handleEsc = function(d, k, e){
31384         if(opt && opt.closable !== false){
31385             dlg.hide();
31386         }
31387         if(e){
31388             e.stopEvent();
31389         }
31390     };
31391
31392     return {
31393         /**
31394          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31395          * @return {Roo.BasicDialog} The BasicDialog element
31396          */
31397         getDialog : function(){
31398            if(!dlg){
31399                 dlg = new Roo.BasicDialog("x-msg-box", {
31400                     autoCreate : true,
31401                     shadow: true,
31402                     draggable: true,
31403                     resizable:false,
31404                     constraintoviewport:false,
31405                     fixedcenter:true,
31406                     collapsible : false,
31407                     shim:true,
31408                     modal: true,
31409                     width:400, height:100,
31410                     buttonAlign:"center",
31411                     closeClick : function(){
31412                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31413                             handleButton("no");
31414                         }else{
31415                             handleButton("cancel");
31416                         }
31417                     }
31418                 });
31419                 dlg.on("hide", handleHide);
31420                 mask = dlg.mask;
31421                 dlg.addKeyListener(27, handleEsc);
31422                 buttons = {};
31423                 var bt = this.buttonText;
31424                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31425                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31426                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31427                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31428                 bodyEl = dlg.body.createChild({
31429
31430                     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>'
31431                 });
31432                 msgEl = bodyEl.dom.firstChild;
31433                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31434                 textboxEl.enableDisplayMode();
31435                 textboxEl.addKeyListener([10,13], function(){
31436                     if(dlg.isVisible() && opt && opt.buttons){
31437                         if(opt.buttons.ok){
31438                             handleButton("ok");
31439                         }else if(opt.buttons.yes){
31440                             handleButton("yes");
31441                         }
31442                     }
31443                 });
31444                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31445                 textareaEl.enableDisplayMode();
31446                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31447                 progressEl.enableDisplayMode();
31448                 var pf = progressEl.dom.firstChild;
31449                 if (pf) {
31450                     pp = Roo.get(pf.firstChild);
31451                     pp.setHeight(pf.offsetHeight);
31452                 }
31453                 
31454             }
31455             return dlg;
31456         },
31457
31458         /**
31459          * Updates the message box body text
31460          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31461          * the XHTML-compliant non-breaking space character '&amp;#160;')
31462          * @return {Roo.MessageBox} This message box
31463          */
31464         updateText : function(text){
31465             if(!dlg.isVisible() && !opt.width){
31466                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31467             }
31468             msgEl.innerHTML = text || '&#160;';
31469       
31470             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31471             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31472             var w = Math.max(
31473                     Math.min(opt.width || cw , this.maxWidth), 
31474                     Math.max(opt.minWidth || this.minWidth, bwidth)
31475             );
31476             if(opt.prompt){
31477                 activeTextEl.setWidth(w);
31478             }
31479             if(dlg.isVisible()){
31480                 dlg.fixedcenter = false;
31481             }
31482             // to big, make it scroll. = But as usual stupid IE does not support
31483             // !important..
31484             
31485             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31486                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31487                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31488             } else {
31489                 bodyEl.dom.style.height = '';
31490                 bodyEl.dom.style.overflowY = '';
31491             }
31492             if (cw > w) {
31493                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31494             } else {
31495                 bodyEl.dom.style.overflowX = '';
31496             }
31497             
31498             dlg.setContentSize(w, bodyEl.getHeight());
31499             if(dlg.isVisible()){
31500                 dlg.fixedcenter = true;
31501             }
31502             return this;
31503         },
31504
31505         /**
31506          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31507          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31508          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31509          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31510          * @return {Roo.MessageBox} This message box
31511          */
31512         updateProgress : function(value, text){
31513             if(text){
31514                 this.updateText(text);
31515             }
31516             if (pp) { // weird bug on my firefox - for some reason this is not defined
31517                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31518             }
31519             return this;
31520         },        
31521
31522         /**
31523          * Returns true if the message box is currently displayed
31524          * @return {Boolean} True if the message box is visible, else false
31525          */
31526         isVisible : function(){
31527             return dlg && dlg.isVisible();  
31528         },
31529
31530         /**
31531          * Hides the message box if it is displayed
31532          */
31533         hide : function(){
31534             if(this.isVisible()){
31535                 dlg.hide();
31536             }  
31537         },
31538
31539         /**
31540          * Displays a new message box, or reinitializes an existing message box, based on the config options
31541          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31542          * The following config object properties are supported:
31543          * <pre>
31544 Property    Type             Description
31545 ----------  ---------------  ------------------------------------------------------------------------------------
31546 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31547                                    closes (defaults to undefined)
31548 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31549                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31550 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31551                                    progress and wait dialogs will ignore this property and always hide the
31552                                    close button as they can only be closed programmatically.
31553 cls               String           A custom CSS class to apply to the message box element
31554 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31555                                    displayed (defaults to 75)
31556 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31557                                    function will be btn (the name of the button that was clicked, if applicable,
31558                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31559                                    Progress and wait dialogs will ignore this option since they do not respond to
31560                                    user actions and can only be closed programmatically, so any required function
31561                                    should be called by the same code after it closes the dialog.
31562 icon              String           A CSS class that provides a background image to be used as an icon for
31563                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31564 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31565 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31566 modal             Boolean          False to allow user interaction with the page while the message box is
31567                                    displayed (defaults to true)
31568 msg               String           A string that will replace the existing message box body text (defaults
31569                                    to the XHTML-compliant non-breaking space character '&#160;')
31570 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31571 progress          Boolean          True to display a progress bar (defaults to false)
31572 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31573 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31574 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31575 title             String           The title text
31576 value             String           The string value to set into the active textbox element if displayed
31577 wait              Boolean          True to display a progress bar (defaults to false)
31578 width             Number           The width of the dialog in pixels
31579 </pre>
31580          *
31581          * Example usage:
31582          * <pre><code>
31583 Roo.Msg.show({
31584    title: 'Address',
31585    msg: 'Please enter your address:',
31586    width: 300,
31587    buttons: Roo.MessageBox.OKCANCEL,
31588    multiline: true,
31589    fn: saveAddress,
31590    animEl: 'addAddressBtn'
31591 });
31592 </code></pre>
31593          * @param {Object} config Configuration options
31594          * @return {Roo.MessageBox} This message box
31595          */
31596         show : function(options)
31597         {
31598             
31599             // this causes nightmares if you show one dialog after another
31600             // especially on callbacks..
31601              
31602             if(this.isVisible()){
31603                 
31604                 this.hide();
31605                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31606                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31607                 Roo.log("New Dialog Message:" +  options.msg )
31608                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31609                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31610                 
31611             }
31612             var d = this.getDialog();
31613             opt = options;
31614             d.setTitle(opt.title || "&#160;");
31615             d.close.setDisplayed(opt.closable !== false);
31616             activeTextEl = textboxEl;
31617             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31618             if(opt.prompt){
31619                 if(opt.multiline){
31620                     textboxEl.hide();
31621                     textareaEl.show();
31622                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31623                         opt.multiline : this.defaultTextHeight);
31624                     activeTextEl = textareaEl;
31625                 }else{
31626                     textboxEl.show();
31627                     textareaEl.hide();
31628                 }
31629             }else{
31630                 textboxEl.hide();
31631                 textareaEl.hide();
31632             }
31633             progressEl.setDisplayed(opt.progress === true);
31634             this.updateProgress(0);
31635             activeTextEl.dom.value = opt.value || "";
31636             if(opt.prompt){
31637                 dlg.setDefaultButton(activeTextEl);
31638             }else{
31639                 var bs = opt.buttons;
31640                 var db = null;
31641                 if(bs && bs.ok){
31642                     db = buttons["ok"];
31643                 }else if(bs && bs.yes){
31644                     db = buttons["yes"];
31645                 }
31646                 dlg.setDefaultButton(db);
31647             }
31648             bwidth = updateButtons(opt.buttons);
31649             this.updateText(opt.msg);
31650             if(opt.cls){
31651                 d.el.addClass(opt.cls);
31652             }
31653             d.proxyDrag = opt.proxyDrag === true;
31654             d.modal = opt.modal !== false;
31655             d.mask = opt.modal !== false ? mask : false;
31656             if(!d.isVisible()){
31657                 // force it to the end of the z-index stack so it gets a cursor in FF
31658                 document.body.appendChild(dlg.el.dom);
31659                 d.animateTarget = null;
31660                 d.show(options.animEl);
31661             }
31662             return this;
31663         },
31664
31665         /**
31666          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31667          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31668          * and closing the message box when the process is complete.
31669          * @param {String} title The title bar text
31670          * @param {String} msg The message box body text
31671          * @return {Roo.MessageBox} This message box
31672          */
31673         progress : function(title, msg){
31674             this.show({
31675                 title : title,
31676                 msg : msg,
31677                 buttons: false,
31678                 progress:true,
31679                 closable:false,
31680                 minWidth: this.minProgressWidth,
31681                 modal : true
31682             });
31683             return this;
31684         },
31685
31686         /**
31687          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31688          * If a callback function is passed it will be called after the user clicks the button, and the
31689          * id of the button that was clicked will be passed as the only parameter to the callback
31690          * (could also be the top-right close button).
31691          * @param {String} title The title bar text
31692          * @param {String} msg The message box body text
31693          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31694          * @param {Object} scope (optional) The scope of the callback function
31695          * @return {Roo.MessageBox} This message box
31696          */
31697         alert : function(title, msg, fn, scope){
31698             this.show({
31699                 title : title,
31700                 msg : msg,
31701                 buttons: this.OK,
31702                 fn: fn,
31703                 scope : scope,
31704                 modal : true
31705             });
31706             return this;
31707         },
31708
31709         /**
31710          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31711          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31712          * You are responsible for closing the message box when the process is complete.
31713          * @param {String} msg The message box body text
31714          * @param {String} title (optional) The title bar text
31715          * @return {Roo.MessageBox} This message box
31716          */
31717         wait : function(msg, title){
31718             this.show({
31719                 title : title,
31720                 msg : msg,
31721                 buttons: false,
31722                 closable:false,
31723                 progress:true,
31724                 modal:true,
31725                 width:300,
31726                 wait:true
31727             });
31728             waitTimer = Roo.TaskMgr.start({
31729                 run: function(i){
31730                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31731                 },
31732                 interval: 1000
31733             });
31734             return this;
31735         },
31736
31737         /**
31738          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31739          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31740          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31741          * @param {String} title The title bar text
31742          * @param {String} msg The message box body text
31743          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31744          * @param {Object} scope (optional) The scope of the callback function
31745          * @return {Roo.MessageBox} This message box
31746          */
31747         confirm : function(title, msg, fn, scope){
31748             this.show({
31749                 title : title,
31750                 msg : msg,
31751                 buttons: this.YESNO,
31752                 fn: fn,
31753                 scope : scope,
31754                 modal : true
31755             });
31756             return this;
31757         },
31758
31759         /**
31760          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31761          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31762          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31763          * (could also be the top-right close button) and the text that was entered will be passed as the two
31764          * parameters to the callback.
31765          * @param {String} title The title bar text
31766          * @param {String} msg The message box body text
31767          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31768          * @param {Object} scope (optional) The scope of the callback function
31769          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31770          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31771          * @return {Roo.MessageBox} This message box
31772          */
31773         prompt : function(title, msg, fn, scope, multiline){
31774             this.show({
31775                 title : title,
31776                 msg : msg,
31777                 buttons: this.OKCANCEL,
31778                 fn: fn,
31779                 minWidth:250,
31780                 scope : scope,
31781                 prompt:true,
31782                 multiline: multiline,
31783                 modal : true
31784             });
31785             return this;
31786         },
31787
31788         /**
31789          * Button config that displays a single OK button
31790          * @type Object
31791          */
31792         OK : {ok:true},
31793         /**
31794          * Button config that displays Yes and No buttons
31795          * @type Object
31796          */
31797         YESNO : {yes:true, no:true},
31798         /**
31799          * Button config that displays OK and Cancel buttons
31800          * @type Object
31801          */
31802         OKCANCEL : {ok:true, cancel:true},
31803         /**
31804          * Button config that displays Yes, No and Cancel buttons
31805          * @type Object
31806          */
31807         YESNOCANCEL : {yes:true, no:true, cancel:true},
31808
31809         /**
31810          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31811          * @type Number
31812          */
31813         defaultTextHeight : 75,
31814         /**
31815          * The maximum width in pixels of the message box (defaults to 600)
31816          * @type Number
31817          */
31818         maxWidth : 600,
31819         /**
31820          * The minimum width in pixels of the message box (defaults to 100)
31821          * @type Number
31822          */
31823         minWidth : 100,
31824         /**
31825          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31826          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31827          * @type Number
31828          */
31829         minProgressWidth : 250,
31830         /**
31831          * An object containing the default button text strings that can be overriden for localized language support.
31832          * Supported properties are: ok, cancel, yes and no.
31833          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31834          * @type Object
31835          */
31836         buttonText : {
31837             ok : "OK",
31838             cancel : "Cancel",
31839             yes : "Yes",
31840             no : "No"
31841         }
31842     };
31843 }();
31844
31845 /**
31846  * Shorthand for {@link Roo.MessageBox}
31847  */
31848 Roo.Msg = Roo.MessageBox;/*
31849  * Based on:
31850  * Ext JS Library 1.1.1
31851  * Copyright(c) 2006-2007, Ext JS, LLC.
31852  *
31853  * Originally Released Under LGPL - original licence link has changed is not relivant.
31854  *
31855  * Fork - LGPL
31856  * <script type="text/javascript">
31857  */
31858 /**
31859  * @class Roo.QuickTips
31860  * Provides attractive and customizable tooltips for any element.
31861  * @singleton
31862  */
31863 Roo.QuickTips = function(){
31864     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31865     var ce, bd, xy, dd;
31866     var visible = false, disabled = true, inited = false;
31867     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31868     
31869     var onOver = function(e){
31870         if(disabled){
31871             return;
31872         }
31873         var t = e.getTarget();
31874         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31875             return;
31876         }
31877         if(ce && t == ce.el){
31878             clearTimeout(hideProc);
31879             return;
31880         }
31881         if(t && tagEls[t.id]){
31882             tagEls[t.id].el = t;
31883             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31884             return;
31885         }
31886         var ttp, et = Roo.fly(t);
31887         var ns = cfg.namespace;
31888         if(tm.interceptTitles && t.title){
31889             ttp = t.title;
31890             t.qtip = ttp;
31891             t.removeAttribute("title");
31892             e.preventDefault();
31893         }else{
31894             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31895         }
31896         if(ttp){
31897             showProc = show.defer(tm.showDelay, tm, [{
31898                 el: t, 
31899                 text: ttp, 
31900                 width: et.getAttributeNS(ns, cfg.width),
31901                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31902                 title: et.getAttributeNS(ns, cfg.title),
31903                     cls: et.getAttributeNS(ns, cfg.cls)
31904             }]);
31905         }
31906     };
31907     
31908     var onOut = function(e){
31909         clearTimeout(showProc);
31910         var t = e.getTarget();
31911         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31912             hideProc = setTimeout(hide, tm.hideDelay);
31913         }
31914     };
31915     
31916     var onMove = function(e){
31917         if(disabled){
31918             return;
31919         }
31920         xy = e.getXY();
31921         xy[1] += 18;
31922         if(tm.trackMouse && ce){
31923             el.setXY(xy);
31924         }
31925     };
31926     
31927     var onDown = function(e){
31928         clearTimeout(showProc);
31929         clearTimeout(hideProc);
31930         if(!e.within(el)){
31931             if(tm.hideOnClick){
31932                 hide();
31933                 tm.disable();
31934                 tm.enable.defer(100, tm);
31935             }
31936         }
31937     };
31938     
31939     var getPad = function(){
31940         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31941     };
31942
31943     var show = function(o){
31944         if(disabled){
31945             return;
31946         }
31947         clearTimeout(dismissProc);
31948         ce = o;
31949         if(removeCls){ // in case manually hidden
31950             el.removeClass(removeCls);
31951             removeCls = null;
31952         }
31953         if(ce.cls){
31954             el.addClass(ce.cls);
31955             removeCls = ce.cls;
31956         }
31957         if(ce.title){
31958             tipTitle.update(ce.title);
31959             tipTitle.show();
31960         }else{
31961             tipTitle.update('');
31962             tipTitle.hide();
31963         }
31964         el.dom.style.width  = tm.maxWidth+'px';
31965         //tipBody.dom.style.width = '';
31966         tipBodyText.update(o.text);
31967         var p = getPad(), w = ce.width;
31968         if(!w){
31969             var td = tipBodyText.dom;
31970             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31971             if(aw > tm.maxWidth){
31972                 w = tm.maxWidth;
31973             }else if(aw < tm.minWidth){
31974                 w = tm.minWidth;
31975             }else{
31976                 w = aw;
31977             }
31978         }
31979         //tipBody.setWidth(w);
31980         el.setWidth(parseInt(w, 10) + p);
31981         if(ce.autoHide === false){
31982             close.setDisplayed(true);
31983             if(dd){
31984                 dd.unlock();
31985             }
31986         }else{
31987             close.setDisplayed(false);
31988             if(dd){
31989                 dd.lock();
31990             }
31991         }
31992         if(xy){
31993             el.avoidY = xy[1]-18;
31994             el.setXY(xy);
31995         }
31996         if(tm.animate){
31997             el.setOpacity(.1);
31998             el.setStyle("visibility", "visible");
31999             el.fadeIn({callback: afterShow});
32000         }else{
32001             afterShow();
32002         }
32003     };
32004     
32005     var afterShow = function(){
32006         if(ce){
32007             el.show();
32008             esc.enable();
32009             if(tm.autoDismiss && ce.autoHide !== false){
32010                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32011             }
32012         }
32013     };
32014     
32015     var hide = function(noanim){
32016         clearTimeout(dismissProc);
32017         clearTimeout(hideProc);
32018         ce = null;
32019         if(el.isVisible()){
32020             esc.disable();
32021             if(noanim !== true && tm.animate){
32022                 el.fadeOut({callback: afterHide});
32023             }else{
32024                 afterHide();
32025             } 
32026         }
32027     };
32028     
32029     var afterHide = function(){
32030         el.hide();
32031         if(removeCls){
32032             el.removeClass(removeCls);
32033             removeCls = null;
32034         }
32035     };
32036     
32037     return {
32038         /**
32039         * @cfg {Number} minWidth
32040         * The minimum width of the quick tip (defaults to 40)
32041         */
32042        minWidth : 40,
32043         /**
32044         * @cfg {Number} maxWidth
32045         * The maximum width of the quick tip (defaults to 300)
32046         */
32047        maxWidth : 300,
32048         /**
32049         * @cfg {Boolean} interceptTitles
32050         * True to automatically use the element's DOM title value if available (defaults to false)
32051         */
32052        interceptTitles : false,
32053         /**
32054         * @cfg {Boolean} trackMouse
32055         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32056         */
32057        trackMouse : false,
32058         /**
32059         * @cfg {Boolean} hideOnClick
32060         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32061         */
32062        hideOnClick : true,
32063         /**
32064         * @cfg {Number} showDelay
32065         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32066         */
32067        showDelay : 500,
32068         /**
32069         * @cfg {Number} hideDelay
32070         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32071         */
32072        hideDelay : 200,
32073         /**
32074         * @cfg {Boolean} autoHide
32075         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32076         * Used in conjunction with hideDelay.
32077         */
32078        autoHide : true,
32079         /**
32080         * @cfg {Boolean}
32081         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32082         * (defaults to true).  Used in conjunction with autoDismissDelay.
32083         */
32084        autoDismiss : true,
32085         /**
32086         * @cfg {Number}
32087         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32088         */
32089        autoDismissDelay : 5000,
32090        /**
32091         * @cfg {Boolean} animate
32092         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32093         */
32094        animate : false,
32095
32096        /**
32097         * @cfg {String} title
32098         * Title text to display (defaults to '').  This can be any valid HTML markup.
32099         */
32100         title: '',
32101        /**
32102         * @cfg {String} text
32103         * Body text to display (defaults to '').  This can be any valid HTML markup.
32104         */
32105         text : '',
32106        /**
32107         * @cfg {String} cls
32108         * A CSS class to apply to the base quick tip element (defaults to '').
32109         */
32110         cls : '',
32111        /**
32112         * @cfg {Number} width
32113         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32114         * minWidth or maxWidth.
32115         */
32116         width : null,
32117
32118     /**
32119      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32120      * or display QuickTips in a page.
32121      */
32122        init : function(){
32123           tm = Roo.QuickTips;
32124           cfg = tm.tagConfig;
32125           if(!inited){
32126               if(!Roo.isReady){ // allow calling of init() before onReady
32127                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32128                   return;
32129               }
32130               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32131               el.fxDefaults = {stopFx: true};
32132               // maximum custom styling
32133               //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>');
32134               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>');              
32135               tipTitle = el.child('h3');
32136               tipTitle.enableDisplayMode("block");
32137               tipBody = el.child('div.x-tip-bd');
32138               tipBodyText = el.child('div.x-tip-bd-inner');
32139               //bdLeft = el.child('div.x-tip-bd-left');
32140               //bdRight = el.child('div.x-tip-bd-right');
32141               close = el.child('div.x-tip-close');
32142               close.enableDisplayMode("block");
32143               close.on("click", hide);
32144               var d = Roo.get(document);
32145               d.on("mousedown", onDown);
32146               d.on("mouseover", onOver);
32147               d.on("mouseout", onOut);
32148               d.on("mousemove", onMove);
32149               esc = d.addKeyListener(27, hide);
32150               esc.disable();
32151               if(Roo.dd.DD){
32152                   dd = el.initDD("default", null, {
32153                       onDrag : function(){
32154                           el.sync();  
32155                       }
32156                   });
32157                   dd.setHandleElId(tipTitle.id);
32158                   dd.lock();
32159               }
32160               inited = true;
32161           }
32162           this.enable(); 
32163        },
32164
32165     /**
32166      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32167      * are supported:
32168      * <pre>
32169 Property    Type                   Description
32170 ----------  ---------------------  ------------------------------------------------------------------------
32171 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32172      * </ul>
32173      * @param {Object} config The config object
32174      */
32175        register : function(config){
32176            var cs = config instanceof Array ? config : arguments;
32177            for(var i = 0, len = cs.length; i < len; i++) {
32178                var c = cs[i];
32179                var target = c.target;
32180                if(target){
32181                    if(target instanceof Array){
32182                        for(var j = 0, jlen = target.length; j < jlen; j++){
32183                            tagEls[target[j]] = c;
32184                        }
32185                    }else{
32186                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32187                    }
32188                }
32189            }
32190        },
32191
32192     /**
32193      * Removes this quick tip from its element and destroys it.
32194      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32195      */
32196        unregister : function(el){
32197            delete tagEls[Roo.id(el)];
32198        },
32199
32200     /**
32201      * Enable this quick tip.
32202      */
32203        enable : function(){
32204            if(inited && disabled){
32205                locks.pop();
32206                if(locks.length < 1){
32207                    disabled = false;
32208                }
32209            }
32210        },
32211
32212     /**
32213      * Disable this quick tip.
32214      */
32215        disable : function(){
32216           disabled = true;
32217           clearTimeout(showProc);
32218           clearTimeout(hideProc);
32219           clearTimeout(dismissProc);
32220           if(ce){
32221               hide(true);
32222           }
32223           locks.push(1);
32224        },
32225
32226     /**
32227      * Returns true if the quick tip is enabled, else false.
32228      */
32229        isEnabled : function(){
32230             return !disabled;
32231        },
32232
32233         // private
32234        tagConfig : {
32235            namespace : "ext",
32236            attribute : "qtip",
32237            width : "width",
32238            target : "target",
32239            title : "qtitle",
32240            hide : "hide",
32241            cls : "qclass"
32242        }
32243    };
32244 }();
32245
32246 // backwards compat
32247 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32248  * Based on:
32249  * Ext JS Library 1.1.1
32250  * Copyright(c) 2006-2007, Ext JS, LLC.
32251  *
32252  * Originally Released Under LGPL - original licence link has changed is not relivant.
32253  *
32254  * Fork - LGPL
32255  * <script type="text/javascript">
32256  */
32257  
32258
32259 /**
32260  * @class Roo.tree.TreePanel
32261  * @extends Roo.data.Tree
32262
32263  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32264  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32265  * @cfg {Boolean} enableDD true to enable drag and drop
32266  * @cfg {Boolean} enableDrag true to enable just drag
32267  * @cfg {Boolean} enableDrop true to enable just drop
32268  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32269  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32270  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32271  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32272  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32273  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32274  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32275  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32276  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32277  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32278  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32279  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32280  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32281  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32282  * @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>
32283  * @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>
32284  * 
32285  * @constructor
32286  * @param {String/HTMLElement/Element} el The container element
32287  * @param {Object} config
32288  */
32289 Roo.tree.TreePanel = function(el, config){
32290     var root = false;
32291     var loader = false;
32292     if (config.root) {
32293         root = config.root;
32294         delete config.root;
32295     }
32296     if (config.loader) {
32297         loader = config.loader;
32298         delete config.loader;
32299     }
32300     
32301     Roo.apply(this, config);
32302     Roo.tree.TreePanel.superclass.constructor.call(this);
32303     this.el = Roo.get(el);
32304     this.el.addClass('x-tree');
32305     //console.log(root);
32306     if (root) {
32307         this.setRootNode( Roo.factory(root, Roo.tree));
32308     }
32309     if (loader) {
32310         this.loader = Roo.factory(loader, Roo.tree);
32311     }
32312    /**
32313     * Read-only. The id of the container element becomes this TreePanel's id.
32314     */
32315     this.id = this.el.id;
32316     this.addEvents({
32317         /**
32318         * @event beforeload
32319         * Fires before a node is loaded, return false to cancel
32320         * @param {Node} node The node being loaded
32321         */
32322         "beforeload" : true,
32323         /**
32324         * @event load
32325         * Fires when a node is loaded
32326         * @param {Node} node The node that was loaded
32327         */
32328         "load" : true,
32329         /**
32330         * @event textchange
32331         * Fires when the text for a node is changed
32332         * @param {Node} node The node
32333         * @param {String} text The new text
32334         * @param {String} oldText The old text
32335         */
32336         "textchange" : true,
32337         /**
32338         * @event beforeexpand
32339         * Fires before a node is expanded, return false to cancel.
32340         * @param {Node} node The node
32341         * @param {Boolean} deep
32342         * @param {Boolean} anim
32343         */
32344         "beforeexpand" : true,
32345         /**
32346         * @event beforecollapse
32347         * Fires before a node is collapsed, return false to cancel.
32348         * @param {Node} node The node
32349         * @param {Boolean} deep
32350         * @param {Boolean} anim
32351         */
32352         "beforecollapse" : true,
32353         /**
32354         * @event expand
32355         * Fires when a node is expanded
32356         * @param {Node} node The node
32357         */
32358         "expand" : true,
32359         /**
32360         * @event disabledchange
32361         * Fires when the disabled status of a node changes
32362         * @param {Node} node The node
32363         * @param {Boolean} disabled
32364         */
32365         "disabledchange" : true,
32366         /**
32367         * @event collapse
32368         * Fires when a node is collapsed
32369         * @param {Node} node The node
32370         */
32371         "collapse" : true,
32372         /**
32373         * @event beforeclick
32374         * Fires before click processing on a node. Return false to cancel the default action.
32375         * @param {Node} node The node
32376         * @param {Roo.EventObject} e The event object
32377         */
32378         "beforeclick":true,
32379         /**
32380         * @event checkchange
32381         * Fires when a node with a checkbox's checked property changes
32382         * @param {Node} this This node
32383         * @param {Boolean} checked
32384         */
32385         "checkchange":true,
32386         /**
32387         * @event click
32388         * Fires when a node is clicked
32389         * @param {Node} node The node
32390         * @param {Roo.EventObject} e The event object
32391         */
32392         "click":true,
32393         /**
32394         * @event dblclick
32395         * Fires when a node is double clicked
32396         * @param {Node} node The node
32397         * @param {Roo.EventObject} e The event object
32398         */
32399         "dblclick":true,
32400         /**
32401         * @event contextmenu
32402         * Fires when a node is right clicked
32403         * @param {Node} node The node
32404         * @param {Roo.EventObject} e The event object
32405         */
32406         "contextmenu":true,
32407         /**
32408         * @event beforechildrenrendered
32409         * Fires right before the child nodes for a node are rendered
32410         * @param {Node} node The node
32411         */
32412         "beforechildrenrendered":true,
32413         /**
32414         * @event startdrag
32415         * Fires when a node starts being dragged
32416         * @param {Roo.tree.TreePanel} this
32417         * @param {Roo.tree.TreeNode} node
32418         * @param {event} e The raw browser event
32419         */ 
32420        "startdrag" : true,
32421        /**
32422         * @event enddrag
32423         * Fires when a drag operation is complete
32424         * @param {Roo.tree.TreePanel} this
32425         * @param {Roo.tree.TreeNode} node
32426         * @param {event} e The raw browser event
32427         */
32428        "enddrag" : true,
32429        /**
32430         * @event dragdrop
32431         * Fires when a dragged node is dropped on a valid DD target
32432         * @param {Roo.tree.TreePanel} this
32433         * @param {Roo.tree.TreeNode} node
32434         * @param {DD} dd The dd it was dropped on
32435         * @param {event} e The raw browser event
32436         */
32437        "dragdrop" : true,
32438        /**
32439         * @event beforenodedrop
32440         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32441         * passed to handlers has the following properties:<br />
32442         * <ul style="padding:5px;padding-left:16px;">
32443         * <li>tree - The TreePanel</li>
32444         * <li>target - The node being targeted for the drop</li>
32445         * <li>data - The drag data from the drag source</li>
32446         * <li>point - The point of the drop - append, above or below</li>
32447         * <li>source - The drag source</li>
32448         * <li>rawEvent - Raw mouse event</li>
32449         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32450         * to be inserted by setting them on this object.</li>
32451         * <li>cancel - Set this to true to cancel the drop.</li>
32452         * </ul>
32453         * @param {Object} dropEvent
32454         */
32455        "beforenodedrop" : true,
32456        /**
32457         * @event nodedrop
32458         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32459         * passed to handlers has the following properties:<br />
32460         * <ul style="padding:5px;padding-left:16px;">
32461         * <li>tree - The TreePanel</li>
32462         * <li>target - The node being targeted for the drop</li>
32463         * <li>data - The drag data from the drag source</li>
32464         * <li>point - The point of the drop - append, above or below</li>
32465         * <li>source - The drag source</li>
32466         * <li>rawEvent - Raw mouse event</li>
32467         * <li>dropNode - Dropped node(s).</li>
32468         * </ul>
32469         * @param {Object} dropEvent
32470         */
32471        "nodedrop" : true,
32472         /**
32473         * @event nodedragover
32474         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32475         * passed to handlers has the following properties:<br />
32476         * <ul style="padding:5px;padding-left:16px;">
32477         * <li>tree - The TreePanel</li>
32478         * <li>target - The node being targeted for the drop</li>
32479         * <li>data - The drag data from the drag source</li>
32480         * <li>point - The point of the drop - append, above or below</li>
32481         * <li>source - The drag source</li>
32482         * <li>rawEvent - Raw mouse event</li>
32483         * <li>dropNode - Drop node(s) provided by the source.</li>
32484         * <li>cancel - Set this to true to signal drop not allowed.</li>
32485         * </ul>
32486         * @param {Object} dragOverEvent
32487         */
32488        "nodedragover" : true
32489         
32490     });
32491     if(this.singleExpand){
32492        this.on("beforeexpand", this.restrictExpand, this);
32493     }
32494     if (this.editor) {
32495         this.editor.tree = this;
32496         this.editor = Roo.factory(this.editor, Roo.tree);
32497     }
32498     
32499     if (this.selModel) {
32500         this.selModel = Roo.factory(this.selModel, Roo.tree);
32501     }
32502    
32503 };
32504 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32505     rootVisible : true,
32506     animate: Roo.enableFx,
32507     lines : true,
32508     enableDD : false,
32509     hlDrop : Roo.enableFx,
32510   
32511     renderer: false,
32512     
32513     rendererTip: false,
32514     // private
32515     restrictExpand : function(node){
32516         var p = node.parentNode;
32517         if(p){
32518             if(p.expandedChild && p.expandedChild.parentNode == p){
32519                 p.expandedChild.collapse();
32520             }
32521             p.expandedChild = node;
32522         }
32523     },
32524
32525     // private override
32526     setRootNode : function(node){
32527         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32528         if(!this.rootVisible){
32529             node.ui = new Roo.tree.RootTreeNodeUI(node);
32530         }
32531         return node;
32532     },
32533
32534     /**
32535      * Returns the container element for this TreePanel
32536      */
32537     getEl : function(){
32538         return this.el;
32539     },
32540
32541     /**
32542      * Returns the default TreeLoader for this TreePanel
32543      */
32544     getLoader : function(){
32545         return this.loader;
32546     },
32547
32548     /**
32549      * Expand all nodes
32550      */
32551     expandAll : function(){
32552         this.root.expand(true);
32553     },
32554
32555     /**
32556      * Collapse all nodes
32557      */
32558     collapseAll : function(){
32559         this.root.collapse(true);
32560     },
32561
32562     /**
32563      * Returns the selection model used by this TreePanel
32564      */
32565     getSelectionModel : function(){
32566         if(!this.selModel){
32567             this.selModel = new Roo.tree.DefaultSelectionModel();
32568         }
32569         return this.selModel;
32570     },
32571
32572     /**
32573      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32574      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32575      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32576      * @return {Array}
32577      */
32578     getChecked : function(a, startNode){
32579         startNode = startNode || this.root;
32580         var r = [];
32581         var f = function(){
32582             if(this.attributes.checked){
32583                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32584             }
32585         }
32586         startNode.cascade(f);
32587         return r;
32588     },
32589
32590     /**
32591      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32592      * @param {String} path
32593      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32594      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32595      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32596      */
32597     expandPath : function(path, attr, callback){
32598         attr = attr || "id";
32599         var keys = path.split(this.pathSeparator);
32600         var curNode = this.root;
32601         if(curNode.attributes[attr] != keys[1]){ // invalid root
32602             if(callback){
32603                 callback(false, null);
32604             }
32605             return;
32606         }
32607         var index = 1;
32608         var f = function(){
32609             if(++index == keys.length){
32610                 if(callback){
32611                     callback(true, curNode);
32612                 }
32613                 return;
32614             }
32615             var c = curNode.findChild(attr, keys[index]);
32616             if(!c){
32617                 if(callback){
32618                     callback(false, curNode);
32619                 }
32620                 return;
32621             }
32622             curNode = c;
32623             c.expand(false, false, f);
32624         };
32625         curNode.expand(false, false, f);
32626     },
32627
32628     /**
32629      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32630      * @param {String} path
32631      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32632      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32633      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32634      */
32635     selectPath : function(path, attr, callback){
32636         attr = attr || "id";
32637         var keys = path.split(this.pathSeparator);
32638         var v = keys.pop();
32639         if(keys.length > 0){
32640             var f = function(success, node){
32641                 if(success && node){
32642                     var n = node.findChild(attr, v);
32643                     if(n){
32644                         n.select();
32645                         if(callback){
32646                             callback(true, n);
32647                         }
32648                     }else if(callback){
32649                         callback(false, n);
32650                     }
32651                 }else{
32652                     if(callback){
32653                         callback(false, n);
32654                     }
32655                 }
32656             };
32657             this.expandPath(keys.join(this.pathSeparator), attr, f);
32658         }else{
32659             this.root.select();
32660             if(callback){
32661                 callback(true, this.root);
32662             }
32663         }
32664     },
32665
32666     getTreeEl : function(){
32667         return this.el;
32668     },
32669
32670     /**
32671      * Trigger rendering of this TreePanel
32672      */
32673     render : function(){
32674         if (this.innerCt) {
32675             return this; // stop it rendering more than once!!
32676         }
32677         
32678         this.innerCt = this.el.createChild({tag:"ul",
32679                cls:"x-tree-root-ct " +
32680                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32681
32682         if(this.containerScroll){
32683             Roo.dd.ScrollManager.register(this.el);
32684         }
32685         if((this.enableDD || this.enableDrop) && !this.dropZone){
32686            /**
32687             * The dropZone used by this tree if drop is enabled
32688             * @type Roo.tree.TreeDropZone
32689             */
32690              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32691                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32692            });
32693         }
32694         if((this.enableDD || this.enableDrag) && !this.dragZone){
32695            /**
32696             * The dragZone used by this tree if drag is enabled
32697             * @type Roo.tree.TreeDragZone
32698             */
32699             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32700                ddGroup: this.ddGroup || "TreeDD",
32701                scroll: this.ddScroll
32702            });
32703         }
32704         this.getSelectionModel().init(this);
32705         if (!this.root) {
32706             Roo.log("ROOT not set in tree");
32707             return this;
32708         }
32709         this.root.render();
32710         if(!this.rootVisible){
32711             this.root.renderChildren();
32712         }
32713         return this;
32714     }
32715 });/*
32716  * Based on:
32717  * Ext JS Library 1.1.1
32718  * Copyright(c) 2006-2007, Ext JS, LLC.
32719  *
32720  * Originally Released Under LGPL - original licence link has changed is not relivant.
32721  *
32722  * Fork - LGPL
32723  * <script type="text/javascript">
32724  */
32725  
32726
32727 /**
32728  * @class Roo.tree.DefaultSelectionModel
32729  * @extends Roo.util.Observable
32730  * The default single selection for a TreePanel.
32731  * @param {Object} cfg Configuration
32732  */
32733 Roo.tree.DefaultSelectionModel = function(cfg){
32734    this.selNode = null;
32735    
32736    
32737    
32738    this.addEvents({
32739        /**
32740         * @event selectionchange
32741         * Fires when the selected node changes
32742         * @param {DefaultSelectionModel} this
32743         * @param {TreeNode} node the new selection
32744         */
32745        "selectionchange" : true,
32746
32747        /**
32748         * @event beforeselect
32749         * Fires before the selected node changes, return false to cancel the change
32750         * @param {DefaultSelectionModel} this
32751         * @param {TreeNode} node the new selection
32752         * @param {TreeNode} node the old selection
32753         */
32754        "beforeselect" : true
32755    });
32756    
32757     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32758 };
32759
32760 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32761     init : function(tree){
32762         this.tree = tree;
32763         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32764         tree.on("click", this.onNodeClick, this);
32765     },
32766     
32767     onNodeClick : function(node, e){
32768         if (e.ctrlKey && this.selNode == node)  {
32769             this.unselect(node);
32770             return;
32771         }
32772         this.select(node);
32773     },
32774     
32775     /**
32776      * Select a node.
32777      * @param {TreeNode} node The node to select
32778      * @return {TreeNode} The selected node
32779      */
32780     select : function(node){
32781         var last = this.selNode;
32782         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32783             if(last){
32784                 last.ui.onSelectedChange(false);
32785             }
32786             this.selNode = node;
32787             node.ui.onSelectedChange(true);
32788             this.fireEvent("selectionchange", this, node, last);
32789         }
32790         return node;
32791     },
32792     
32793     /**
32794      * Deselect a node.
32795      * @param {TreeNode} node The node to unselect
32796      */
32797     unselect : function(node){
32798         if(this.selNode == node){
32799             this.clearSelections();
32800         }    
32801     },
32802     
32803     /**
32804      * Clear all selections
32805      */
32806     clearSelections : function(){
32807         var n = this.selNode;
32808         if(n){
32809             n.ui.onSelectedChange(false);
32810             this.selNode = null;
32811             this.fireEvent("selectionchange", this, null);
32812         }
32813         return n;
32814     },
32815     
32816     /**
32817      * Get the selected node
32818      * @return {TreeNode} The selected node
32819      */
32820     getSelectedNode : function(){
32821         return this.selNode;    
32822     },
32823     
32824     /**
32825      * Returns true if the node is selected
32826      * @param {TreeNode} node The node to check
32827      * @return {Boolean}
32828      */
32829     isSelected : function(node){
32830         return this.selNode == node;  
32831     },
32832
32833     /**
32834      * Selects the node above the selected node in the tree, intelligently walking the nodes
32835      * @return TreeNode The new selection
32836      */
32837     selectPrevious : function(){
32838         var s = this.selNode || this.lastSelNode;
32839         if(!s){
32840             return null;
32841         }
32842         var ps = s.previousSibling;
32843         if(ps){
32844             if(!ps.isExpanded() || ps.childNodes.length < 1){
32845                 return this.select(ps);
32846             } else{
32847                 var lc = ps.lastChild;
32848                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32849                     lc = lc.lastChild;
32850                 }
32851                 return this.select(lc);
32852             }
32853         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32854             return this.select(s.parentNode);
32855         }
32856         return null;
32857     },
32858
32859     /**
32860      * Selects the node above the selected node in the tree, intelligently walking the nodes
32861      * @return TreeNode The new selection
32862      */
32863     selectNext : function(){
32864         var s = this.selNode || this.lastSelNode;
32865         if(!s){
32866             return null;
32867         }
32868         if(s.firstChild && s.isExpanded()){
32869              return this.select(s.firstChild);
32870          }else if(s.nextSibling){
32871              return this.select(s.nextSibling);
32872          }else if(s.parentNode){
32873             var newS = null;
32874             s.parentNode.bubble(function(){
32875                 if(this.nextSibling){
32876                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32877                     return false;
32878                 }
32879             });
32880             return newS;
32881          }
32882         return null;
32883     },
32884
32885     onKeyDown : function(e){
32886         var s = this.selNode || this.lastSelNode;
32887         // undesirable, but required
32888         var sm = this;
32889         if(!s){
32890             return;
32891         }
32892         var k = e.getKey();
32893         switch(k){
32894              case e.DOWN:
32895                  e.stopEvent();
32896                  this.selectNext();
32897              break;
32898              case e.UP:
32899                  e.stopEvent();
32900                  this.selectPrevious();
32901              break;
32902              case e.RIGHT:
32903                  e.preventDefault();
32904                  if(s.hasChildNodes()){
32905                      if(!s.isExpanded()){
32906                          s.expand();
32907                      }else if(s.firstChild){
32908                          this.select(s.firstChild, e);
32909                      }
32910                  }
32911              break;
32912              case e.LEFT:
32913                  e.preventDefault();
32914                  if(s.hasChildNodes() && s.isExpanded()){
32915                      s.collapse();
32916                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32917                      this.select(s.parentNode, e);
32918                  }
32919              break;
32920         };
32921     }
32922 });
32923
32924 /**
32925  * @class Roo.tree.MultiSelectionModel
32926  * @extends Roo.util.Observable
32927  * Multi selection for a TreePanel.
32928  * @param {Object} cfg Configuration
32929  */
32930 Roo.tree.MultiSelectionModel = function(){
32931    this.selNodes = [];
32932    this.selMap = {};
32933    this.addEvents({
32934        /**
32935         * @event selectionchange
32936         * Fires when the selected nodes change
32937         * @param {MultiSelectionModel} this
32938         * @param {Array} nodes Array of the selected nodes
32939         */
32940        "selectionchange" : true
32941    });
32942    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32943    
32944 };
32945
32946 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32947     init : function(tree){
32948         this.tree = tree;
32949         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32950         tree.on("click", this.onNodeClick, this);
32951     },
32952     
32953     onNodeClick : function(node, e){
32954         this.select(node, e, e.ctrlKey);
32955     },
32956     
32957     /**
32958      * Select a node.
32959      * @param {TreeNode} node The node to select
32960      * @param {EventObject} e (optional) An event associated with the selection
32961      * @param {Boolean} keepExisting True to retain existing selections
32962      * @return {TreeNode} The selected node
32963      */
32964     select : function(node, e, keepExisting){
32965         if(keepExisting !== true){
32966             this.clearSelections(true);
32967         }
32968         if(this.isSelected(node)){
32969             this.lastSelNode = node;
32970             return node;
32971         }
32972         this.selNodes.push(node);
32973         this.selMap[node.id] = node;
32974         this.lastSelNode = node;
32975         node.ui.onSelectedChange(true);
32976         this.fireEvent("selectionchange", this, this.selNodes);
32977         return node;
32978     },
32979     
32980     /**
32981      * Deselect a node.
32982      * @param {TreeNode} node The node to unselect
32983      */
32984     unselect : function(node){
32985         if(this.selMap[node.id]){
32986             node.ui.onSelectedChange(false);
32987             var sn = this.selNodes;
32988             var index = -1;
32989             if(sn.indexOf){
32990                 index = sn.indexOf(node);
32991             }else{
32992                 for(var i = 0, len = sn.length; i < len; i++){
32993                     if(sn[i] == node){
32994                         index = i;
32995                         break;
32996                     }
32997                 }
32998             }
32999             if(index != -1){
33000                 this.selNodes.splice(index, 1);
33001             }
33002             delete this.selMap[node.id];
33003             this.fireEvent("selectionchange", this, this.selNodes);
33004         }
33005     },
33006     
33007     /**
33008      * Clear all selections
33009      */
33010     clearSelections : function(suppressEvent){
33011         var sn = this.selNodes;
33012         if(sn.length > 0){
33013             for(var i = 0, len = sn.length; i < len; i++){
33014                 sn[i].ui.onSelectedChange(false);
33015             }
33016             this.selNodes = [];
33017             this.selMap = {};
33018             if(suppressEvent !== true){
33019                 this.fireEvent("selectionchange", this, this.selNodes);
33020             }
33021         }
33022     },
33023     
33024     /**
33025      * Returns true if the node is selected
33026      * @param {TreeNode} node The node to check
33027      * @return {Boolean}
33028      */
33029     isSelected : function(node){
33030         return this.selMap[node.id] ? true : false;  
33031     },
33032     
33033     /**
33034      * Returns an array of the selected nodes
33035      * @return {Array}
33036      */
33037     getSelectedNodes : function(){
33038         return this.selNodes;    
33039     },
33040
33041     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33042
33043     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33044
33045     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33046 });/*
33047  * Based on:
33048  * Ext JS Library 1.1.1
33049  * Copyright(c) 2006-2007, Ext JS, LLC.
33050  *
33051  * Originally Released Under LGPL - original licence link has changed is not relivant.
33052  *
33053  * Fork - LGPL
33054  * <script type="text/javascript">
33055  */
33056  
33057 /**
33058  * @class Roo.tree.TreeNode
33059  * @extends Roo.data.Node
33060  * @cfg {String} text The text for this node
33061  * @cfg {Boolean} expanded true to start the node expanded
33062  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33063  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33064  * @cfg {Boolean} disabled true to start the node disabled
33065  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33066  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33067  * @cfg {String} cls A css class to be added to the node
33068  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33069  * @cfg {String} href URL of the link used for the node (defaults to #)
33070  * @cfg {String} hrefTarget target frame for the link
33071  * @cfg {String} qtip An Ext QuickTip for the node
33072  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33073  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33074  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33075  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33076  * (defaults to undefined with no checkbox rendered)
33077  * @constructor
33078  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33079  */
33080 Roo.tree.TreeNode = function(attributes){
33081     attributes = attributes || {};
33082     if(typeof attributes == "string"){
33083         attributes = {text: attributes};
33084     }
33085     this.childrenRendered = false;
33086     this.rendered = false;
33087     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33088     this.expanded = attributes.expanded === true;
33089     this.isTarget = attributes.isTarget !== false;
33090     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33091     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33092
33093     /**
33094      * Read-only. The text for this node. To change it use setText().
33095      * @type String
33096      */
33097     this.text = attributes.text;
33098     /**
33099      * True if this node is disabled.
33100      * @type Boolean
33101      */
33102     this.disabled = attributes.disabled === true;
33103
33104     this.addEvents({
33105         /**
33106         * @event textchange
33107         * Fires when the text for this node is changed
33108         * @param {Node} this This node
33109         * @param {String} text The new text
33110         * @param {String} oldText The old text
33111         */
33112         "textchange" : true,
33113         /**
33114         * @event beforeexpand
33115         * Fires before this node is expanded, return false to cancel.
33116         * @param {Node} this This node
33117         * @param {Boolean} deep
33118         * @param {Boolean} anim
33119         */
33120         "beforeexpand" : true,
33121         /**
33122         * @event beforecollapse
33123         * Fires before this node is collapsed, return false to cancel.
33124         * @param {Node} this This node
33125         * @param {Boolean} deep
33126         * @param {Boolean} anim
33127         */
33128         "beforecollapse" : true,
33129         /**
33130         * @event expand
33131         * Fires when this node is expanded
33132         * @param {Node} this This node
33133         */
33134         "expand" : true,
33135         /**
33136         * @event disabledchange
33137         * Fires when the disabled status of this node changes
33138         * @param {Node} this This node
33139         * @param {Boolean} disabled
33140         */
33141         "disabledchange" : true,
33142         /**
33143         * @event collapse
33144         * Fires when this node is collapsed
33145         * @param {Node} this This node
33146         */
33147         "collapse" : true,
33148         /**
33149         * @event beforeclick
33150         * Fires before click processing. Return false to cancel the default action.
33151         * @param {Node} this This node
33152         * @param {Roo.EventObject} e The event object
33153         */
33154         "beforeclick":true,
33155         /**
33156         * @event checkchange
33157         * Fires when a node with a checkbox's checked property changes
33158         * @param {Node} this This node
33159         * @param {Boolean} checked
33160         */
33161         "checkchange":true,
33162         /**
33163         * @event click
33164         * Fires when this node is clicked
33165         * @param {Node} this This node
33166         * @param {Roo.EventObject} e The event object
33167         */
33168         "click":true,
33169         /**
33170         * @event dblclick
33171         * Fires when this node is double clicked
33172         * @param {Node} this This node
33173         * @param {Roo.EventObject} e The event object
33174         */
33175         "dblclick":true,
33176         /**
33177         * @event contextmenu
33178         * Fires when this node is right clicked
33179         * @param {Node} this This node
33180         * @param {Roo.EventObject} e The event object
33181         */
33182         "contextmenu":true,
33183         /**
33184         * @event beforechildrenrendered
33185         * Fires right before the child nodes for this node are rendered
33186         * @param {Node} this This node
33187         */
33188         "beforechildrenrendered":true
33189     });
33190
33191     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33192
33193     /**
33194      * Read-only. The UI for this node
33195      * @type TreeNodeUI
33196      */
33197     this.ui = new uiClass(this);
33198     
33199     // finally support items[]
33200     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33201         return;
33202     }
33203     
33204     
33205     Roo.each(this.attributes.items, function(c) {
33206         this.appendChild(Roo.factory(c,Roo.Tree));
33207     }, this);
33208     delete this.attributes.items;
33209     
33210     
33211     
33212 };
33213 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33214     preventHScroll: true,
33215     /**
33216      * Returns true if this node is expanded
33217      * @return {Boolean}
33218      */
33219     isExpanded : function(){
33220         return this.expanded;
33221     },
33222
33223     /**
33224      * Returns the UI object for this node
33225      * @return {TreeNodeUI}
33226      */
33227     getUI : function(){
33228         return this.ui;
33229     },
33230
33231     // private override
33232     setFirstChild : function(node){
33233         var of = this.firstChild;
33234         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33235         if(this.childrenRendered && of && node != of){
33236             of.renderIndent(true, true);
33237         }
33238         if(this.rendered){
33239             this.renderIndent(true, true);
33240         }
33241     },
33242
33243     // private override
33244     setLastChild : function(node){
33245         var ol = this.lastChild;
33246         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33247         if(this.childrenRendered && ol && node != ol){
33248             ol.renderIndent(true, true);
33249         }
33250         if(this.rendered){
33251             this.renderIndent(true, true);
33252         }
33253     },
33254
33255     // these methods are overridden to provide lazy rendering support
33256     // private override
33257     appendChild : function()
33258     {
33259         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33260         if(node && this.childrenRendered){
33261             node.render();
33262         }
33263         this.ui.updateExpandIcon();
33264         return node;
33265     },
33266
33267     // private override
33268     removeChild : function(node){
33269         this.ownerTree.getSelectionModel().unselect(node);
33270         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33271         // if it's been rendered remove dom node
33272         if(this.childrenRendered){
33273             node.ui.remove();
33274         }
33275         if(this.childNodes.length < 1){
33276             this.collapse(false, false);
33277         }else{
33278             this.ui.updateExpandIcon();
33279         }
33280         if(!this.firstChild) {
33281             this.childrenRendered = false;
33282         }
33283         return node;
33284     },
33285
33286     // private override
33287     insertBefore : function(node, refNode){
33288         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33289         if(newNode && refNode && this.childrenRendered){
33290             node.render();
33291         }
33292         this.ui.updateExpandIcon();
33293         return newNode;
33294     },
33295
33296     /**
33297      * Sets the text for this node
33298      * @param {String} text
33299      */
33300     setText : function(text){
33301         var oldText = this.text;
33302         this.text = text;
33303         this.attributes.text = text;
33304         if(this.rendered){ // event without subscribing
33305             this.ui.onTextChange(this, text, oldText);
33306         }
33307         this.fireEvent("textchange", this, text, oldText);
33308     },
33309
33310     /**
33311      * Triggers selection of this node
33312      */
33313     select : function(){
33314         this.getOwnerTree().getSelectionModel().select(this);
33315     },
33316
33317     /**
33318      * Triggers deselection of this node
33319      */
33320     unselect : function(){
33321         this.getOwnerTree().getSelectionModel().unselect(this);
33322     },
33323
33324     /**
33325      * Returns true if this node is selected
33326      * @return {Boolean}
33327      */
33328     isSelected : function(){
33329         return this.getOwnerTree().getSelectionModel().isSelected(this);
33330     },
33331
33332     /**
33333      * Expand this node.
33334      * @param {Boolean} deep (optional) True to expand all children as well
33335      * @param {Boolean} anim (optional) false to cancel the default animation
33336      * @param {Function} callback (optional) A callback to be called when
33337      * expanding this node completes (does not wait for deep expand to complete).
33338      * Called with 1 parameter, this node.
33339      */
33340     expand : function(deep, anim, callback){
33341         if(!this.expanded){
33342             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33343                 return;
33344             }
33345             if(!this.childrenRendered){
33346                 this.renderChildren();
33347             }
33348             this.expanded = true;
33349             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33350                 this.ui.animExpand(function(){
33351                     this.fireEvent("expand", this);
33352                     if(typeof callback == "function"){
33353                         callback(this);
33354                     }
33355                     if(deep === true){
33356                         this.expandChildNodes(true);
33357                     }
33358                 }.createDelegate(this));
33359                 return;
33360             }else{
33361                 this.ui.expand();
33362                 this.fireEvent("expand", this);
33363                 if(typeof callback == "function"){
33364                     callback(this);
33365                 }
33366             }
33367         }else{
33368            if(typeof callback == "function"){
33369                callback(this);
33370            }
33371         }
33372         if(deep === true){
33373             this.expandChildNodes(true);
33374         }
33375     },
33376
33377     isHiddenRoot : function(){
33378         return this.isRoot && !this.getOwnerTree().rootVisible;
33379     },
33380
33381     /**
33382      * Collapse this node.
33383      * @param {Boolean} deep (optional) True to collapse all children as well
33384      * @param {Boolean} anim (optional) false to cancel the default animation
33385      */
33386     collapse : function(deep, anim){
33387         if(this.expanded && !this.isHiddenRoot()){
33388             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33389                 return;
33390             }
33391             this.expanded = false;
33392             if((this.getOwnerTree().animate && anim !== false) || anim){
33393                 this.ui.animCollapse(function(){
33394                     this.fireEvent("collapse", this);
33395                     if(deep === true){
33396                         this.collapseChildNodes(true);
33397                     }
33398                 }.createDelegate(this));
33399                 return;
33400             }else{
33401                 this.ui.collapse();
33402                 this.fireEvent("collapse", this);
33403             }
33404         }
33405         if(deep === true){
33406             var cs = this.childNodes;
33407             for(var i = 0, len = cs.length; i < len; i++) {
33408                 cs[i].collapse(true, false);
33409             }
33410         }
33411     },
33412
33413     // private
33414     delayedExpand : function(delay){
33415         if(!this.expandProcId){
33416             this.expandProcId = this.expand.defer(delay, this);
33417         }
33418     },
33419
33420     // private
33421     cancelExpand : function(){
33422         if(this.expandProcId){
33423             clearTimeout(this.expandProcId);
33424         }
33425         this.expandProcId = false;
33426     },
33427
33428     /**
33429      * Toggles expanded/collapsed state of the node
33430      */
33431     toggle : function(){
33432         if(this.expanded){
33433             this.collapse();
33434         }else{
33435             this.expand();
33436         }
33437     },
33438
33439     /**
33440      * Ensures all parent nodes are expanded
33441      */
33442     ensureVisible : function(callback){
33443         var tree = this.getOwnerTree();
33444         tree.expandPath(this.parentNode.getPath(), false, function(){
33445             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33446             Roo.callback(callback);
33447         }.createDelegate(this));
33448     },
33449
33450     /**
33451      * Expand all child nodes
33452      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33453      */
33454     expandChildNodes : function(deep){
33455         var cs = this.childNodes;
33456         for(var i = 0, len = cs.length; i < len; i++) {
33457                 cs[i].expand(deep);
33458         }
33459     },
33460
33461     /**
33462      * Collapse all child nodes
33463      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33464      */
33465     collapseChildNodes : function(deep){
33466         var cs = this.childNodes;
33467         for(var i = 0, len = cs.length; i < len; i++) {
33468                 cs[i].collapse(deep);
33469         }
33470     },
33471
33472     /**
33473      * Disables this node
33474      */
33475     disable : function(){
33476         this.disabled = true;
33477         this.unselect();
33478         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33479             this.ui.onDisableChange(this, true);
33480         }
33481         this.fireEvent("disabledchange", this, true);
33482     },
33483
33484     /**
33485      * Enables this node
33486      */
33487     enable : function(){
33488         this.disabled = false;
33489         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33490             this.ui.onDisableChange(this, false);
33491         }
33492         this.fireEvent("disabledchange", this, false);
33493     },
33494
33495     // private
33496     renderChildren : function(suppressEvent){
33497         if(suppressEvent !== false){
33498             this.fireEvent("beforechildrenrendered", this);
33499         }
33500         var cs = this.childNodes;
33501         for(var i = 0, len = cs.length; i < len; i++){
33502             cs[i].render(true);
33503         }
33504         this.childrenRendered = true;
33505     },
33506
33507     // private
33508     sort : function(fn, scope){
33509         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33510         if(this.childrenRendered){
33511             var cs = this.childNodes;
33512             for(var i = 0, len = cs.length; i < len; i++){
33513                 cs[i].render(true);
33514             }
33515         }
33516     },
33517
33518     // private
33519     render : function(bulkRender){
33520         this.ui.render(bulkRender);
33521         if(!this.rendered){
33522             this.rendered = true;
33523             if(this.expanded){
33524                 this.expanded = false;
33525                 this.expand(false, false);
33526             }
33527         }
33528     },
33529
33530     // private
33531     renderIndent : function(deep, refresh){
33532         if(refresh){
33533             this.ui.childIndent = null;
33534         }
33535         this.ui.renderIndent();
33536         if(deep === true && this.childrenRendered){
33537             var cs = this.childNodes;
33538             for(var i = 0, len = cs.length; i < len; i++){
33539                 cs[i].renderIndent(true, refresh);
33540             }
33541         }
33542     }
33543 });/*
33544  * Based on:
33545  * Ext JS Library 1.1.1
33546  * Copyright(c) 2006-2007, Ext JS, LLC.
33547  *
33548  * Originally Released Under LGPL - original licence link has changed is not relivant.
33549  *
33550  * Fork - LGPL
33551  * <script type="text/javascript">
33552  */
33553  
33554 /**
33555  * @class Roo.tree.AsyncTreeNode
33556  * @extends Roo.tree.TreeNode
33557  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33558  * @constructor
33559  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33560  */
33561  Roo.tree.AsyncTreeNode = function(config){
33562     this.loaded = false;
33563     this.loading = false;
33564     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33565     /**
33566     * @event beforeload
33567     * Fires before this node is loaded, return false to cancel
33568     * @param {Node} this This node
33569     */
33570     this.addEvents({'beforeload':true, 'load': true});
33571     /**
33572     * @event load
33573     * Fires when this node is loaded
33574     * @param {Node} this This node
33575     */
33576     /**
33577      * The loader used by this node (defaults to using the tree's defined loader)
33578      * @type TreeLoader
33579      * @property loader
33580      */
33581 };
33582 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33583     expand : function(deep, anim, callback){
33584         if(this.loading){ // if an async load is already running, waiting til it's done
33585             var timer;
33586             var f = function(){
33587                 if(!this.loading){ // done loading
33588                     clearInterval(timer);
33589                     this.expand(deep, anim, callback);
33590                 }
33591             }.createDelegate(this);
33592             timer = setInterval(f, 200);
33593             return;
33594         }
33595         if(!this.loaded){
33596             if(this.fireEvent("beforeload", this) === false){
33597                 return;
33598             }
33599             this.loading = true;
33600             this.ui.beforeLoad(this);
33601             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33602             if(loader){
33603                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33604                 return;
33605             }
33606         }
33607         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33608     },
33609     
33610     /**
33611      * Returns true if this node is currently loading
33612      * @return {Boolean}
33613      */
33614     isLoading : function(){
33615         return this.loading;  
33616     },
33617     
33618     loadComplete : function(deep, anim, callback){
33619         this.loading = false;
33620         this.loaded = true;
33621         this.ui.afterLoad(this);
33622         this.fireEvent("load", this);
33623         this.expand(deep, anim, callback);
33624     },
33625     
33626     /**
33627      * Returns true if this node has been loaded
33628      * @return {Boolean}
33629      */
33630     isLoaded : function(){
33631         return this.loaded;
33632     },
33633     
33634     hasChildNodes : function(){
33635         if(!this.isLeaf() && !this.loaded){
33636             return true;
33637         }else{
33638             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33639         }
33640     },
33641
33642     /**
33643      * Trigger a reload for this node
33644      * @param {Function} callback
33645      */
33646     reload : function(callback){
33647         this.collapse(false, false);
33648         while(this.firstChild){
33649             this.removeChild(this.firstChild);
33650         }
33651         this.childrenRendered = false;
33652         this.loaded = false;
33653         if(this.isHiddenRoot()){
33654             this.expanded = false;
33655         }
33656         this.expand(false, false, callback);
33657     }
33658 });/*
33659  * Based on:
33660  * Ext JS Library 1.1.1
33661  * Copyright(c) 2006-2007, Ext JS, LLC.
33662  *
33663  * Originally Released Under LGPL - original licence link has changed is not relivant.
33664  *
33665  * Fork - LGPL
33666  * <script type="text/javascript">
33667  */
33668  
33669 /**
33670  * @class Roo.tree.TreeNodeUI
33671  * @constructor
33672  * @param {Object} node The node to render
33673  * The TreeNode UI implementation is separate from the
33674  * tree implementation. Unless you are customizing the tree UI,
33675  * you should never have to use this directly.
33676  */
33677 Roo.tree.TreeNodeUI = function(node){
33678     this.node = node;
33679     this.rendered = false;
33680     this.animating = false;
33681     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33682 };
33683
33684 Roo.tree.TreeNodeUI.prototype = {
33685     removeChild : function(node){
33686         if(this.rendered){
33687             this.ctNode.removeChild(node.ui.getEl());
33688         }
33689     },
33690
33691     beforeLoad : function(){
33692          this.addClass("x-tree-node-loading");
33693     },
33694
33695     afterLoad : function(){
33696          this.removeClass("x-tree-node-loading");
33697     },
33698
33699     onTextChange : function(node, text, oldText){
33700         if(this.rendered){
33701             this.textNode.innerHTML = text;
33702         }
33703     },
33704
33705     onDisableChange : function(node, state){
33706         this.disabled = state;
33707         if(state){
33708             this.addClass("x-tree-node-disabled");
33709         }else{
33710             this.removeClass("x-tree-node-disabled");
33711         }
33712     },
33713
33714     onSelectedChange : function(state){
33715         if(state){
33716             this.focus();
33717             this.addClass("x-tree-selected");
33718         }else{
33719             //this.blur();
33720             this.removeClass("x-tree-selected");
33721         }
33722     },
33723
33724     onMove : function(tree, node, oldParent, newParent, index, refNode){
33725         this.childIndent = null;
33726         if(this.rendered){
33727             var targetNode = newParent.ui.getContainer();
33728             if(!targetNode){//target not rendered
33729                 this.holder = document.createElement("div");
33730                 this.holder.appendChild(this.wrap);
33731                 return;
33732             }
33733             var insertBefore = refNode ? refNode.ui.getEl() : null;
33734             if(insertBefore){
33735                 targetNode.insertBefore(this.wrap, insertBefore);
33736             }else{
33737                 targetNode.appendChild(this.wrap);
33738             }
33739             this.node.renderIndent(true);
33740         }
33741     },
33742
33743     addClass : function(cls){
33744         if(this.elNode){
33745             Roo.fly(this.elNode).addClass(cls);
33746         }
33747     },
33748
33749     removeClass : function(cls){
33750         if(this.elNode){
33751             Roo.fly(this.elNode).removeClass(cls);
33752         }
33753     },
33754
33755     remove : function(){
33756         if(this.rendered){
33757             this.holder = document.createElement("div");
33758             this.holder.appendChild(this.wrap);
33759         }
33760     },
33761
33762     fireEvent : function(){
33763         return this.node.fireEvent.apply(this.node, arguments);
33764     },
33765
33766     initEvents : function(){
33767         this.node.on("move", this.onMove, this);
33768         var E = Roo.EventManager;
33769         var a = this.anchor;
33770
33771         var el = Roo.fly(a, '_treeui');
33772
33773         if(Roo.isOpera){ // opera render bug ignores the CSS
33774             el.setStyle("text-decoration", "none");
33775         }
33776
33777         el.on("click", this.onClick, this);
33778         el.on("dblclick", this.onDblClick, this);
33779
33780         if(this.checkbox){
33781             Roo.EventManager.on(this.checkbox,
33782                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33783         }
33784
33785         el.on("contextmenu", this.onContextMenu, this);
33786
33787         var icon = Roo.fly(this.iconNode);
33788         icon.on("click", this.onClick, this);
33789         icon.on("dblclick", this.onDblClick, this);
33790         icon.on("contextmenu", this.onContextMenu, this);
33791         E.on(this.ecNode, "click", this.ecClick, this, true);
33792
33793         if(this.node.disabled){
33794             this.addClass("x-tree-node-disabled");
33795         }
33796         if(this.node.hidden){
33797             this.addClass("x-tree-node-disabled");
33798         }
33799         var ot = this.node.getOwnerTree();
33800         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33801         if(dd && (!this.node.isRoot || ot.rootVisible)){
33802             Roo.dd.Registry.register(this.elNode, {
33803                 node: this.node,
33804                 handles: this.getDDHandles(),
33805                 isHandle: false
33806             });
33807         }
33808     },
33809
33810     getDDHandles : function(){
33811         return [this.iconNode, this.textNode];
33812     },
33813
33814     hide : function(){
33815         if(this.rendered){
33816             this.wrap.style.display = "none";
33817         }
33818     },
33819
33820     show : function(){
33821         if(this.rendered){
33822             this.wrap.style.display = "";
33823         }
33824     },
33825
33826     onContextMenu : function(e){
33827         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33828             e.preventDefault();
33829             this.focus();
33830             this.fireEvent("contextmenu", this.node, e);
33831         }
33832     },
33833
33834     onClick : function(e){
33835         if(this.dropping){
33836             e.stopEvent();
33837             return;
33838         }
33839         if(this.fireEvent("beforeclick", this.node, e) !== false){
33840             if(!this.disabled && this.node.attributes.href){
33841                 this.fireEvent("click", this.node, e);
33842                 return;
33843             }
33844             e.preventDefault();
33845             if(this.disabled){
33846                 return;
33847             }
33848
33849             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33850                 this.node.toggle();
33851             }
33852
33853             this.fireEvent("click", this.node, e);
33854         }else{
33855             e.stopEvent();
33856         }
33857     },
33858
33859     onDblClick : function(e){
33860         e.preventDefault();
33861         if(this.disabled){
33862             return;
33863         }
33864         if(this.checkbox){
33865             this.toggleCheck();
33866         }
33867         if(!this.animating && this.node.hasChildNodes()){
33868             this.node.toggle();
33869         }
33870         this.fireEvent("dblclick", this.node, e);
33871     },
33872
33873     onCheckChange : function(){
33874         var checked = this.checkbox.checked;
33875         this.node.attributes.checked = checked;
33876         this.fireEvent('checkchange', this.node, checked);
33877     },
33878
33879     ecClick : function(e){
33880         if(!this.animating && this.node.hasChildNodes()){
33881             this.node.toggle();
33882         }
33883     },
33884
33885     startDrop : function(){
33886         this.dropping = true;
33887     },
33888
33889     // delayed drop so the click event doesn't get fired on a drop
33890     endDrop : function(){
33891        setTimeout(function(){
33892            this.dropping = false;
33893        }.createDelegate(this), 50);
33894     },
33895
33896     expand : function(){
33897         this.updateExpandIcon();
33898         this.ctNode.style.display = "";
33899     },
33900
33901     focus : function(){
33902         if(!this.node.preventHScroll){
33903             try{this.anchor.focus();
33904             }catch(e){}
33905         }else if(!Roo.isIE){
33906             try{
33907                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33908                 var l = noscroll.scrollLeft;
33909                 this.anchor.focus();
33910                 noscroll.scrollLeft = l;
33911             }catch(e){}
33912         }
33913     },
33914
33915     toggleCheck : function(value){
33916         var cb = this.checkbox;
33917         if(cb){
33918             cb.checked = (value === undefined ? !cb.checked : value);
33919         }
33920     },
33921
33922     blur : function(){
33923         try{
33924             this.anchor.blur();
33925         }catch(e){}
33926     },
33927
33928     animExpand : function(callback){
33929         var ct = Roo.get(this.ctNode);
33930         ct.stopFx();
33931         if(!this.node.hasChildNodes()){
33932             this.updateExpandIcon();
33933             this.ctNode.style.display = "";
33934             Roo.callback(callback);
33935             return;
33936         }
33937         this.animating = true;
33938         this.updateExpandIcon();
33939
33940         ct.slideIn('t', {
33941            callback : function(){
33942                this.animating = false;
33943                Roo.callback(callback);
33944             },
33945             scope: this,
33946             duration: this.node.ownerTree.duration || .25
33947         });
33948     },
33949
33950     highlight : function(){
33951         var tree = this.node.getOwnerTree();
33952         Roo.fly(this.wrap).highlight(
33953             tree.hlColor || "C3DAF9",
33954             {endColor: tree.hlBaseColor}
33955         );
33956     },
33957
33958     collapse : function(){
33959         this.updateExpandIcon();
33960         this.ctNode.style.display = "none";
33961     },
33962
33963     animCollapse : function(callback){
33964         var ct = Roo.get(this.ctNode);
33965         ct.enableDisplayMode('block');
33966         ct.stopFx();
33967
33968         this.animating = true;
33969         this.updateExpandIcon();
33970
33971         ct.slideOut('t', {
33972             callback : function(){
33973                this.animating = false;
33974                Roo.callback(callback);
33975             },
33976             scope: this,
33977             duration: this.node.ownerTree.duration || .25
33978         });
33979     },
33980
33981     getContainer : function(){
33982         return this.ctNode;
33983     },
33984
33985     getEl : function(){
33986         return this.wrap;
33987     },
33988
33989     appendDDGhost : function(ghostNode){
33990         ghostNode.appendChild(this.elNode.cloneNode(true));
33991     },
33992
33993     getDDRepairXY : function(){
33994         return Roo.lib.Dom.getXY(this.iconNode);
33995     },
33996
33997     onRender : function(){
33998         this.render();
33999     },
34000
34001     render : function(bulkRender){
34002         var n = this.node, a = n.attributes;
34003         var targetNode = n.parentNode ?
34004               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34005
34006         if(!this.rendered){
34007             this.rendered = true;
34008
34009             this.renderElements(n, a, targetNode, bulkRender);
34010
34011             if(a.qtip){
34012                if(this.textNode.setAttributeNS){
34013                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34014                    if(a.qtipTitle){
34015                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34016                    }
34017                }else{
34018                    this.textNode.setAttribute("ext:qtip", a.qtip);
34019                    if(a.qtipTitle){
34020                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34021                    }
34022                }
34023             }else if(a.qtipCfg){
34024                 a.qtipCfg.target = Roo.id(this.textNode);
34025                 Roo.QuickTips.register(a.qtipCfg);
34026             }
34027             this.initEvents();
34028             if(!this.node.expanded){
34029                 this.updateExpandIcon();
34030             }
34031         }else{
34032             if(bulkRender === true) {
34033                 targetNode.appendChild(this.wrap);
34034             }
34035         }
34036     },
34037
34038     renderElements : function(n, a, targetNode, bulkRender)
34039     {
34040         // add some indent caching, this helps performance when rendering a large tree
34041         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34042         var t = n.getOwnerTree();
34043         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34044         if (typeof(n.attributes.html) != 'undefined') {
34045             txt = n.attributes.html;
34046         }
34047         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34048         var cb = typeof a.checked == 'boolean';
34049         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34050         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34051             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34052             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34053             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34054             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34055             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34056              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34057                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34058             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34059             "</li>"];
34060
34061         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34062             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34063                                 n.nextSibling.ui.getEl(), buf.join(""));
34064         }else{
34065             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34066         }
34067
34068         this.elNode = this.wrap.childNodes[0];
34069         this.ctNode = this.wrap.childNodes[1];
34070         var cs = this.elNode.childNodes;
34071         this.indentNode = cs[0];
34072         this.ecNode = cs[1];
34073         this.iconNode = cs[2];
34074         var index = 3;
34075         if(cb){
34076             this.checkbox = cs[3];
34077             index++;
34078         }
34079         this.anchor = cs[index];
34080         this.textNode = cs[index].firstChild;
34081     },
34082
34083     getAnchor : function(){
34084         return this.anchor;
34085     },
34086
34087     getTextEl : function(){
34088         return this.textNode;
34089     },
34090
34091     getIconEl : function(){
34092         return this.iconNode;
34093     },
34094
34095     isChecked : function(){
34096         return this.checkbox ? this.checkbox.checked : false;
34097     },
34098
34099     updateExpandIcon : function(){
34100         if(this.rendered){
34101             var n = this.node, c1, c2;
34102             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34103             var hasChild = n.hasChildNodes();
34104             if(hasChild){
34105                 if(n.expanded){
34106                     cls += "-minus";
34107                     c1 = "x-tree-node-collapsed";
34108                     c2 = "x-tree-node-expanded";
34109                 }else{
34110                     cls += "-plus";
34111                     c1 = "x-tree-node-expanded";
34112                     c2 = "x-tree-node-collapsed";
34113                 }
34114                 if(this.wasLeaf){
34115                     this.removeClass("x-tree-node-leaf");
34116                     this.wasLeaf = false;
34117                 }
34118                 if(this.c1 != c1 || this.c2 != c2){
34119                     Roo.fly(this.elNode).replaceClass(c1, c2);
34120                     this.c1 = c1; this.c2 = c2;
34121                 }
34122             }else{
34123                 // this changes non-leafs into leafs if they have no children.
34124                 // it's not very rational behaviour..
34125                 
34126                 if(!this.wasLeaf && this.node.leaf){
34127                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34128                     delete this.c1;
34129                     delete this.c2;
34130                     this.wasLeaf = true;
34131                 }
34132             }
34133             var ecc = "x-tree-ec-icon "+cls;
34134             if(this.ecc != ecc){
34135                 this.ecNode.className = ecc;
34136                 this.ecc = ecc;
34137             }
34138         }
34139     },
34140
34141     getChildIndent : function(){
34142         if(!this.childIndent){
34143             var buf = [];
34144             var p = this.node;
34145             while(p){
34146                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34147                     if(!p.isLast()) {
34148                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34149                     } else {
34150                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34151                     }
34152                 }
34153                 p = p.parentNode;
34154             }
34155             this.childIndent = buf.join("");
34156         }
34157         return this.childIndent;
34158     },
34159
34160     renderIndent : function(){
34161         if(this.rendered){
34162             var indent = "";
34163             var p = this.node.parentNode;
34164             if(p){
34165                 indent = p.ui.getChildIndent();
34166             }
34167             if(this.indentMarkup != indent){ // don't rerender if not required
34168                 this.indentNode.innerHTML = indent;
34169                 this.indentMarkup = indent;
34170             }
34171             this.updateExpandIcon();
34172         }
34173     }
34174 };
34175
34176 Roo.tree.RootTreeNodeUI = function(){
34177     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34178 };
34179 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34180     render : function(){
34181         if(!this.rendered){
34182             var targetNode = this.node.ownerTree.innerCt.dom;
34183             this.node.expanded = true;
34184             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34185             this.wrap = this.ctNode = targetNode.firstChild;
34186         }
34187     },
34188     collapse : function(){
34189     },
34190     expand : function(){
34191     }
34192 });/*
34193  * Based on:
34194  * Ext JS Library 1.1.1
34195  * Copyright(c) 2006-2007, Ext JS, LLC.
34196  *
34197  * Originally Released Under LGPL - original licence link has changed is not relivant.
34198  *
34199  * Fork - LGPL
34200  * <script type="text/javascript">
34201  */
34202 /**
34203  * @class Roo.tree.TreeLoader
34204  * @extends Roo.util.Observable
34205  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34206  * nodes from a specified URL. The response must be a javascript Array definition
34207  * who's elements are node definition objects. eg:
34208  * <pre><code>
34209 {  success : true,
34210    data :      [
34211    
34212     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34213     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34214     ]
34215 }
34216
34217
34218 </code></pre>
34219  * <br><br>
34220  * The old style respose with just an array is still supported, but not recommended.
34221  * <br><br>
34222  *
34223  * A server request is sent, and child nodes are loaded only when a node is expanded.
34224  * The loading node's id is passed to the server under the parameter name "node" to
34225  * enable the server to produce the correct child nodes.
34226  * <br><br>
34227  * To pass extra parameters, an event handler may be attached to the "beforeload"
34228  * event, and the parameters specified in the TreeLoader's baseParams property:
34229  * <pre><code>
34230     myTreeLoader.on("beforeload", function(treeLoader, node) {
34231         this.baseParams.category = node.attributes.category;
34232     }, this);
34233 </code></pre><
34234  * This would pass an HTTP parameter called "category" to the server containing
34235  * the value of the Node's "category" attribute.
34236  * @constructor
34237  * Creates a new Treeloader.
34238  * @param {Object} config A config object containing config properties.
34239  */
34240 Roo.tree.TreeLoader = function(config){
34241     this.baseParams = {};
34242     this.requestMethod = "POST";
34243     Roo.apply(this, config);
34244
34245     this.addEvents({
34246     
34247         /**
34248          * @event beforeload
34249          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34250          * @param {Object} This TreeLoader object.
34251          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34252          * @param {Object} callback The callback function specified in the {@link #load} call.
34253          */
34254         beforeload : true,
34255         /**
34256          * @event load
34257          * Fires when the node has been successfuly loaded.
34258          * @param {Object} This TreeLoader object.
34259          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34260          * @param {Object} response The response object containing the data from the server.
34261          */
34262         load : true,
34263         /**
34264          * @event loadexception
34265          * Fires if the network request failed.
34266          * @param {Object} This TreeLoader object.
34267          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34268          * @param {Object} response The response object containing the data from the server.
34269          */
34270         loadexception : true,
34271         /**
34272          * @event create
34273          * Fires before a node is created, enabling you to return custom Node types 
34274          * @param {Object} This TreeLoader object.
34275          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34276          */
34277         create : true
34278     });
34279
34280     Roo.tree.TreeLoader.superclass.constructor.call(this);
34281 };
34282
34283 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34284     /**
34285     * @cfg {String} dataUrl The URL from which to request a Json string which
34286     * specifies an array of node definition object representing the child nodes
34287     * to be loaded.
34288     */
34289     /**
34290     * @cfg {String} requestMethod either GET or POST
34291     * defaults to POST (due to BC)
34292     * to be loaded.
34293     */
34294     /**
34295     * @cfg {Object} baseParams (optional) An object containing properties which
34296     * specify HTTP parameters to be passed to each request for child nodes.
34297     */
34298     /**
34299     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34300     * created by this loader. If the attributes sent by the server have an attribute in this object,
34301     * they take priority.
34302     */
34303     /**
34304     * @cfg {Object} uiProviders (optional) An object containing properties which
34305     * 
34306     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34307     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34308     * <i>uiProvider</i> attribute of a returned child node is a string rather
34309     * than a reference to a TreeNodeUI implementation, this that string value
34310     * is used as a property name in the uiProviders object. You can define the provider named
34311     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34312     */
34313     uiProviders : {},
34314
34315     /**
34316     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34317     * child nodes before loading.
34318     */
34319     clearOnLoad : true,
34320
34321     /**
34322     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34323     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34324     * Grid query { data : [ .....] }
34325     */
34326     
34327     root : false,
34328      /**
34329     * @cfg {String} queryParam (optional) 
34330     * Name of the query as it will be passed on the querystring (defaults to 'node')
34331     * eg. the request will be ?node=[id]
34332     */
34333     
34334     
34335     queryParam: false,
34336     
34337     /**
34338      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34339      * This is called automatically when a node is expanded, but may be used to reload
34340      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34341      * @param {Roo.tree.TreeNode} node
34342      * @param {Function} callback
34343      */
34344     load : function(node, callback){
34345         if(this.clearOnLoad){
34346             while(node.firstChild){
34347                 node.removeChild(node.firstChild);
34348             }
34349         }
34350         if(node.attributes.children){ // preloaded json children
34351             var cs = node.attributes.children;
34352             for(var i = 0, len = cs.length; i < len; i++){
34353                 node.appendChild(this.createNode(cs[i]));
34354             }
34355             if(typeof callback == "function"){
34356                 callback();
34357             }
34358         }else if(this.dataUrl){
34359             this.requestData(node, callback);
34360         }
34361     },
34362
34363     getParams: function(node){
34364         var buf = [], bp = this.baseParams;
34365         for(var key in bp){
34366             if(typeof bp[key] != "function"){
34367                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34368             }
34369         }
34370         var n = this.queryParam === false ? 'node' : this.queryParam;
34371         buf.push(n + "=", encodeURIComponent(node.id));
34372         return buf.join("");
34373     },
34374
34375     requestData : function(node, callback){
34376         if(this.fireEvent("beforeload", this, node, callback) !== false){
34377             this.transId = Roo.Ajax.request({
34378                 method:this.requestMethod,
34379                 url: this.dataUrl||this.url,
34380                 success: this.handleResponse,
34381                 failure: this.handleFailure,
34382                 scope: this,
34383                 argument: {callback: callback, node: node},
34384                 params: this.getParams(node)
34385             });
34386         }else{
34387             // if the load is cancelled, make sure we notify
34388             // the node that we are done
34389             if(typeof callback == "function"){
34390                 callback();
34391             }
34392         }
34393     },
34394
34395     isLoading : function(){
34396         return this.transId ? true : false;
34397     },
34398
34399     abort : function(){
34400         if(this.isLoading()){
34401             Roo.Ajax.abort(this.transId);
34402         }
34403     },
34404
34405     // private
34406     createNode : function(attr)
34407     {
34408         // apply baseAttrs, nice idea Corey!
34409         if(this.baseAttrs){
34410             Roo.applyIf(attr, this.baseAttrs);
34411         }
34412         if(this.applyLoader !== false){
34413             attr.loader = this;
34414         }
34415         // uiProvider = depreciated..
34416         
34417         if(typeof(attr.uiProvider) == 'string'){
34418            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34419                 /**  eval:var:attr */ eval(attr.uiProvider);
34420         }
34421         if(typeof(this.uiProviders['default']) != 'undefined') {
34422             attr.uiProvider = this.uiProviders['default'];
34423         }
34424         
34425         this.fireEvent('create', this, attr);
34426         
34427         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34428         return(attr.leaf ?
34429                         new Roo.tree.TreeNode(attr) :
34430                         new Roo.tree.AsyncTreeNode(attr));
34431     },
34432
34433     processResponse : function(response, node, callback)
34434     {
34435         var json = response.responseText;
34436         try {
34437             
34438             var o = Roo.decode(json);
34439             
34440             if (this.root === false && typeof(o.success) != undefined) {
34441                 this.root = 'data'; // the default behaviour for list like data..
34442                 }
34443                 
34444             if (this.root !== false &&  !o.success) {
34445                 // it's a failure condition.
34446                 var a = response.argument;
34447                 this.fireEvent("loadexception", this, a.node, response);
34448                 Roo.log("Load failed - should have a handler really");
34449                 return;
34450             }
34451             
34452             
34453             
34454             if (this.root !== false) {
34455                  o = o[this.root];
34456             }
34457             
34458             for(var i = 0, len = o.length; i < len; i++){
34459                 var n = this.createNode(o[i]);
34460                 if(n){
34461                     node.appendChild(n);
34462                 }
34463             }
34464             if(typeof callback == "function"){
34465                 callback(this, node);
34466             }
34467         }catch(e){
34468             this.handleFailure(response);
34469         }
34470     },
34471
34472     handleResponse : function(response){
34473         this.transId = false;
34474         var a = response.argument;
34475         this.processResponse(response, a.node, a.callback);
34476         this.fireEvent("load", this, a.node, response);
34477     },
34478
34479     handleFailure : function(response)
34480     {
34481         // should handle failure better..
34482         this.transId = false;
34483         var a = response.argument;
34484         this.fireEvent("loadexception", this, a.node, response);
34485         if(typeof a.callback == "function"){
34486             a.callback(this, a.node);
34487         }
34488     }
34489 });/*
34490  * Based on:
34491  * Ext JS Library 1.1.1
34492  * Copyright(c) 2006-2007, Ext JS, LLC.
34493  *
34494  * Originally Released Under LGPL - original licence link has changed is not relivant.
34495  *
34496  * Fork - LGPL
34497  * <script type="text/javascript">
34498  */
34499
34500 /**
34501 * @class Roo.tree.TreeFilter
34502 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34503 * @param {TreePanel} tree
34504 * @param {Object} config (optional)
34505  */
34506 Roo.tree.TreeFilter = function(tree, config){
34507     this.tree = tree;
34508     this.filtered = {};
34509     Roo.apply(this, config);
34510 };
34511
34512 Roo.tree.TreeFilter.prototype = {
34513     clearBlank:false,
34514     reverse:false,
34515     autoClear:false,
34516     remove:false,
34517
34518      /**
34519      * Filter the data by a specific attribute.
34520      * @param {String/RegExp} value Either string that the attribute value
34521      * should start with or a RegExp to test against the attribute
34522      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34523      * @param {TreeNode} startNode (optional) The node to start the filter at.
34524      */
34525     filter : function(value, attr, startNode){
34526         attr = attr || "text";
34527         var f;
34528         if(typeof value == "string"){
34529             var vlen = value.length;
34530             // auto clear empty filter
34531             if(vlen == 0 && this.clearBlank){
34532                 this.clear();
34533                 return;
34534             }
34535             value = value.toLowerCase();
34536             f = function(n){
34537                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34538             };
34539         }else if(value.exec){ // regex?
34540             f = function(n){
34541                 return value.test(n.attributes[attr]);
34542             };
34543         }else{
34544             throw 'Illegal filter type, must be string or regex';
34545         }
34546         this.filterBy(f, null, startNode);
34547         },
34548
34549     /**
34550      * Filter by a function. The passed function will be called with each
34551      * node in the tree (or from the startNode). If the function returns true, the node is kept
34552      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34553      * @param {Function} fn The filter function
34554      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34555      */
34556     filterBy : function(fn, scope, startNode){
34557         startNode = startNode || this.tree.root;
34558         if(this.autoClear){
34559             this.clear();
34560         }
34561         var af = this.filtered, rv = this.reverse;
34562         var f = function(n){
34563             if(n == startNode){
34564                 return true;
34565             }
34566             if(af[n.id]){
34567                 return false;
34568             }
34569             var m = fn.call(scope || n, n);
34570             if(!m || rv){
34571                 af[n.id] = n;
34572                 n.ui.hide();
34573                 return false;
34574             }
34575             return true;
34576         };
34577         startNode.cascade(f);
34578         if(this.remove){
34579            for(var id in af){
34580                if(typeof id != "function"){
34581                    var n = af[id];
34582                    if(n && n.parentNode){
34583                        n.parentNode.removeChild(n);
34584                    }
34585                }
34586            }
34587         }
34588     },
34589
34590     /**
34591      * Clears the current filter. Note: with the "remove" option
34592      * set a filter cannot be cleared.
34593      */
34594     clear : function(){
34595         var t = this.tree;
34596         var af = this.filtered;
34597         for(var id in af){
34598             if(typeof id != "function"){
34599                 var n = af[id];
34600                 if(n){
34601                     n.ui.show();
34602                 }
34603             }
34604         }
34605         this.filtered = {};
34606     }
34607 };
34608 /*
34609  * Based on:
34610  * Ext JS Library 1.1.1
34611  * Copyright(c) 2006-2007, Ext JS, LLC.
34612  *
34613  * Originally Released Under LGPL - original licence link has changed is not relivant.
34614  *
34615  * Fork - LGPL
34616  * <script type="text/javascript">
34617  */
34618  
34619
34620 /**
34621  * @class Roo.tree.TreeSorter
34622  * Provides sorting of nodes in a TreePanel
34623  * 
34624  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34625  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34626  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34627  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34628  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34629  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34630  * @constructor
34631  * @param {TreePanel} tree
34632  * @param {Object} config
34633  */
34634 Roo.tree.TreeSorter = function(tree, config){
34635     Roo.apply(this, config);
34636     tree.on("beforechildrenrendered", this.doSort, this);
34637     tree.on("append", this.updateSort, this);
34638     tree.on("insert", this.updateSort, this);
34639     
34640     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34641     var p = this.property || "text";
34642     var sortType = this.sortType;
34643     var fs = this.folderSort;
34644     var cs = this.caseSensitive === true;
34645     var leafAttr = this.leafAttr || 'leaf';
34646
34647     this.sortFn = function(n1, n2){
34648         if(fs){
34649             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34650                 return 1;
34651             }
34652             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34653                 return -1;
34654             }
34655         }
34656         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34657         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34658         if(v1 < v2){
34659                         return dsc ? +1 : -1;
34660                 }else if(v1 > v2){
34661                         return dsc ? -1 : +1;
34662         }else{
34663                 return 0;
34664         }
34665     };
34666 };
34667
34668 Roo.tree.TreeSorter.prototype = {
34669     doSort : function(node){
34670         node.sort(this.sortFn);
34671     },
34672     
34673     compareNodes : function(n1, n2){
34674         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34675     },
34676     
34677     updateSort : function(tree, node){
34678         if(node.childrenRendered){
34679             this.doSort.defer(1, this, [node]);
34680         }
34681     }
34682 };/*
34683  * Based on:
34684  * Ext JS Library 1.1.1
34685  * Copyright(c) 2006-2007, Ext JS, LLC.
34686  *
34687  * Originally Released Under LGPL - original licence link has changed is not relivant.
34688  *
34689  * Fork - LGPL
34690  * <script type="text/javascript">
34691  */
34692
34693 if(Roo.dd.DropZone){
34694     
34695 Roo.tree.TreeDropZone = function(tree, config){
34696     this.allowParentInsert = false;
34697     this.allowContainerDrop = false;
34698     this.appendOnly = false;
34699     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34700     this.tree = tree;
34701     this.lastInsertClass = "x-tree-no-status";
34702     this.dragOverData = {};
34703 };
34704
34705 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34706     ddGroup : "TreeDD",
34707     scroll:  true,
34708     
34709     expandDelay : 1000,
34710     
34711     expandNode : function(node){
34712         if(node.hasChildNodes() && !node.isExpanded()){
34713             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34714         }
34715     },
34716     
34717     queueExpand : function(node){
34718         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34719     },
34720     
34721     cancelExpand : function(){
34722         if(this.expandProcId){
34723             clearTimeout(this.expandProcId);
34724             this.expandProcId = false;
34725         }
34726     },
34727     
34728     isValidDropPoint : function(n, pt, dd, e, data){
34729         if(!n || !data){ return false; }
34730         var targetNode = n.node;
34731         var dropNode = data.node;
34732         // default drop rules
34733         if(!(targetNode && targetNode.isTarget && pt)){
34734             return false;
34735         }
34736         if(pt == "append" && targetNode.allowChildren === false){
34737             return false;
34738         }
34739         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34740             return false;
34741         }
34742         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34743             return false;
34744         }
34745         // reuse the object
34746         var overEvent = this.dragOverData;
34747         overEvent.tree = this.tree;
34748         overEvent.target = targetNode;
34749         overEvent.data = data;
34750         overEvent.point = pt;
34751         overEvent.source = dd;
34752         overEvent.rawEvent = e;
34753         overEvent.dropNode = dropNode;
34754         overEvent.cancel = false;  
34755         var result = this.tree.fireEvent("nodedragover", overEvent);
34756         return overEvent.cancel === false && result !== false;
34757     },
34758     
34759     getDropPoint : function(e, n, dd)
34760     {
34761         var tn = n.node;
34762         if(tn.isRoot){
34763             return tn.allowChildren !== false ? "append" : false; // always append for root
34764         }
34765         var dragEl = n.ddel;
34766         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34767         var y = Roo.lib.Event.getPageY(e);
34768         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34769         
34770         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34771         var noAppend = tn.allowChildren === false;
34772         if(this.appendOnly || tn.parentNode.allowChildren === false){
34773             return noAppend ? false : "append";
34774         }
34775         var noBelow = false;
34776         if(!this.allowParentInsert){
34777             noBelow = tn.hasChildNodes() && tn.isExpanded();
34778         }
34779         var q = (b - t) / (noAppend ? 2 : 3);
34780         if(y >= t && y < (t + q)){
34781             return "above";
34782         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34783             return "below";
34784         }else{
34785             return "append";
34786         }
34787     },
34788     
34789     onNodeEnter : function(n, dd, e, data)
34790     {
34791         this.cancelExpand();
34792     },
34793     
34794     onNodeOver : function(n, dd, e, data)
34795     {
34796        
34797         var pt = this.getDropPoint(e, n, dd);
34798         var node = n.node;
34799         
34800         // auto node expand check
34801         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34802             this.queueExpand(node);
34803         }else if(pt != "append"){
34804             this.cancelExpand();
34805         }
34806         
34807         // set the insert point style on the target node
34808         var returnCls = this.dropNotAllowed;
34809         if(this.isValidDropPoint(n, pt, dd, e, data)){
34810            if(pt){
34811                var el = n.ddel;
34812                var cls;
34813                if(pt == "above"){
34814                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34815                    cls = "x-tree-drag-insert-above";
34816                }else if(pt == "below"){
34817                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34818                    cls = "x-tree-drag-insert-below";
34819                }else{
34820                    returnCls = "x-tree-drop-ok-append";
34821                    cls = "x-tree-drag-append";
34822                }
34823                if(this.lastInsertClass != cls){
34824                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34825                    this.lastInsertClass = cls;
34826                }
34827            }
34828        }
34829        return returnCls;
34830     },
34831     
34832     onNodeOut : function(n, dd, e, data){
34833         
34834         this.cancelExpand();
34835         this.removeDropIndicators(n);
34836     },
34837     
34838     onNodeDrop : function(n, dd, e, data){
34839         var point = this.getDropPoint(e, n, dd);
34840         var targetNode = n.node;
34841         targetNode.ui.startDrop();
34842         if(!this.isValidDropPoint(n, point, dd, e, data)){
34843             targetNode.ui.endDrop();
34844             return false;
34845         }
34846         // first try to find the drop node
34847         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34848         var dropEvent = {
34849             tree : this.tree,
34850             target: targetNode,
34851             data: data,
34852             point: point,
34853             source: dd,
34854             rawEvent: e,
34855             dropNode: dropNode,
34856             cancel: !dropNode   
34857         };
34858         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34859         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34860             targetNode.ui.endDrop();
34861             return false;
34862         }
34863         // allow target changing
34864         targetNode = dropEvent.target;
34865         if(point == "append" && !targetNode.isExpanded()){
34866             targetNode.expand(false, null, function(){
34867                 this.completeDrop(dropEvent);
34868             }.createDelegate(this));
34869         }else{
34870             this.completeDrop(dropEvent);
34871         }
34872         return true;
34873     },
34874     
34875     completeDrop : function(de){
34876         var ns = de.dropNode, p = de.point, t = de.target;
34877         if(!(ns instanceof Array)){
34878             ns = [ns];
34879         }
34880         var n;
34881         for(var i = 0, len = ns.length; i < len; i++){
34882             n = ns[i];
34883             if(p == "above"){
34884                 t.parentNode.insertBefore(n, t);
34885             }else if(p == "below"){
34886                 t.parentNode.insertBefore(n, t.nextSibling);
34887             }else{
34888                 t.appendChild(n);
34889             }
34890         }
34891         n.ui.focus();
34892         if(this.tree.hlDrop){
34893             n.ui.highlight();
34894         }
34895         t.ui.endDrop();
34896         this.tree.fireEvent("nodedrop", de);
34897     },
34898     
34899     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34900         if(this.tree.hlDrop){
34901             dropNode.ui.focus();
34902             dropNode.ui.highlight();
34903         }
34904         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34905     },
34906     
34907     getTree : function(){
34908         return this.tree;
34909     },
34910     
34911     removeDropIndicators : function(n){
34912         if(n && n.ddel){
34913             var el = n.ddel;
34914             Roo.fly(el).removeClass([
34915                     "x-tree-drag-insert-above",
34916                     "x-tree-drag-insert-below",
34917                     "x-tree-drag-append"]);
34918             this.lastInsertClass = "_noclass";
34919         }
34920     },
34921     
34922     beforeDragDrop : function(target, e, id){
34923         this.cancelExpand();
34924         return true;
34925     },
34926     
34927     afterRepair : function(data){
34928         if(data && Roo.enableFx){
34929             data.node.ui.highlight();
34930         }
34931         this.hideProxy();
34932     } 
34933     
34934 });
34935
34936 }
34937 /*
34938  * Based on:
34939  * Ext JS Library 1.1.1
34940  * Copyright(c) 2006-2007, Ext JS, LLC.
34941  *
34942  * Originally Released Under LGPL - original licence link has changed is not relivant.
34943  *
34944  * Fork - LGPL
34945  * <script type="text/javascript">
34946  */
34947  
34948
34949 if(Roo.dd.DragZone){
34950 Roo.tree.TreeDragZone = function(tree, config){
34951     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34952     this.tree = tree;
34953 };
34954
34955 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34956     ddGroup : "TreeDD",
34957    
34958     onBeforeDrag : function(data, e){
34959         var n = data.node;
34960         return n && n.draggable && !n.disabled;
34961     },
34962      
34963     
34964     onInitDrag : function(e){
34965         var data = this.dragData;
34966         this.tree.getSelectionModel().select(data.node);
34967         this.proxy.update("");
34968         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34969         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34970     },
34971     
34972     getRepairXY : function(e, data){
34973         return data.node.ui.getDDRepairXY();
34974     },
34975     
34976     onEndDrag : function(data, e){
34977         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34978         
34979         
34980     },
34981     
34982     onValidDrop : function(dd, e, id){
34983         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34984         this.hideProxy();
34985     },
34986     
34987     beforeInvalidDrop : function(e, id){
34988         // this scrolls the original position back into view
34989         var sm = this.tree.getSelectionModel();
34990         sm.clearSelections();
34991         sm.select(this.dragData.node);
34992     }
34993 });
34994 }/*
34995  * Based on:
34996  * Ext JS Library 1.1.1
34997  * Copyright(c) 2006-2007, Ext JS, LLC.
34998  *
34999  * Originally Released Under LGPL - original licence link has changed is not relivant.
35000  *
35001  * Fork - LGPL
35002  * <script type="text/javascript">
35003  */
35004 /**
35005  * @class Roo.tree.TreeEditor
35006  * @extends Roo.Editor
35007  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35008  * as the editor field.
35009  * @constructor
35010  * @param {Object} config (used to be the tree panel.)
35011  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35012  * 
35013  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35014  * @cfg {Roo.form.TextField|Object} field The field configuration
35015  *
35016  * 
35017  */
35018 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35019     var tree = config;
35020     var field;
35021     if (oldconfig) { // old style..
35022         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35023     } else {
35024         // new style..
35025         tree = config.tree;
35026         config.field = config.field  || {};
35027         config.field.xtype = 'TextField';
35028         field = Roo.factory(config.field, Roo.form);
35029     }
35030     config = config || {};
35031     
35032     
35033     this.addEvents({
35034         /**
35035          * @event beforenodeedit
35036          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35037          * false from the handler of this event.
35038          * @param {Editor} this
35039          * @param {Roo.tree.Node} node 
35040          */
35041         "beforenodeedit" : true
35042     });
35043     
35044     //Roo.log(config);
35045     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35046
35047     this.tree = tree;
35048
35049     tree.on('beforeclick', this.beforeNodeClick, this);
35050     tree.getTreeEl().on('mousedown', this.hide, this);
35051     this.on('complete', this.updateNode, this);
35052     this.on('beforestartedit', this.fitToTree, this);
35053     this.on('startedit', this.bindScroll, this, {delay:10});
35054     this.on('specialkey', this.onSpecialKey, this);
35055 };
35056
35057 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35058     /**
35059      * @cfg {String} alignment
35060      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35061      */
35062     alignment: "l-l",
35063     // inherit
35064     autoSize: false,
35065     /**
35066      * @cfg {Boolean} hideEl
35067      * True to hide the bound element while the editor is displayed (defaults to false)
35068      */
35069     hideEl : false,
35070     /**
35071      * @cfg {String} cls
35072      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35073      */
35074     cls: "x-small-editor x-tree-editor",
35075     /**
35076      * @cfg {Boolean} shim
35077      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35078      */
35079     shim:false,
35080     // inherit
35081     shadow:"frame",
35082     /**
35083      * @cfg {Number} maxWidth
35084      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35085      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35086      * scroll and client offsets into account prior to each edit.
35087      */
35088     maxWidth: 250,
35089
35090     editDelay : 350,
35091
35092     // private
35093     fitToTree : function(ed, el){
35094         var td = this.tree.getTreeEl().dom, nd = el.dom;
35095         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35096             td.scrollLeft = nd.offsetLeft;
35097         }
35098         var w = Math.min(
35099                 this.maxWidth,
35100                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35101         this.setSize(w, '');
35102         
35103         return this.fireEvent('beforenodeedit', this, this.editNode);
35104         
35105     },
35106
35107     // private
35108     triggerEdit : function(node){
35109         this.completeEdit();
35110         this.editNode = node;
35111         this.startEdit(node.ui.textNode, node.text);
35112     },
35113
35114     // private
35115     bindScroll : function(){
35116         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35117     },
35118
35119     // private
35120     beforeNodeClick : function(node, e){
35121         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35122         this.lastClick = new Date();
35123         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35124             e.stopEvent();
35125             this.triggerEdit(node);
35126             return false;
35127         }
35128         return true;
35129     },
35130
35131     // private
35132     updateNode : function(ed, value){
35133         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35134         this.editNode.setText(value);
35135     },
35136
35137     // private
35138     onHide : function(){
35139         Roo.tree.TreeEditor.superclass.onHide.call(this);
35140         if(this.editNode){
35141             this.editNode.ui.focus();
35142         }
35143     },
35144
35145     // private
35146     onSpecialKey : function(field, e){
35147         var k = e.getKey();
35148         if(k == e.ESC){
35149             e.stopEvent();
35150             this.cancelEdit();
35151         }else if(k == e.ENTER && !e.hasModifier()){
35152             e.stopEvent();
35153             this.completeEdit();
35154         }
35155     }
35156 });//<Script type="text/javascript">
35157 /*
35158  * Based on:
35159  * Ext JS Library 1.1.1
35160  * Copyright(c) 2006-2007, Ext JS, LLC.
35161  *
35162  * Originally Released Under LGPL - original licence link has changed is not relivant.
35163  *
35164  * Fork - LGPL
35165  * <script type="text/javascript">
35166  */
35167  
35168 /**
35169  * Not documented??? - probably should be...
35170  */
35171
35172 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35173     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35174     
35175     renderElements : function(n, a, targetNode, bulkRender){
35176         //consel.log("renderElements?");
35177         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35178
35179         var t = n.getOwnerTree();
35180         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35181         
35182         var cols = t.columns;
35183         var bw = t.borderWidth;
35184         var c = cols[0];
35185         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35186          var cb = typeof a.checked == "boolean";
35187         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35188         var colcls = 'x-t-' + tid + '-c0';
35189         var buf = [
35190             '<li class="x-tree-node">',
35191             
35192                 
35193                 '<div class="x-tree-node-el ', a.cls,'">',
35194                     // extran...
35195                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35196                 
35197                 
35198                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35199                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35200                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35201                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35202                            (a.iconCls ? ' '+a.iconCls : ''),
35203                            '" unselectable="on" />',
35204                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35205                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35206                              
35207                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35208                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35209                             '<span unselectable="on" qtip="' + tx + '">',
35210                              tx,
35211                              '</span></a>' ,
35212                     '</div>',
35213                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35214                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35215                  ];
35216         for(var i = 1, len = cols.length; i < len; i++){
35217             c = cols[i];
35218             colcls = 'x-t-' + tid + '-c' +i;
35219             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35220             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35221                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35222                       "</div>");
35223          }
35224          
35225          buf.push(
35226             '</a>',
35227             '<div class="x-clear"></div></div>',
35228             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35229             "</li>");
35230         
35231         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35232             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35233                                 n.nextSibling.ui.getEl(), buf.join(""));
35234         }else{
35235             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35236         }
35237         var el = this.wrap.firstChild;
35238         this.elRow = el;
35239         this.elNode = el.firstChild;
35240         this.ranchor = el.childNodes[1];
35241         this.ctNode = this.wrap.childNodes[1];
35242         var cs = el.firstChild.childNodes;
35243         this.indentNode = cs[0];
35244         this.ecNode = cs[1];
35245         this.iconNode = cs[2];
35246         var index = 3;
35247         if(cb){
35248             this.checkbox = cs[3];
35249             index++;
35250         }
35251         this.anchor = cs[index];
35252         
35253         this.textNode = cs[index].firstChild;
35254         
35255         //el.on("click", this.onClick, this);
35256         //el.on("dblclick", this.onDblClick, this);
35257         
35258         
35259        // console.log(this);
35260     },
35261     initEvents : function(){
35262         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35263         
35264             
35265         var a = this.ranchor;
35266
35267         var el = Roo.get(a);
35268
35269         if(Roo.isOpera){ // opera render bug ignores the CSS
35270             el.setStyle("text-decoration", "none");
35271         }
35272
35273         el.on("click", this.onClick, this);
35274         el.on("dblclick", this.onDblClick, this);
35275         el.on("contextmenu", this.onContextMenu, this);
35276         
35277     },
35278     
35279     /*onSelectedChange : function(state){
35280         if(state){
35281             this.focus();
35282             this.addClass("x-tree-selected");
35283         }else{
35284             //this.blur();
35285             this.removeClass("x-tree-selected");
35286         }
35287     },*/
35288     addClass : function(cls){
35289         if(this.elRow){
35290             Roo.fly(this.elRow).addClass(cls);
35291         }
35292         
35293     },
35294     
35295     
35296     removeClass : function(cls){
35297         if(this.elRow){
35298             Roo.fly(this.elRow).removeClass(cls);
35299         }
35300     }
35301
35302     
35303     
35304 });//<Script type="text/javascript">
35305
35306 /*
35307  * Based on:
35308  * Ext JS Library 1.1.1
35309  * Copyright(c) 2006-2007, Ext JS, LLC.
35310  *
35311  * Originally Released Under LGPL - original licence link has changed is not relivant.
35312  *
35313  * Fork - LGPL
35314  * <script type="text/javascript">
35315  */
35316  
35317
35318 /**
35319  * @class Roo.tree.ColumnTree
35320  * @extends Roo.data.TreePanel
35321  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35322  * @cfg {int} borderWidth  compined right/left border allowance
35323  * @constructor
35324  * @param {String/HTMLElement/Element} el The container element
35325  * @param {Object} config
35326  */
35327 Roo.tree.ColumnTree =  function(el, config)
35328 {
35329    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35330    this.addEvents({
35331         /**
35332         * @event resize
35333         * Fire this event on a container when it resizes
35334         * @param {int} w Width
35335         * @param {int} h Height
35336         */
35337        "resize" : true
35338     });
35339     this.on('resize', this.onResize, this);
35340 };
35341
35342 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35343     //lines:false,
35344     
35345     
35346     borderWidth: Roo.isBorderBox ? 0 : 2, 
35347     headEls : false,
35348     
35349     render : function(){
35350         // add the header.....
35351        
35352         Roo.tree.ColumnTree.superclass.render.apply(this);
35353         
35354         this.el.addClass('x-column-tree');
35355         
35356         this.headers = this.el.createChild(
35357             {cls:'x-tree-headers'},this.innerCt.dom);
35358    
35359         var cols = this.columns, c;
35360         var totalWidth = 0;
35361         this.headEls = [];
35362         var  len = cols.length;
35363         for(var i = 0; i < len; i++){
35364              c = cols[i];
35365              totalWidth += c.width;
35366             this.headEls.push(this.headers.createChild({
35367                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35368                  cn: {
35369                      cls:'x-tree-hd-text',
35370                      html: c.header
35371                  },
35372                  style:'width:'+(c.width-this.borderWidth)+'px;'
35373              }));
35374         }
35375         this.headers.createChild({cls:'x-clear'});
35376         // prevent floats from wrapping when clipped
35377         this.headers.setWidth(totalWidth);
35378         //this.innerCt.setWidth(totalWidth);
35379         this.innerCt.setStyle({ overflow: 'auto' });
35380         this.onResize(this.width, this.height);
35381              
35382         
35383     },
35384     onResize : function(w,h)
35385     {
35386         this.height = h;
35387         this.width = w;
35388         // resize cols..
35389         this.innerCt.setWidth(this.width);
35390         this.innerCt.setHeight(this.height-20);
35391         
35392         // headers...
35393         var cols = this.columns, c;
35394         var totalWidth = 0;
35395         var expEl = false;
35396         var len = cols.length;
35397         for(var i = 0; i < len; i++){
35398             c = cols[i];
35399             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35400                 // it's the expander..
35401                 expEl  = this.headEls[i];
35402                 continue;
35403             }
35404             totalWidth += c.width;
35405             
35406         }
35407         if (expEl) {
35408             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35409         }
35410         this.headers.setWidth(w-20);
35411
35412         
35413         
35414         
35415     }
35416 });
35417 /*
35418  * Based on:
35419  * Ext JS Library 1.1.1
35420  * Copyright(c) 2006-2007, Ext JS, LLC.
35421  *
35422  * Originally Released Under LGPL - original licence link has changed is not relivant.
35423  *
35424  * Fork - LGPL
35425  * <script type="text/javascript">
35426  */
35427  
35428 /**
35429  * @class Roo.menu.Menu
35430  * @extends Roo.util.Observable
35431  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35432  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35433  * @constructor
35434  * Creates a new Menu
35435  * @param {Object} config Configuration options
35436  */
35437 Roo.menu.Menu = function(config){
35438     Roo.apply(this, config);
35439     this.id = this.id || Roo.id();
35440     this.addEvents({
35441         /**
35442          * @event beforeshow
35443          * Fires before this menu is displayed
35444          * @param {Roo.menu.Menu} this
35445          */
35446         beforeshow : true,
35447         /**
35448          * @event beforehide
35449          * Fires before this menu is hidden
35450          * @param {Roo.menu.Menu} this
35451          */
35452         beforehide : true,
35453         /**
35454          * @event show
35455          * Fires after this menu is displayed
35456          * @param {Roo.menu.Menu} this
35457          */
35458         show : true,
35459         /**
35460          * @event hide
35461          * Fires after this menu is hidden
35462          * @param {Roo.menu.Menu} this
35463          */
35464         hide : true,
35465         /**
35466          * @event click
35467          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35468          * @param {Roo.menu.Menu} this
35469          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35470          * @param {Roo.EventObject} e
35471          */
35472         click : true,
35473         /**
35474          * @event mouseover
35475          * Fires when the mouse is hovering over this menu
35476          * @param {Roo.menu.Menu} this
35477          * @param {Roo.EventObject} e
35478          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35479          */
35480         mouseover : true,
35481         /**
35482          * @event mouseout
35483          * Fires when the mouse exits this menu
35484          * @param {Roo.menu.Menu} this
35485          * @param {Roo.EventObject} e
35486          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35487          */
35488         mouseout : true,
35489         /**
35490          * @event itemclick
35491          * Fires when a menu item contained in this menu is clicked
35492          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35493          * @param {Roo.EventObject} e
35494          */
35495         itemclick: true
35496     });
35497     if (this.registerMenu) {
35498         Roo.menu.MenuMgr.register(this);
35499     }
35500     
35501     var mis = this.items;
35502     this.items = new Roo.util.MixedCollection();
35503     if(mis){
35504         this.add.apply(this, mis);
35505     }
35506 };
35507
35508 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35509     /**
35510      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35511      */
35512     minWidth : 120,
35513     /**
35514      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35515      * for bottom-right shadow (defaults to "sides")
35516      */
35517     shadow : "sides",
35518     /**
35519      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35520      * this menu (defaults to "tl-tr?")
35521      */
35522     subMenuAlign : "tl-tr?",
35523     /**
35524      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35525      * relative to its element of origin (defaults to "tl-bl?")
35526      */
35527     defaultAlign : "tl-bl?",
35528     /**
35529      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35530      */
35531     allowOtherMenus : false,
35532     /**
35533      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35534      */
35535     registerMenu : true,
35536
35537     hidden:true,
35538
35539     // private
35540     render : function(){
35541         if(this.el){
35542             return;
35543         }
35544         var el = this.el = new Roo.Layer({
35545             cls: "x-menu",
35546             shadow:this.shadow,
35547             constrain: false,
35548             parentEl: this.parentEl || document.body,
35549             zindex:15000
35550         });
35551
35552         this.keyNav = new Roo.menu.MenuNav(this);
35553
35554         if(this.plain){
35555             el.addClass("x-menu-plain");
35556         }
35557         if(this.cls){
35558             el.addClass(this.cls);
35559         }
35560         // generic focus element
35561         this.focusEl = el.createChild({
35562             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35563         });
35564         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35565         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35566         
35567         ul.on("mouseover", this.onMouseOver, this);
35568         ul.on("mouseout", this.onMouseOut, this);
35569         this.items.each(function(item){
35570             if (item.hidden) {
35571                 return;
35572             }
35573             
35574             var li = document.createElement("li");
35575             li.className = "x-menu-list-item";
35576             ul.dom.appendChild(li);
35577             item.render(li, this);
35578         }, this);
35579         this.ul = ul;
35580         this.autoWidth();
35581     },
35582
35583     // private
35584     autoWidth : function(){
35585         var el = this.el, ul = this.ul;
35586         if(!el){
35587             return;
35588         }
35589         var w = this.width;
35590         if(w){
35591             el.setWidth(w);
35592         }else if(Roo.isIE){
35593             el.setWidth(this.minWidth);
35594             var t = el.dom.offsetWidth; // force recalc
35595             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35596         }
35597     },
35598
35599     // private
35600     delayAutoWidth : function(){
35601         if(this.rendered){
35602             if(!this.awTask){
35603                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35604             }
35605             this.awTask.delay(20);
35606         }
35607     },
35608
35609     // private
35610     findTargetItem : function(e){
35611         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35612         if(t && t.menuItemId){
35613             return this.items.get(t.menuItemId);
35614         }
35615     },
35616
35617     // private
35618     onClick : function(e){
35619         Roo.log("menu.onClick");
35620         var t = this.findTargetItem(e);
35621         if(!t){
35622             return;
35623         }
35624         Roo.log(e);
35625         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35626             if(t == this.activeItem && t.shouldDeactivate(e)){
35627                 this.activeItem.deactivate();
35628                 delete this.activeItem;
35629                 return;
35630             }
35631             if(t.canActivate){
35632                 this.setActiveItem(t, true);
35633             }
35634             return;
35635             
35636             
35637         }
35638         
35639         t.onClick(e);
35640         this.fireEvent("click", this, t, e);
35641     },
35642
35643     // private
35644     setActiveItem : function(item, autoExpand){
35645         if(item != this.activeItem){
35646             if(this.activeItem){
35647                 this.activeItem.deactivate();
35648             }
35649             this.activeItem = item;
35650             item.activate(autoExpand);
35651         }else if(autoExpand){
35652             item.expandMenu();
35653         }
35654     },
35655
35656     // private
35657     tryActivate : function(start, step){
35658         var items = this.items;
35659         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35660             var item = items.get(i);
35661             if(!item.disabled && item.canActivate){
35662                 this.setActiveItem(item, false);
35663                 return item;
35664             }
35665         }
35666         return false;
35667     },
35668
35669     // private
35670     onMouseOver : function(e){
35671         var t;
35672         if(t = this.findTargetItem(e)){
35673             if(t.canActivate && !t.disabled){
35674                 this.setActiveItem(t, true);
35675             }
35676         }
35677         this.fireEvent("mouseover", this, e, t);
35678     },
35679
35680     // private
35681     onMouseOut : function(e){
35682         var t;
35683         if(t = this.findTargetItem(e)){
35684             if(t == this.activeItem && t.shouldDeactivate(e)){
35685                 this.activeItem.deactivate();
35686                 delete this.activeItem;
35687             }
35688         }
35689         this.fireEvent("mouseout", this, e, t);
35690     },
35691
35692     /**
35693      * Read-only.  Returns true if the menu is currently displayed, else false.
35694      * @type Boolean
35695      */
35696     isVisible : function(){
35697         return this.el && !this.hidden;
35698     },
35699
35700     /**
35701      * Displays this menu relative to another element
35702      * @param {String/HTMLElement/Roo.Element} element The element to align to
35703      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35704      * the element (defaults to this.defaultAlign)
35705      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35706      */
35707     show : function(el, pos, parentMenu){
35708         this.parentMenu = parentMenu;
35709         if(!this.el){
35710             this.render();
35711         }
35712         this.fireEvent("beforeshow", this);
35713         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35714     },
35715
35716     /**
35717      * Displays this menu at a specific xy position
35718      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35719      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35720      */
35721     showAt : function(xy, parentMenu, /* private: */_e){
35722         this.parentMenu = parentMenu;
35723         if(!this.el){
35724             this.render();
35725         }
35726         if(_e !== false){
35727             this.fireEvent("beforeshow", this);
35728             xy = this.el.adjustForConstraints(xy);
35729         }
35730         this.el.setXY(xy);
35731         this.el.show();
35732         this.hidden = false;
35733         this.focus();
35734         this.fireEvent("show", this);
35735     },
35736
35737     focus : function(){
35738         if(!this.hidden){
35739             this.doFocus.defer(50, this);
35740         }
35741     },
35742
35743     doFocus : function(){
35744         if(!this.hidden){
35745             this.focusEl.focus();
35746         }
35747     },
35748
35749     /**
35750      * Hides this menu and optionally all parent menus
35751      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35752      */
35753     hide : function(deep){
35754         if(this.el && this.isVisible()){
35755             this.fireEvent("beforehide", this);
35756             if(this.activeItem){
35757                 this.activeItem.deactivate();
35758                 this.activeItem = null;
35759             }
35760             this.el.hide();
35761             this.hidden = true;
35762             this.fireEvent("hide", this);
35763         }
35764         if(deep === true && this.parentMenu){
35765             this.parentMenu.hide(true);
35766         }
35767     },
35768
35769     /**
35770      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35771      * Any of the following are valid:
35772      * <ul>
35773      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35774      * <li>An HTMLElement object which will be converted to a menu item</li>
35775      * <li>A menu item config object that will be created as a new menu item</li>
35776      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35777      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35778      * </ul>
35779      * Usage:
35780      * <pre><code>
35781 // Create the menu
35782 var menu = new Roo.menu.Menu();
35783
35784 // Create a menu item to add by reference
35785 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35786
35787 // Add a bunch of items at once using different methods.
35788 // Only the last item added will be returned.
35789 var item = menu.add(
35790     menuItem,                // add existing item by ref
35791     'Dynamic Item',          // new TextItem
35792     '-',                     // new separator
35793     { text: 'Config Item' }  // new item by config
35794 );
35795 </code></pre>
35796      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35797      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35798      */
35799     add : function(){
35800         var a = arguments, l = a.length, item;
35801         for(var i = 0; i < l; i++){
35802             var el = a[i];
35803             if ((typeof(el) == "object") && el.xtype && el.xns) {
35804                 el = Roo.factory(el, Roo.menu);
35805             }
35806             
35807             if(el.render){ // some kind of Item
35808                 item = this.addItem(el);
35809             }else if(typeof el == "string"){ // string
35810                 if(el == "separator" || el == "-"){
35811                     item = this.addSeparator();
35812                 }else{
35813                     item = this.addText(el);
35814                 }
35815             }else if(el.tagName || el.el){ // element
35816                 item = this.addElement(el);
35817             }else if(typeof el == "object"){ // must be menu item config?
35818                 item = this.addMenuItem(el);
35819             }
35820         }
35821         return item;
35822     },
35823
35824     /**
35825      * Returns this menu's underlying {@link Roo.Element} object
35826      * @return {Roo.Element} The element
35827      */
35828     getEl : function(){
35829         if(!this.el){
35830             this.render();
35831         }
35832         return this.el;
35833     },
35834
35835     /**
35836      * Adds a separator bar to the menu
35837      * @return {Roo.menu.Item} The menu item that was added
35838      */
35839     addSeparator : function(){
35840         return this.addItem(new Roo.menu.Separator());
35841     },
35842
35843     /**
35844      * Adds an {@link Roo.Element} object to the menu
35845      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35846      * @return {Roo.menu.Item} The menu item that was added
35847      */
35848     addElement : function(el){
35849         return this.addItem(new Roo.menu.BaseItem(el));
35850     },
35851
35852     /**
35853      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35854      * @param {Roo.menu.Item} item The menu item to add
35855      * @return {Roo.menu.Item} The menu item that was added
35856      */
35857     addItem : function(item){
35858         this.items.add(item);
35859         if(this.ul){
35860             var li = document.createElement("li");
35861             li.className = "x-menu-list-item";
35862             this.ul.dom.appendChild(li);
35863             item.render(li, this);
35864             this.delayAutoWidth();
35865         }
35866         return item;
35867     },
35868
35869     /**
35870      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35871      * @param {Object} config A MenuItem config object
35872      * @return {Roo.menu.Item} The menu item that was added
35873      */
35874     addMenuItem : function(config){
35875         if(!(config instanceof Roo.menu.Item)){
35876             if(typeof config.checked == "boolean"){ // must be check menu item config?
35877                 config = new Roo.menu.CheckItem(config);
35878             }else{
35879                 config = new Roo.menu.Item(config);
35880             }
35881         }
35882         return this.addItem(config);
35883     },
35884
35885     /**
35886      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35887      * @param {String} text The text to display in the menu item
35888      * @return {Roo.menu.Item} The menu item that was added
35889      */
35890     addText : function(text){
35891         return this.addItem(new Roo.menu.TextItem({ text : text }));
35892     },
35893
35894     /**
35895      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35896      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35897      * @param {Roo.menu.Item} item The menu item to add
35898      * @return {Roo.menu.Item} The menu item that was added
35899      */
35900     insert : function(index, item){
35901         this.items.insert(index, item);
35902         if(this.ul){
35903             var li = document.createElement("li");
35904             li.className = "x-menu-list-item";
35905             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35906             item.render(li, this);
35907             this.delayAutoWidth();
35908         }
35909         return item;
35910     },
35911
35912     /**
35913      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35914      * @param {Roo.menu.Item} item The menu item to remove
35915      */
35916     remove : function(item){
35917         this.items.removeKey(item.id);
35918         item.destroy();
35919     },
35920
35921     /**
35922      * Removes and destroys all items in the menu
35923      */
35924     removeAll : function(){
35925         var f;
35926         while(f = this.items.first()){
35927             this.remove(f);
35928         }
35929     }
35930 });
35931
35932 // MenuNav is a private utility class used internally by the Menu
35933 Roo.menu.MenuNav = function(menu){
35934     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35935     this.scope = this.menu = menu;
35936 };
35937
35938 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35939     doRelay : function(e, h){
35940         var k = e.getKey();
35941         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35942             this.menu.tryActivate(0, 1);
35943             return false;
35944         }
35945         return h.call(this.scope || this, e, this.menu);
35946     },
35947
35948     up : function(e, m){
35949         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35950             m.tryActivate(m.items.length-1, -1);
35951         }
35952     },
35953
35954     down : function(e, m){
35955         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35956             m.tryActivate(0, 1);
35957         }
35958     },
35959
35960     right : function(e, m){
35961         if(m.activeItem){
35962             m.activeItem.expandMenu(true);
35963         }
35964     },
35965
35966     left : function(e, m){
35967         m.hide();
35968         if(m.parentMenu && m.parentMenu.activeItem){
35969             m.parentMenu.activeItem.activate();
35970         }
35971     },
35972
35973     enter : function(e, m){
35974         if(m.activeItem){
35975             e.stopPropagation();
35976             m.activeItem.onClick(e);
35977             m.fireEvent("click", this, m.activeItem);
35978             return true;
35979         }
35980     }
35981 });/*
35982  * Based on:
35983  * Ext JS Library 1.1.1
35984  * Copyright(c) 2006-2007, Ext JS, LLC.
35985  *
35986  * Originally Released Under LGPL - original licence link has changed is not relivant.
35987  *
35988  * Fork - LGPL
35989  * <script type="text/javascript">
35990  */
35991  
35992 /**
35993  * @class Roo.menu.MenuMgr
35994  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35995  * @singleton
35996  */
35997 Roo.menu.MenuMgr = function(){
35998    var menus, active, groups = {}, attached = false, lastShow = new Date();
35999
36000    // private - called when first menu is created
36001    function init(){
36002        menus = {};
36003        active = new Roo.util.MixedCollection();
36004        Roo.get(document).addKeyListener(27, function(){
36005            if(active.length > 0){
36006                hideAll();
36007            }
36008        });
36009    }
36010
36011    // private
36012    function hideAll(){
36013        if(active && active.length > 0){
36014            var c = active.clone();
36015            c.each(function(m){
36016                m.hide();
36017            });
36018        }
36019    }
36020
36021    // private
36022    function onHide(m){
36023        active.remove(m);
36024        if(active.length < 1){
36025            Roo.get(document).un("mousedown", onMouseDown);
36026            attached = false;
36027        }
36028    }
36029
36030    // private
36031    function onShow(m){
36032        var last = active.last();
36033        lastShow = new Date();
36034        active.add(m);
36035        if(!attached){
36036            Roo.get(document).on("mousedown", onMouseDown);
36037            attached = true;
36038        }
36039        if(m.parentMenu){
36040           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36041           m.parentMenu.activeChild = m;
36042        }else if(last && last.isVisible()){
36043           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36044        }
36045    }
36046
36047    // private
36048    function onBeforeHide(m){
36049        if(m.activeChild){
36050            m.activeChild.hide();
36051        }
36052        if(m.autoHideTimer){
36053            clearTimeout(m.autoHideTimer);
36054            delete m.autoHideTimer;
36055        }
36056    }
36057
36058    // private
36059    function onBeforeShow(m){
36060        var pm = m.parentMenu;
36061        if(!pm && !m.allowOtherMenus){
36062            hideAll();
36063        }else if(pm && pm.activeChild && active != m){
36064            pm.activeChild.hide();
36065        }
36066    }
36067
36068    // private
36069    function onMouseDown(e){
36070        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36071            hideAll();
36072        }
36073    }
36074
36075    // private
36076    function onBeforeCheck(mi, state){
36077        if(state){
36078            var g = groups[mi.group];
36079            for(var i = 0, l = g.length; i < l; i++){
36080                if(g[i] != mi){
36081                    g[i].setChecked(false);
36082                }
36083            }
36084        }
36085    }
36086
36087    return {
36088
36089        /**
36090         * Hides all menus that are currently visible
36091         */
36092        hideAll : function(){
36093             hideAll();  
36094        },
36095
36096        // private
36097        register : function(menu){
36098            if(!menus){
36099                init();
36100            }
36101            menus[menu.id] = menu;
36102            menu.on("beforehide", onBeforeHide);
36103            menu.on("hide", onHide);
36104            menu.on("beforeshow", onBeforeShow);
36105            menu.on("show", onShow);
36106            var g = menu.group;
36107            if(g && menu.events["checkchange"]){
36108                if(!groups[g]){
36109                    groups[g] = [];
36110                }
36111                groups[g].push(menu);
36112                menu.on("checkchange", onCheck);
36113            }
36114        },
36115
36116         /**
36117          * Returns a {@link Roo.menu.Menu} object
36118          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36119          * be used to generate and return a new Menu instance.
36120          */
36121        get : function(menu){
36122            if(typeof menu == "string"){ // menu id
36123                return menus[menu];
36124            }else if(menu.events){  // menu instance
36125                return menu;
36126            }else if(typeof menu.length == 'number'){ // array of menu items?
36127                return new Roo.menu.Menu({items:menu});
36128            }else{ // otherwise, must be a config
36129                return new Roo.menu.Menu(menu);
36130            }
36131        },
36132
36133        // private
36134        unregister : function(menu){
36135            delete menus[menu.id];
36136            menu.un("beforehide", onBeforeHide);
36137            menu.un("hide", onHide);
36138            menu.un("beforeshow", onBeforeShow);
36139            menu.un("show", onShow);
36140            var g = menu.group;
36141            if(g && menu.events["checkchange"]){
36142                groups[g].remove(menu);
36143                menu.un("checkchange", onCheck);
36144            }
36145        },
36146
36147        // private
36148        registerCheckable : function(menuItem){
36149            var g = menuItem.group;
36150            if(g){
36151                if(!groups[g]){
36152                    groups[g] = [];
36153                }
36154                groups[g].push(menuItem);
36155                menuItem.on("beforecheckchange", onBeforeCheck);
36156            }
36157        },
36158
36159        // private
36160        unregisterCheckable : function(menuItem){
36161            var g = menuItem.group;
36162            if(g){
36163                groups[g].remove(menuItem);
36164                menuItem.un("beforecheckchange", onBeforeCheck);
36165            }
36166        }
36167    };
36168 }();/*
36169  * Based on:
36170  * Ext JS Library 1.1.1
36171  * Copyright(c) 2006-2007, Ext JS, LLC.
36172  *
36173  * Originally Released Under LGPL - original licence link has changed is not relivant.
36174  *
36175  * Fork - LGPL
36176  * <script type="text/javascript">
36177  */
36178  
36179
36180 /**
36181  * @class Roo.menu.BaseItem
36182  * @extends Roo.Component
36183  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36184  * management and base configuration options shared by all menu components.
36185  * @constructor
36186  * Creates a new BaseItem
36187  * @param {Object} config Configuration options
36188  */
36189 Roo.menu.BaseItem = function(config){
36190     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36191
36192     this.addEvents({
36193         /**
36194          * @event click
36195          * Fires when this item is clicked
36196          * @param {Roo.menu.BaseItem} this
36197          * @param {Roo.EventObject} e
36198          */
36199         click: true,
36200         /**
36201          * @event activate
36202          * Fires when this item is activated
36203          * @param {Roo.menu.BaseItem} this
36204          */
36205         activate : true,
36206         /**
36207          * @event deactivate
36208          * Fires when this item is deactivated
36209          * @param {Roo.menu.BaseItem} this
36210          */
36211         deactivate : true
36212     });
36213
36214     if(this.handler){
36215         this.on("click", this.handler, this.scope, true);
36216     }
36217 };
36218
36219 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36220     /**
36221      * @cfg {Function} handler
36222      * A function that will handle the click event of this menu item (defaults to undefined)
36223      */
36224     /**
36225      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36226      */
36227     canActivate : false,
36228     
36229      /**
36230      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36231      */
36232     hidden: false,
36233     
36234     /**
36235      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36236      */
36237     activeClass : "x-menu-item-active",
36238     /**
36239      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36240      */
36241     hideOnClick : true,
36242     /**
36243      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36244      */
36245     hideDelay : 100,
36246
36247     // private
36248     ctype: "Roo.menu.BaseItem",
36249
36250     // private
36251     actionMode : "container",
36252
36253     // private
36254     render : function(container, parentMenu){
36255         this.parentMenu = parentMenu;
36256         Roo.menu.BaseItem.superclass.render.call(this, container);
36257         this.container.menuItemId = this.id;
36258     },
36259
36260     // private
36261     onRender : function(container, position){
36262         this.el = Roo.get(this.el);
36263         container.dom.appendChild(this.el.dom);
36264     },
36265
36266     // private
36267     onClick : function(e){
36268         if(!this.disabled && this.fireEvent("click", this, e) !== false
36269                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36270             this.handleClick(e);
36271         }else{
36272             e.stopEvent();
36273         }
36274     },
36275
36276     // private
36277     activate : function(){
36278         if(this.disabled){
36279             return false;
36280         }
36281         var li = this.container;
36282         li.addClass(this.activeClass);
36283         this.region = li.getRegion().adjust(2, 2, -2, -2);
36284         this.fireEvent("activate", this);
36285         return true;
36286     },
36287
36288     // private
36289     deactivate : function(){
36290         this.container.removeClass(this.activeClass);
36291         this.fireEvent("deactivate", this);
36292     },
36293
36294     // private
36295     shouldDeactivate : function(e){
36296         return !this.region || !this.region.contains(e.getPoint());
36297     },
36298
36299     // private
36300     handleClick : function(e){
36301         if(this.hideOnClick){
36302             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36303         }
36304     },
36305
36306     // private
36307     expandMenu : function(autoActivate){
36308         // do nothing
36309     },
36310
36311     // private
36312     hideMenu : function(){
36313         // do nothing
36314     }
36315 });/*
36316  * Based on:
36317  * Ext JS Library 1.1.1
36318  * Copyright(c) 2006-2007, Ext JS, LLC.
36319  *
36320  * Originally Released Under LGPL - original licence link has changed is not relivant.
36321  *
36322  * Fork - LGPL
36323  * <script type="text/javascript">
36324  */
36325  
36326 /**
36327  * @class Roo.menu.Adapter
36328  * @extends Roo.menu.BaseItem
36329  * 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.
36330  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36331  * @constructor
36332  * Creates a new Adapter
36333  * @param {Object} config Configuration options
36334  */
36335 Roo.menu.Adapter = function(component, config){
36336     Roo.menu.Adapter.superclass.constructor.call(this, config);
36337     this.component = component;
36338 };
36339 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36340     // private
36341     canActivate : true,
36342
36343     // private
36344     onRender : function(container, position){
36345         this.component.render(container);
36346         this.el = this.component.getEl();
36347     },
36348
36349     // private
36350     activate : function(){
36351         if(this.disabled){
36352             return false;
36353         }
36354         this.component.focus();
36355         this.fireEvent("activate", this);
36356         return true;
36357     },
36358
36359     // private
36360     deactivate : function(){
36361         this.fireEvent("deactivate", this);
36362     },
36363
36364     // private
36365     disable : function(){
36366         this.component.disable();
36367         Roo.menu.Adapter.superclass.disable.call(this);
36368     },
36369
36370     // private
36371     enable : function(){
36372         this.component.enable();
36373         Roo.menu.Adapter.superclass.enable.call(this);
36374     }
36375 });/*
36376  * Based on:
36377  * Ext JS Library 1.1.1
36378  * Copyright(c) 2006-2007, Ext JS, LLC.
36379  *
36380  * Originally Released Under LGPL - original licence link has changed is not relivant.
36381  *
36382  * Fork - LGPL
36383  * <script type="text/javascript">
36384  */
36385
36386 /**
36387  * @class Roo.menu.TextItem
36388  * @extends Roo.menu.BaseItem
36389  * Adds a static text string to a menu, usually used as either a heading or group separator.
36390  * Note: old style constructor with text is still supported.
36391  * 
36392  * @constructor
36393  * Creates a new TextItem
36394  * @param {Object} cfg Configuration
36395  */
36396 Roo.menu.TextItem = function(cfg){
36397     if (typeof(cfg) == 'string') {
36398         this.text = cfg;
36399     } else {
36400         Roo.apply(this,cfg);
36401     }
36402     
36403     Roo.menu.TextItem.superclass.constructor.call(this);
36404 };
36405
36406 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36407     /**
36408      * @cfg {Boolean} text Text to show on item.
36409      */
36410     text : '',
36411     
36412     /**
36413      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36414      */
36415     hideOnClick : false,
36416     /**
36417      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36418      */
36419     itemCls : "x-menu-text",
36420
36421     // private
36422     onRender : function(){
36423         var s = document.createElement("span");
36424         s.className = this.itemCls;
36425         s.innerHTML = this.text;
36426         this.el = s;
36427         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36428     }
36429 });/*
36430  * Based on:
36431  * Ext JS Library 1.1.1
36432  * Copyright(c) 2006-2007, Ext JS, LLC.
36433  *
36434  * Originally Released Under LGPL - original licence link has changed is not relivant.
36435  *
36436  * Fork - LGPL
36437  * <script type="text/javascript">
36438  */
36439
36440 /**
36441  * @class Roo.menu.Separator
36442  * @extends Roo.menu.BaseItem
36443  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36444  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36445  * @constructor
36446  * @param {Object} config Configuration options
36447  */
36448 Roo.menu.Separator = function(config){
36449     Roo.menu.Separator.superclass.constructor.call(this, config);
36450 };
36451
36452 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36453     /**
36454      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36455      */
36456     itemCls : "x-menu-sep",
36457     /**
36458      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36459      */
36460     hideOnClick : false,
36461
36462     // private
36463     onRender : function(li){
36464         var s = document.createElement("span");
36465         s.className = this.itemCls;
36466         s.innerHTML = "&#160;";
36467         this.el = s;
36468         li.addClass("x-menu-sep-li");
36469         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36470     }
36471 });/*
36472  * Based on:
36473  * Ext JS Library 1.1.1
36474  * Copyright(c) 2006-2007, Ext JS, LLC.
36475  *
36476  * Originally Released Under LGPL - original licence link has changed is not relivant.
36477  *
36478  * Fork - LGPL
36479  * <script type="text/javascript">
36480  */
36481 /**
36482  * @class Roo.menu.Item
36483  * @extends Roo.menu.BaseItem
36484  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36485  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36486  * activation and click handling.
36487  * @constructor
36488  * Creates a new Item
36489  * @param {Object} config Configuration options
36490  */
36491 Roo.menu.Item = function(config){
36492     Roo.menu.Item.superclass.constructor.call(this, config);
36493     if(this.menu){
36494         this.menu = Roo.menu.MenuMgr.get(this.menu);
36495     }
36496 };
36497 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36498     
36499     /**
36500      * @cfg {String} text
36501      * The text to show on the menu item.
36502      */
36503     text: '',
36504      /**
36505      * @cfg {String} HTML to render in menu
36506      * The text to show on the menu item (HTML version).
36507      */
36508     html: '',
36509     /**
36510      * @cfg {String} icon
36511      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36512      */
36513     icon: undefined,
36514     /**
36515      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36516      */
36517     itemCls : "x-menu-item",
36518     /**
36519      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36520      */
36521     canActivate : true,
36522     /**
36523      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36524      */
36525     showDelay: 200,
36526     // doc'd in BaseItem
36527     hideDelay: 200,
36528
36529     // private
36530     ctype: "Roo.menu.Item",
36531     
36532     // private
36533     onRender : function(container, position){
36534         var el = document.createElement("a");
36535         el.hideFocus = true;
36536         el.unselectable = "on";
36537         el.href = this.href || "#";
36538         if(this.hrefTarget){
36539             el.target = this.hrefTarget;
36540         }
36541         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36542         
36543         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36544         
36545         el.innerHTML = String.format(
36546                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36547                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36548         this.el = el;
36549         Roo.menu.Item.superclass.onRender.call(this, container, position);
36550     },
36551
36552     /**
36553      * Sets the text to display in this menu item
36554      * @param {String} text The text to display
36555      * @param {Boolean} isHTML true to indicate text is pure html.
36556      */
36557     setText : function(text, isHTML){
36558         if (isHTML) {
36559             this.html = text;
36560         } else {
36561             this.text = text;
36562             this.html = '';
36563         }
36564         if(this.rendered){
36565             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36566      
36567             this.el.update(String.format(
36568                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36569                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36570             this.parentMenu.autoWidth();
36571         }
36572     },
36573
36574     // private
36575     handleClick : function(e){
36576         if(!this.href){ // if no link defined, stop the event automatically
36577             e.stopEvent();
36578         }
36579         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36580     },
36581
36582     // private
36583     activate : function(autoExpand){
36584         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36585             this.focus();
36586             if(autoExpand){
36587                 this.expandMenu();
36588             }
36589         }
36590         return true;
36591     },
36592
36593     // private
36594     shouldDeactivate : function(e){
36595         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36596             if(this.menu && this.menu.isVisible()){
36597                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36598             }
36599             return true;
36600         }
36601         return false;
36602     },
36603
36604     // private
36605     deactivate : function(){
36606         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36607         this.hideMenu();
36608     },
36609
36610     // private
36611     expandMenu : function(autoActivate){
36612         if(!this.disabled && this.menu){
36613             clearTimeout(this.hideTimer);
36614             delete this.hideTimer;
36615             if(!this.menu.isVisible() && !this.showTimer){
36616                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36617             }else if (this.menu.isVisible() && autoActivate){
36618                 this.menu.tryActivate(0, 1);
36619             }
36620         }
36621     },
36622
36623     // private
36624     deferExpand : function(autoActivate){
36625         delete this.showTimer;
36626         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36627         if(autoActivate){
36628             this.menu.tryActivate(0, 1);
36629         }
36630     },
36631
36632     // private
36633     hideMenu : function(){
36634         clearTimeout(this.showTimer);
36635         delete this.showTimer;
36636         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36637             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36638         }
36639     },
36640
36641     // private
36642     deferHide : function(){
36643         delete this.hideTimer;
36644         this.menu.hide();
36645     }
36646 });/*
36647  * Based on:
36648  * Ext JS Library 1.1.1
36649  * Copyright(c) 2006-2007, Ext JS, LLC.
36650  *
36651  * Originally Released Under LGPL - original licence link has changed is not relivant.
36652  *
36653  * Fork - LGPL
36654  * <script type="text/javascript">
36655  */
36656  
36657 /**
36658  * @class Roo.menu.CheckItem
36659  * @extends Roo.menu.Item
36660  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36661  * @constructor
36662  * Creates a new CheckItem
36663  * @param {Object} config Configuration options
36664  */
36665 Roo.menu.CheckItem = function(config){
36666     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36667     this.addEvents({
36668         /**
36669          * @event beforecheckchange
36670          * Fires before the checked value is set, providing an opportunity to cancel if needed
36671          * @param {Roo.menu.CheckItem} this
36672          * @param {Boolean} checked The new checked value that will be set
36673          */
36674         "beforecheckchange" : true,
36675         /**
36676          * @event checkchange
36677          * Fires after the checked value has been set
36678          * @param {Roo.menu.CheckItem} this
36679          * @param {Boolean} checked The checked value that was set
36680          */
36681         "checkchange" : true
36682     });
36683     if(this.checkHandler){
36684         this.on('checkchange', this.checkHandler, this.scope);
36685     }
36686 };
36687 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36688     /**
36689      * @cfg {String} group
36690      * All check items with the same group name will automatically be grouped into a single-select
36691      * radio button group (defaults to '')
36692      */
36693     /**
36694      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36695      */
36696     itemCls : "x-menu-item x-menu-check-item",
36697     /**
36698      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36699      */
36700     groupClass : "x-menu-group-item",
36701
36702     /**
36703      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36704      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36705      * initialized with checked = true will be rendered as checked.
36706      */
36707     checked: false,
36708
36709     // private
36710     ctype: "Roo.menu.CheckItem",
36711
36712     // private
36713     onRender : function(c){
36714         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36715         if(this.group){
36716             this.el.addClass(this.groupClass);
36717         }
36718         Roo.menu.MenuMgr.registerCheckable(this);
36719         if(this.checked){
36720             this.checked = false;
36721             this.setChecked(true, true);
36722         }
36723     },
36724
36725     // private
36726     destroy : function(){
36727         if(this.rendered){
36728             Roo.menu.MenuMgr.unregisterCheckable(this);
36729         }
36730         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36731     },
36732
36733     /**
36734      * Set the checked state of this item
36735      * @param {Boolean} checked The new checked value
36736      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36737      */
36738     setChecked : function(state, suppressEvent){
36739         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36740             if(this.container){
36741                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36742             }
36743             this.checked = state;
36744             if(suppressEvent !== true){
36745                 this.fireEvent("checkchange", this, state);
36746             }
36747         }
36748     },
36749
36750     // private
36751     handleClick : function(e){
36752        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36753            this.setChecked(!this.checked);
36754        }
36755        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36756     }
36757 });/*
36758  * Based on:
36759  * Ext JS Library 1.1.1
36760  * Copyright(c) 2006-2007, Ext JS, LLC.
36761  *
36762  * Originally Released Under LGPL - original licence link has changed is not relivant.
36763  *
36764  * Fork - LGPL
36765  * <script type="text/javascript">
36766  */
36767  
36768 /**
36769  * @class Roo.menu.DateItem
36770  * @extends Roo.menu.Adapter
36771  * A menu item that wraps the {@link Roo.DatPicker} component.
36772  * @constructor
36773  * Creates a new DateItem
36774  * @param {Object} config Configuration options
36775  */
36776 Roo.menu.DateItem = function(config){
36777     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36778     /** The Roo.DatePicker object @type Roo.DatePicker */
36779     this.picker = this.component;
36780     this.addEvents({select: true});
36781     
36782     this.picker.on("render", function(picker){
36783         picker.getEl().swallowEvent("click");
36784         picker.container.addClass("x-menu-date-item");
36785     });
36786
36787     this.picker.on("select", this.onSelect, this);
36788 };
36789
36790 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36791     // private
36792     onSelect : function(picker, date){
36793         this.fireEvent("select", this, date, picker);
36794         Roo.menu.DateItem.superclass.handleClick.call(this);
36795     }
36796 });/*
36797  * Based on:
36798  * Ext JS Library 1.1.1
36799  * Copyright(c) 2006-2007, Ext JS, LLC.
36800  *
36801  * Originally Released Under LGPL - original licence link has changed is not relivant.
36802  *
36803  * Fork - LGPL
36804  * <script type="text/javascript">
36805  */
36806  
36807 /**
36808  * @class Roo.menu.ColorItem
36809  * @extends Roo.menu.Adapter
36810  * A menu item that wraps the {@link Roo.ColorPalette} component.
36811  * @constructor
36812  * Creates a new ColorItem
36813  * @param {Object} config Configuration options
36814  */
36815 Roo.menu.ColorItem = function(config){
36816     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36817     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36818     this.palette = this.component;
36819     this.relayEvents(this.palette, ["select"]);
36820     if(this.selectHandler){
36821         this.on('select', this.selectHandler, this.scope);
36822     }
36823 };
36824 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36825  * Based on:
36826  * Ext JS Library 1.1.1
36827  * Copyright(c) 2006-2007, Ext JS, LLC.
36828  *
36829  * Originally Released Under LGPL - original licence link has changed is not relivant.
36830  *
36831  * Fork - LGPL
36832  * <script type="text/javascript">
36833  */
36834  
36835
36836 /**
36837  * @class Roo.menu.DateMenu
36838  * @extends Roo.menu.Menu
36839  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36840  * @constructor
36841  * Creates a new DateMenu
36842  * @param {Object} config Configuration options
36843  */
36844 Roo.menu.DateMenu = function(config){
36845     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36846     this.plain = true;
36847     var di = new Roo.menu.DateItem(config);
36848     this.add(di);
36849     /**
36850      * The {@link Roo.DatePicker} instance for this DateMenu
36851      * @type DatePicker
36852      */
36853     this.picker = di.picker;
36854     /**
36855      * @event select
36856      * @param {DatePicker} picker
36857      * @param {Date} date
36858      */
36859     this.relayEvents(di, ["select"]);
36860     this.on('beforeshow', function(){
36861         if(this.picker){
36862             this.picker.hideMonthPicker(false);
36863         }
36864     }, this);
36865 };
36866 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36867     cls:'x-date-menu'
36868 });/*
36869  * Based on:
36870  * Ext JS Library 1.1.1
36871  * Copyright(c) 2006-2007, Ext JS, LLC.
36872  *
36873  * Originally Released Under LGPL - original licence link has changed is not relivant.
36874  *
36875  * Fork - LGPL
36876  * <script type="text/javascript">
36877  */
36878  
36879
36880 /**
36881  * @class Roo.menu.ColorMenu
36882  * @extends Roo.menu.Menu
36883  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36884  * @constructor
36885  * Creates a new ColorMenu
36886  * @param {Object} config Configuration options
36887  */
36888 Roo.menu.ColorMenu = function(config){
36889     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36890     this.plain = true;
36891     var ci = new Roo.menu.ColorItem(config);
36892     this.add(ci);
36893     /**
36894      * The {@link Roo.ColorPalette} instance for this ColorMenu
36895      * @type ColorPalette
36896      */
36897     this.palette = ci.palette;
36898     /**
36899      * @event select
36900      * @param {ColorPalette} palette
36901      * @param {String} color
36902      */
36903     this.relayEvents(ci, ["select"]);
36904 };
36905 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36906  * Based on:
36907  * Ext JS Library 1.1.1
36908  * Copyright(c) 2006-2007, Ext JS, LLC.
36909  *
36910  * Originally Released Under LGPL - original licence link has changed is not relivant.
36911  *
36912  * Fork - LGPL
36913  * <script type="text/javascript">
36914  */
36915  
36916 /**
36917  * @class Roo.form.Field
36918  * @extends Roo.BoxComponent
36919  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36920  * @constructor
36921  * Creates a new Field
36922  * @param {Object} config Configuration options
36923  */
36924 Roo.form.Field = function(config){
36925     Roo.form.Field.superclass.constructor.call(this, config);
36926 };
36927
36928 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36929     /**
36930      * @cfg {String} fieldLabel Label to use when rendering a form.
36931      */
36932        /**
36933      * @cfg {String} qtip Mouse over tip
36934      */
36935      
36936     /**
36937      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36938      */
36939     invalidClass : "x-form-invalid",
36940     /**
36941      * @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")
36942      */
36943     invalidText : "The value in this field is invalid",
36944     /**
36945      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36946      */
36947     focusClass : "x-form-focus",
36948     /**
36949      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36950       automatic validation (defaults to "keyup").
36951      */
36952     validationEvent : "keyup",
36953     /**
36954      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36955      */
36956     validateOnBlur : true,
36957     /**
36958      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36959      */
36960     validationDelay : 250,
36961     /**
36962      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36963      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36964      */
36965     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36966     /**
36967      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36968      */
36969     fieldClass : "x-form-field",
36970     /**
36971      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36972      *<pre>
36973 Value         Description
36974 -----------   ----------------------------------------------------------------------
36975 qtip          Display a quick tip when the user hovers over the field
36976 title         Display a default browser title attribute popup
36977 under         Add a block div beneath the field containing the error text
36978 side          Add an error icon to the right of the field with a popup on hover
36979 [element id]  Add the error text directly to the innerHTML of the specified element
36980 </pre>
36981      */
36982     msgTarget : 'qtip',
36983     /**
36984      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36985      */
36986     msgFx : 'normal',
36987
36988     /**
36989      * @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.
36990      */
36991     readOnly : false,
36992
36993     /**
36994      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36995      */
36996     disabled : false,
36997
36998     /**
36999      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37000      */
37001     inputType : undefined,
37002     
37003     /**
37004      * @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).
37005          */
37006         tabIndex : undefined,
37007         
37008     // private
37009     isFormField : true,
37010
37011     // private
37012     hasFocus : false,
37013     /**
37014      * @property {Roo.Element} fieldEl
37015      * Element Containing the rendered Field (with label etc.)
37016      */
37017     /**
37018      * @cfg {Mixed} value A value to initialize this field with.
37019      */
37020     value : undefined,
37021
37022     /**
37023      * @cfg {String} name The field's HTML name attribute.
37024      */
37025     /**
37026      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37027      */
37028
37029         // private ??
37030         initComponent : function(){
37031         Roo.form.Field.superclass.initComponent.call(this);
37032         this.addEvents({
37033             /**
37034              * @event focus
37035              * Fires when this field receives input focus.
37036              * @param {Roo.form.Field} this
37037              */
37038             focus : true,
37039             /**
37040              * @event blur
37041              * Fires when this field loses input focus.
37042              * @param {Roo.form.Field} this
37043              */
37044             blur : true,
37045             /**
37046              * @event specialkey
37047              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37048              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37049              * @param {Roo.form.Field} this
37050              * @param {Roo.EventObject} e The event object
37051              */
37052             specialkey : true,
37053             /**
37054              * @event change
37055              * Fires just before the field blurs if the field value has changed.
37056              * @param {Roo.form.Field} this
37057              * @param {Mixed} newValue The new value
37058              * @param {Mixed} oldValue The original value
37059              */
37060             change : true,
37061             /**
37062              * @event invalid
37063              * Fires after the field has been marked as invalid.
37064              * @param {Roo.form.Field} this
37065              * @param {String} msg The validation message
37066              */
37067             invalid : true,
37068             /**
37069              * @event valid
37070              * Fires after the field has been validated with no errors.
37071              * @param {Roo.form.Field} this
37072              */
37073             valid : true,
37074              /**
37075              * @event keyup
37076              * Fires after the key up
37077              * @param {Roo.form.Field} this
37078              * @param {Roo.EventObject}  e The event Object
37079              */
37080             keyup : true
37081         });
37082     },
37083
37084     /**
37085      * Returns the name attribute of the field if available
37086      * @return {String} name The field name
37087      */
37088     getName: function(){
37089          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37090     },
37091
37092     // private
37093     onRender : function(ct, position){
37094         Roo.form.Field.superclass.onRender.call(this, ct, position);
37095         if(!this.el){
37096             var cfg = this.getAutoCreate();
37097             if(!cfg.name){
37098                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37099             }
37100             if (!cfg.name.length) {
37101                 delete cfg.name;
37102             }
37103             if(this.inputType){
37104                 cfg.type = this.inputType;
37105             }
37106             this.el = ct.createChild(cfg, position);
37107         }
37108         var type = this.el.dom.type;
37109         if(type){
37110             if(type == 'password'){
37111                 type = 'text';
37112             }
37113             this.el.addClass('x-form-'+type);
37114         }
37115         if(this.readOnly){
37116             this.el.dom.readOnly = true;
37117         }
37118         if(this.tabIndex !== undefined){
37119             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37120         }
37121
37122         this.el.addClass([this.fieldClass, this.cls]);
37123         this.initValue();
37124     },
37125
37126     /**
37127      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37128      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37129      * @return {Roo.form.Field} this
37130      */
37131     applyTo : function(target){
37132         this.allowDomMove = false;
37133         this.el = Roo.get(target);
37134         this.render(this.el.dom.parentNode);
37135         return this;
37136     },
37137
37138     // private
37139     initValue : function(){
37140         if(this.value !== undefined){
37141             this.setValue(this.value);
37142         }else if(this.el.dom.value.length > 0){
37143             this.setValue(this.el.dom.value);
37144         }
37145     },
37146
37147     /**
37148      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37149      */
37150     isDirty : function() {
37151         if(this.disabled) {
37152             return false;
37153         }
37154         return String(this.getValue()) !== String(this.originalValue);
37155     },
37156
37157     // private
37158     afterRender : function(){
37159         Roo.form.Field.superclass.afterRender.call(this);
37160         this.initEvents();
37161     },
37162
37163     // private
37164     fireKey : function(e){
37165         //Roo.log('field ' + e.getKey());
37166         if(e.isNavKeyPress()){
37167             this.fireEvent("specialkey", this, e);
37168         }
37169     },
37170
37171     /**
37172      * Resets the current field value to the originally loaded value and clears any validation messages
37173      */
37174     reset : function(){
37175         this.setValue(this.resetValue);
37176         this.clearInvalid();
37177     },
37178
37179     // private
37180     initEvents : function(){
37181         // safari killled keypress - so keydown is now used..
37182         this.el.on("keydown" , this.fireKey,  this);
37183         this.el.on("focus", this.onFocus,  this);
37184         this.el.on("blur", this.onBlur,  this);
37185         this.el.relayEvent('keyup', this);
37186
37187         // reference to original value for reset
37188         this.originalValue = this.getValue();
37189         this.resetValue =  this.getValue();
37190     },
37191
37192     // private
37193     onFocus : function(){
37194         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37195             this.el.addClass(this.focusClass);
37196         }
37197         if(!this.hasFocus){
37198             this.hasFocus = true;
37199             this.startValue = this.getValue();
37200             this.fireEvent("focus", this);
37201         }
37202     },
37203
37204     beforeBlur : Roo.emptyFn,
37205
37206     // private
37207     onBlur : function(){
37208         this.beforeBlur();
37209         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37210             this.el.removeClass(this.focusClass);
37211         }
37212         this.hasFocus = false;
37213         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37214             this.validate();
37215         }
37216         var v = this.getValue();
37217         if(String(v) !== String(this.startValue)){
37218             this.fireEvent('change', this, v, this.startValue);
37219         }
37220         this.fireEvent("blur", this);
37221     },
37222
37223     /**
37224      * Returns whether or not the field value is currently valid
37225      * @param {Boolean} preventMark True to disable marking the field invalid
37226      * @return {Boolean} True if the value is valid, else false
37227      */
37228     isValid : function(preventMark){
37229         if(this.disabled){
37230             return true;
37231         }
37232         var restore = this.preventMark;
37233         this.preventMark = preventMark === true;
37234         var v = this.validateValue(this.processValue(this.getRawValue()));
37235         this.preventMark = restore;
37236         return v;
37237     },
37238
37239     /**
37240      * Validates the field value
37241      * @return {Boolean} True if the value is valid, else false
37242      */
37243     validate : function(){
37244         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37245             this.clearInvalid();
37246             return true;
37247         }
37248         return false;
37249     },
37250
37251     processValue : function(value){
37252         return value;
37253     },
37254
37255     // private
37256     // Subclasses should provide the validation implementation by overriding this
37257     validateValue : function(value){
37258         return true;
37259     },
37260
37261     /**
37262      * Mark this field as invalid
37263      * @param {String} msg The validation message
37264      */
37265     markInvalid : function(msg){
37266         if(!this.rendered || this.preventMark){ // not rendered
37267             return;
37268         }
37269         
37270         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37271         
37272         obj.el.addClass(this.invalidClass);
37273         msg = msg || this.invalidText;
37274         switch(this.msgTarget){
37275             case 'qtip':
37276                 obj.el.dom.qtip = msg;
37277                 obj.el.dom.qclass = 'x-form-invalid-tip';
37278                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37279                     Roo.QuickTips.enable();
37280                 }
37281                 break;
37282             case 'title':
37283                 this.el.dom.title = msg;
37284                 break;
37285             case 'under':
37286                 if(!this.errorEl){
37287                     var elp = this.el.findParent('.x-form-element', 5, true);
37288                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37289                     this.errorEl.setWidth(elp.getWidth(true)-20);
37290                 }
37291                 this.errorEl.update(msg);
37292                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37293                 break;
37294             case 'side':
37295                 if(!this.errorIcon){
37296                     var elp = this.el.findParent('.x-form-element', 5, true);
37297                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37298                 }
37299                 this.alignErrorIcon();
37300                 this.errorIcon.dom.qtip = msg;
37301                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37302                 this.errorIcon.show();
37303                 this.on('resize', this.alignErrorIcon, this);
37304                 break;
37305             default:
37306                 var t = Roo.getDom(this.msgTarget);
37307                 t.innerHTML = msg;
37308                 t.style.display = this.msgDisplay;
37309                 break;
37310         }
37311         this.fireEvent('invalid', this, msg);
37312     },
37313
37314     // private
37315     alignErrorIcon : function(){
37316         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37317     },
37318
37319     /**
37320      * Clear any invalid styles/messages for this field
37321      */
37322     clearInvalid : function(){
37323         if(!this.rendered || this.preventMark){ // not rendered
37324             return;
37325         }
37326         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37327         
37328         obj.el.removeClass(this.invalidClass);
37329         switch(this.msgTarget){
37330             case 'qtip':
37331                 obj.el.dom.qtip = '';
37332                 break;
37333             case 'title':
37334                 this.el.dom.title = '';
37335                 break;
37336             case 'under':
37337                 if(this.errorEl){
37338                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37339                 }
37340                 break;
37341             case 'side':
37342                 if(this.errorIcon){
37343                     this.errorIcon.dom.qtip = '';
37344                     this.errorIcon.hide();
37345                     this.un('resize', this.alignErrorIcon, this);
37346                 }
37347                 break;
37348             default:
37349                 var t = Roo.getDom(this.msgTarget);
37350                 t.innerHTML = '';
37351                 t.style.display = 'none';
37352                 break;
37353         }
37354         this.fireEvent('valid', this);
37355     },
37356
37357     /**
37358      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37359      * @return {Mixed} value The field value
37360      */
37361     getRawValue : function(){
37362         var v = this.el.getValue();
37363         
37364         return v;
37365     },
37366
37367     /**
37368      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37369      * @return {Mixed} value The field value
37370      */
37371     getValue : function(){
37372         var v = this.el.getValue();
37373          
37374         return v;
37375     },
37376
37377     /**
37378      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37379      * @param {Mixed} value The value to set
37380      */
37381     setRawValue : function(v){
37382         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37383     },
37384
37385     /**
37386      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37387      * @param {Mixed} value The value to set
37388      */
37389     setValue : function(v){
37390         this.value = v;
37391         if(this.rendered){
37392             this.el.dom.value = (v === null || v === undefined ? '' : v);
37393              this.validate();
37394         }
37395     },
37396
37397     adjustSize : function(w, h){
37398         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37399         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37400         return s;
37401     },
37402
37403     adjustWidth : function(tag, w){
37404         tag = tag.toLowerCase();
37405         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37406             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37407                 if(tag == 'input'){
37408                     return w + 2;
37409                 }
37410                 if(tag == 'textarea'){
37411                     return w-2;
37412                 }
37413             }else if(Roo.isOpera){
37414                 if(tag == 'input'){
37415                     return w + 2;
37416                 }
37417                 if(tag == 'textarea'){
37418                     return w-2;
37419                 }
37420             }
37421         }
37422         return w;
37423     }
37424 });
37425
37426
37427 // anything other than normal should be considered experimental
37428 Roo.form.Field.msgFx = {
37429     normal : {
37430         show: function(msgEl, f){
37431             msgEl.setDisplayed('block');
37432         },
37433
37434         hide : function(msgEl, f){
37435             msgEl.setDisplayed(false).update('');
37436         }
37437     },
37438
37439     slide : {
37440         show: function(msgEl, f){
37441             msgEl.slideIn('t', {stopFx:true});
37442         },
37443
37444         hide : function(msgEl, f){
37445             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37446         }
37447     },
37448
37449     slideRight : {
37450         show: function(msgEl, f){
37451             msgEl.fixDisplay();
37452             msgEl.alignTo(f.el, 'tl-tr');
37453             msgEl.slideIn('l', {stopFx:true});
37454         },
37455
37456         hide : function(msgEl, f){
37457             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37458         }
37459     }
37460 };/*
37461  * Based on:
37462  * Ext JS Library 1.1.1
37463  * Copyright(c) 2006-2007, Ext JS, LLC.
37464  *
37465  * Originally Released Under LGPL - original licence link has changed is not relivant.
37466  *
37467  * Fork - LGPL
37468  * <script type="text/javascript">
37469  */
37470  
37471
37472 /**
37473  * @class Roo.form.TextField
37474  * @extends Roo.form.Field
37475  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37476  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37477  * @constructor
37478  * Creates a new TextField
37479  * @param {Object} config Configuration options
37480  */
37481 Roo.form.TextField = function(config){
37482     Roo.form.TextField.superclass.constructor.call(this, config);
37483     this.addEvents({
37484         /**
37485          * @event autosize
37486          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37487          * according to the default logic, but this event provides a hook for the developer to apply additional
37488          * logic at runtime to resize the field if needed.
37489              * @param {Roo.form.Field} this This text field
37490              * @param {Number} width The new field width
37491              */
37492         autosize : true
37493     });
37494 };
37495
37496 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37497     /**
37498      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37499      */
37500     grow : false,
37501     /**
37502      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37503      */
37504     growMin : 30,
37505     /**
37506      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37507      */
37508     growMax : 800,
37509     /**
37510      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37511      */
37512     vtype : null,
37513     /**
37514      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37515      */
37516     maskRe : null,
37517     /**
37518      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37519      */
37520     disableKeyFilter : false,
37521     /**
37522      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37523      */
37524     allowBlank : true,
37525     /**
37526      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37527      */
37528     minLength : 0,
37529     /**
37530      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37531      */
37532     maxLength : Number.MAX_VALUE,
37533     /**
37534      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37535      */
37536     minLengthText : "The minimum length for this field is {0}",
37537     /**
37538      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37539      */
37540     maxLengthText : "The maximum length for this field is {0}",
37541     /**
37542      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37543      */
37544     selectOnFocus : false,
37545     /**
37546      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37547      */
37548     blankText : "This field is required",
37549     /**
37550      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37551      * If available, this function will be called only after the basic validators all return true, and will be passed the
37552      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37553      */
37554     validator : null,
37555     /**
37556      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37557      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37558      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37559      */
37560     regex : null,
37561     /**
37562      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37563      */
37564     regexText : "",
37565     /**
37566      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37567      */
37568     emptyText : null,
37569    
37570
37571     // private
37572     initEvents : function()
37573     {
37574         if (this.emptyText) {
37575             this.el.attr('placeholder', this.emptyText);
37576         }
37577         
37578         Roo.form.TextField.superclass.initEvents.call(this);
37579         if(this.validationEvent == 'keyup'){
37580             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37581             this.el.on('keyup', this.filterValidation, this);
37582         }
37583         else if(this.validationEvent !== false){
37584             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37585         }
37586         
37587         if(this.selectOnFocus){
37588             this.on("focus", this.preFocus, this);
37589             
37590         }
37591         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37592             this.el.on("keypress", this.filterKeys, this);
37593         }
37594         if(this.grow){
37595             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37596             this.el.on("click", this.autoSize,  this);
37597         }
37598         if(this.el.is('input[type=password]') && Roo.isSafari){
37599             this.el.on('keydown', this.SafariOnKeyDown, this);
37600         }
37601     },
37602
37603     processValue : function(value){
37604         if(this.stripCharsRe){
37605             var newValue = value.replace(this.stripCharsRe, '');
37606             if(newValue !== value){
37607                 this.setRawValue(newValue);
37608                 return newValue;
37609             }
37610         }
37611         return value;
37612     },
37613
37614     filterValidation : function(e){
37615         if(!e.isNavKeyPress()){
37616             this.validationTask.delay(this.validationDelay);
37617         }
37618     },
37619
37620     // private
37621     onKeyUp : function(e){
37622         if(!e.isNavKeyPress()){
37623             this.autoSize();
37624         }
37625     },
37626
37627     /**
37628      * Resets the current field value to the originally-loaded value and clears any validation messages.
37629      *  
37630      */
37631     reset : function(){
37632         Roo.form.TextField.superclass.reset.call(this);
37633        
37634     },
37635
37636     
37637     // private
37638     preFocus : function(){
37639         
37640         if(this.selectOnFocus){
37641             this.el.dom.select();
37642         }
37643     },
37644
37645     
37646     // private
37647     filterKeys : function(e){
37648         var k = e.getKey();
37649         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37650             return;
37651         }
37652         var c = e.getCharCode(), cc = String.fromCharCode(c);
37653         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37654             return;
37655         }
37656         if(!this.maskRe.test(cc)){
37657             e.stopEvent();
37658         }
37659     },
37660
37661     setValue : function(v){
37662         
37663         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37664         
37665         this.autoSize();
37666     },
37667
37668     /**
37669      * Validates a value according to the field's validation rules and marks the field as invalid
37670      * if the validation fails
37671      * @param {Mixed} value The value to validate
37672      * @return {Boolean} True if the value is valid, else false
37673      */
37674     validateValue : function(value){
37675         if(value.length < 1)  { // if it's blank
37676              if(this.allowBlank){
37677                 this.clearInvalid();
37678                 return true;
37679              }else{
37680                 this.markInvalid(this.blankText);
37681                 return false;
37682              }
37683         }
37684         if(value.length < this.minLength){
37685             this.markInvalid(String.format(this.minLengthText, this.minLength));
37686             return false;
37687         }
37688         if(value.length > this.maxLength){
37689             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37690             return false;
37691         }
37692         if(this.vtype){
37693             var vt = Roo.form.VTypes;
37694             if(!vt[this.vtype](value, this)){
37695                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37696                 return false;
37697             }
37698         }
37699         if(typeof this.validator == "function"){
37700             var msg = this.validator(value);
37701             if(msg !== true){
37702                 this.markInvalid(msg);
37703                 return false;
37704             }
37705         }
37706         if(this.regex && !this.regex.test(value)){
37707             this.markInvalid(this.regexText);
37708             return false;
37709         }
37710         return true;
37711     },
37712
37713     /**
37714      * Selects text in this field
37715      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37716      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37717      */
37718     selectText : function(start, end){
37719         var v = this.getRawValue();
37720         if(v.length > 0){
37721             start = start === undefined ? 0 : start;
37722             end = end === undefined ? v.length : end;
37723             var d = this.el.dom;
37724             if(d.setSelectionRange){
37725                 d.setSelectionRange(start, end);
37726             }else if(d.createTextRange){
37727                 var range = d.createTextRange();
37728                 range.moveStart("character", start);
37729                 range.moveEnd("character", v.length-end);
37730                 range.select();
37731             }
37732         }
37733     },
37734
37735     /**
37736      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37737      * This only takes effect if grow = true, and fires the autosize event.
37738      */
37739     autoSize : function(){
37740         if(!this.grow || !this.rendered){
37741             return;
37742         }
37743         if(!this.metrics){
37744             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37745         }
37746         var el = this.el;
37747         var v = el.dom.value;
37748         var d = document.createElement('div');
37749         d.appendChild(document.createTextNode(v));
37750         v = d.innerHTML;
37751         d = null;
37752         v += "&#160;";
37753         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37754         this.el.setWidth(w);
37755         this.fireEvent("autosize", this, w);
37756     },
37757     
37758     // private
37759     SafariOnKeyDown : function(event)
37760     {
37761         // this is a workaround for a password hang bug on chrome/ webkit.
37762         
37763         var isSelectAll = false;
37764         
37765         if(this.el.dom.selectionEnd > 0){
37766             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37767         }
37768         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37769             event.preventDefault();
37770             this.setValue('');
37771             return;
37772         }
37773         
37774         if(isSelectAll){ // backspace and delete key
37775             
37776             event.preventDefault();
37777             // this is very hacky as keydown always get's upper case.
37778             //
37779             var cc = String.fromCharCode(event.getCharCode());
37780             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37781             
37782         }
37783         
37784         
37785     }
37786 });/*
37787  * Based on:
37788  * Ext JS Library 1.1.1
37789  * Copyright(c) 2006-2007, Ext JS, LLC.
37790  *
37791  * Originally Released Under LGPL - original licence link has changed is not relivant.
37792  *
37793  * Fork - LGPL
37794  * <script type="text/javascript">
37795  */
37796  
37797 /**
37798  * @class Roo.form.Hidden
37799  * @extends Roo.form.TextField
37800  * Simple Hidden element used on forms 
37801  * 
37802  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37803  * 
37804  * @constructor
37805  * Creates a new Hidden form element.
37806  * @param {Object} config Configuration options
37807  */
37808
37809
37810
37811 // easy hidden field...
37812 Roo.form.Hidden = function(config){
37813     Roo.form.Hidden.superclass.constructor.call(this, config);
37814 };
37815   
37816 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37817     fieldLabel:      '',
37818     inputType:      'hidden',
37819     width:          50,
37820     allowBlank:     true,
37821     labelSeparator: '',
37822     hidden:         true,
37823     itemCls :       'x-form-item-display-none'
37824
37825
37826 });
37827
37828
37829 /*
37830  * Based on:
37831  * Ext JS Library 1.1.1
37832  * Copyright(c) 2006-2007, Ext JS, LLC.
37833  *
37834  * Originally Released Under LGPL - original licence link has changed is not relivant.
37835  *
37836  * Fork - LGPL
37837  * <script type="text/javascript">
37838  */
37839  
37840 /**
37841  * @class Roo.form.TriggerField
37842  * @extends Roo.form.TextField
37843  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37844  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37845  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37846  * for which you can provide a custom implementation.  For example:
37847  * <pre><code>
37848 var trigger = new Roo.form.TriggerField();
37849 trigger.onTriggerClick = myTriggerFn;
37850 trigger.applyTo('my-field');
37851 </code></pre>
37852  *
37853  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37854  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37855  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37856  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37857  * @constructor
37858  * Create a new TriggerField.
37859  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37860  * to the base TextField)
37861  */
37862 Roo.form.TriggerField = function(config){
37863     this.mimicing = false;
37864     Roo.form.TriggerField.superclass.constructor.call(this, config);
37865 };
37866
37867 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37868     /**
37869      * @cfg {String} triggerClass A CSS class to apply to the trigger
37870      */
37871     /**
37872      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37873      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37874      */
37875     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37876     /**
37877      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37878      */
37879     hideTrigger:false,
37880
37881     /** @cfg {Boolean} grow @hide */
37882     /** @cfg {Number} growMin @hide */
37883     /** @cfg {Number} growMax @hide */
37884
37885     /**
37886      * @hide 
37887      * @method
37888      */
37889     autoSize: Roo.emptyFn,
37890     // private
37891     monitorTab : true,
37892     // private
37893     deferHeight : true,
37894
37895     
37896     actionMode : 'wrap',
37897     // private
37898     onResize : function(w, h){
37899         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37900         if(typeof w == 'number'){
37901             var x = w - this.trigger.getWidth();
37902             this.el.setWidth(this.adjustWidth('input', x));
37903             this.trigger.setStyle('left', x+'px');
37904         }
37905     },
37906
37907     // private
37908     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37909
37910     // private
37911     getResizeEl : function(){
37912         return this.wrap;
37913     },
37914
37915     // private
37916     getPositionEl : function(){
37917         return this.wrap;
37918     },
37919
37920     // private
37921     alignErrorIcon : function(){
37922         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37923     },
37924
37925     // private
37926     onRender : function(ct, position){
37927         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37928         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37929         this.trigger = this.wrap.createChild(this.triggerConfig ||
37930                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37931         if(this.hideTrigger){
37932             this.trigger.setDisplayed(false);
37933         }
37934         this.initTrigger();
37935         if(!this.width){
37936             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37937         }
37938     },
37939
37940     // private
37941     initTrigger : function(){
37942         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37943         this.trigger.addClassOnOver('x-form-trigger-over');
37944         this.trigger.addClassOnClick('x-form-trigger-click');
37945     },
37946
37947     // private
37948     onDestroy : function(){
37949         if(this.trigger){
37950             this.trigger.removeAllListeners();
37951             this.trigger.remove();
37952         }
37953         if(this.wrap){
37954             this.wrap.remove();
37955         }
37956         Roo.form.TriggerField.superclass.onDestroy.call(this);
37957     },
37958
37959     // private
37960     onFocus : function(){
37961         Roo.form.TriggerField.superclass.onFocus.call(this);
37962         if(!this.mimicing){
37963             this.wrap.addClass('x-trigger-wrap-focus');
37964             this.mimicing = true;
37965             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37966             if(this.monitorTab){
37967                 this.el.on("keydown", this.checkTab, this);
37968             }
37969         }
37970     },
37971
37972     // private
37973     checkTab : function(e){
37974         if(e.getKey() == e.TAB){
37975             this.triggerBlur();
37976         }
37977     },
37978
37979     // private
37980     onBlur : function(){
37981         // do nothing
37982     },
37983
37984     // private
37985     mimicBlur : function(e, t){
37986         if(!this.wrap.contains(t) && this.validateBlur()){
37987             this.triggerBlur();
37988         }
37989     },
37990
37991     // private
37992     triggerBlur : function(){
37993         this.mimicing = false;
37994         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37995         if(this.monitorTab){
37996             this.el.un("keydown", this.checkTab, this);
37997         }
37998         this.wrap.removeClass('x-trigger-wrap-focus');
37999         Roo.form.TriggerField.superclass.onBlur.call(this);
38000     },
38001
38002     // private
38003     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38004     validateBlur : function(e, t){
38005         return true;
38006     },
38007
38008     // private
38009     onDisable : function(){
38010         Roo.form.TriggerField.superclass.onDisable.call(this);
38011         if(this.wrap){
38012             this.wrap.addClass('x-item-disabled');
38013         }
38014     },
38015
38016     // private
38017     onEnable : function(){
38018         Roo.form.TriggerField.superclass.onEnable.call(this);
38019         if(this.wrap){
38020             this.wrap.removeClass('x-item-disabled');
38021         }
38022     },
38023
38024     // private
38025     onShow : function(){
38026         var ae = this.getActionEl();
38027         
38028         if(ae){
38029             ae.dom.style.display = '';
38030             ae.dom.style.visibility = 'visible';
38031         }
38032     },
38033
38034     // private
38035     
38036     onHide : function(){
38037         var ae = this.getActionEl();
38038         ae.dom.style.display = 'none';
38039     },
38040
38041     /**
38042      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38043      * by an implementing function.
38044      * @method
38045      * @param {EventObject} e
38046      */
38047     onTriggerClick : Roo.emptyFn
38048 });
38049
38050 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38051 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38052 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38053 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38054     initComponent : function(){
38055         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38056
38057         this.triggerConfig = {
38058             tag:'span', cls:'x-form-twin-triggers', cn:[
38059             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38060             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38061         ]};
38062     },
38063
38064     getTrigger : function(index){
38065         return this.triggers[index];
38066     },
38067
38068     initTrigger : function(){
38069         var ts = this.trigger.select('.x-form-trigger', true);
38070         this.wrap.setStyle('overflow', 'hidden');
38071         var triggerField = this;
38072         ts.each(function(t, all, index){
38073             t.hide = function(){
38074                 var w = triggerField.wrap.getWidth();
38075                 this.dom.style.display = 'none';
38076                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38077             };
38078             t.show = function(){
38079                 var w = triggerField.wrap.getWidth();
38080                 this.dom.style.display = '';
38081                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38082             };
38083             var triggerIndex = 'Trigger'+(index+1);
38084
38085             if(this['hide'+triggerIndex]){
38086                 t.dom.style.display = 'none';
38087             }
38088             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38089             t.addClassOnOver('x-form-trigger-over');
38090             t.addClassOnClick('x-form-trigger-click');
38091         }, this);
38092         this.triggers = ts.elements;
38093     },
38094
38095     onTrigger1Click : Roo.emptyFn,
38096     onTrigger2Click : Roo.emptyFn
38097 });/*
38098  * Based on:
38099  * Ext JS Library 1.1.1
38100  * Copyright(c) 2006-2007, Ext JS, LLC.
38101  *
38102  * Originally Released Under LGPL - original licence link has changed is not relivant.
38103  *
38104  * Fork - LGPL
38105  * <script type="text/javascript">
38106  */
38107  
38108 /**
38109  * @class Roo.form.TextArea
38110  * @extends Roo.form.TextField
38111  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38112  * support for auto-sizing.
38113  * @constructor
38114  * Creates a new TextArea
38115  * @param {Object} config Configuration options
38116  */
38117 Roo.form.TextArea = function(config){
38118     Roo.form.TextArea.superclass.constructor.call(this, config);
38119     // these are provided exchanges for backwards compat
38120     // minHeight/maxHeight were replaced by growMin/growMax to be
38121     // compatible with TextField growing config values
38122     if(this.minHeight !== undefined){
38123         this.growMin = this.minHeight;
38124     }
38125     if(this.maxHeight !== undefined){
38126         this.growMax = this.maxHeight;
38127     }
38128 };
38129
38130 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38131     /**
38132      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38133      */
38134     growMin : 60,
38135     /**
38136      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38137      */
38138     growMax: 1000,
38139     /**
38140      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38141      * in the field (equivalent to setting overflow: hidden, defaults to false)
38142      */
38143     preventScrollbars: false,
38144     /**
38145      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38146      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38147      */
38148
38149     // private
38150     onRender : function(ct, position){
38151         if(!this.el){
38152             this.defaultAutoCreate = {
38153                 tag: "textarea",
38154                 style:"width:300px;height:60px;",
38155                 autocomplete: "off"
38156             };
38157         }
38158         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38159         if(this.grow){
38160             this.textSizeEl = Roo.DomHelper.append(document.body, {
38161                 tag: "pre", cls: "x-form-grow-sizer"
38162             });
38163             if(this.preventScrollbars){
38164                 this.el.setStyle("overflow", "hidden");
38165             }
38166             this.el.setHeight(this.growMin);
38167         }
38168     },
38169
38170     onDestroy : function(){
38171         if(this.textSizeEl){
38172             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38173         }
38174         Roo.form.TextArea.superclass.onDestroy.call(this);
38175     },
38176
38177     // private
38178     onKeyUp : function(e){
38179         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38180             this.autoSize();
38181         }
38182     },
38183
38184     /**
38185      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38186      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38187      */
38188     autoSize : function(){
38189         if(!this.grow || !this.textSizeEl){
38190             return;
38191         }
38192         var el = this.el;
38193         var v = el.dom.value;
38194         var ts = this.textSizeEl;
38195
38196         ts.innerHTML = '';
38197         ts.appendChild(document.createTextNode(v));
38198         v = ts.innerHTML;
38199
38200         Roo.fly(ts).setWidth(this.el.getWidth());
38201         if(v.length < 1){
38202             v = "&#160;&#160;";
38203         }else{
38204             if(Roo.isIE){
38205                 v = v.replace(/\n/g, '<p>&#160;</p>');
38206             }
38207             v += "&#160;\n&#160;";
38208         }
38209         ts.innerHTML = v;
38210         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38211         if(h != this.lastHeight){
38212             this.lastHeight = h;
38213             this.el.setHeight(h);
38214             this.fireEvent("autosize", this, h);
38215         }
38216     }
38217 });/*
38218  * Based on:
38219  * Ext JS Library 1.1.1
38220  * Copyright(c) 2006-2007, Ext JS, LLC.
38221  *
38222  * Originally Released Under LGPL - original licence link has changed is not relivant.
38223  *
38224  * Fork - LGPL
38225  * <script type="text/javascript">
38226  */
38227  
38228
38229 /**
38230  * @class Roo.form.NumberField
38231  * @extends Roo.form.TextField
38232  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38233  * @constructor
38234  * Creates a new NumberField
38235  * @param {Object} config Configuration options
38236  */
38237 Roo.form.NumberField = function(config){
38238     Roo.form.NumberField.superclass.constructor.call(this, config);
38239 };
38240
38241 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38242     /**
38243      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38244      */
38245     fieldClass: "x-form-field x-form-num-field",
38246     /**
38247      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38248      */
38249     allowDecimals : true,
38250     /**
38251      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38252      */
38253     decimalSeparator : ".",
38254     /**
38255      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38256      */
38257     decimalPrecision : 2,
38258     /**
38259      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38260      */
38261     allowNegative : true,
38262     /**
38263      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38264      */
38265     minValue : Number.NEGATIVE_INFINITY,
38266     /**
38267      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38268      */
38269     maxValue : Number.MAX_VALUE,
38270     /**
38271      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38272      */
38273     minText : "The minimum value for this field is {0}",
38274     /**
38275      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38276      */
38277     maxText : "The maximum value for this field is {0}",
38278     /**
38279      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38280      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38281      */
38282     nanText : "{0} is not a valid number",
38283
38284     // private
38285     initEvents : function(){
38286         Roo.form.NumberField.superclass.initEvents.call(this);
38287         var allowed = "0123456789";
38288         if(this.allowDecimals){
38289             allowed += this.decimalSeparator;
38290         }
38291         if(this.allowNegative){
38292             allowed += "-";
38293         }
38294         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38295         var keyPress = function(e){
38296             var k = e.getKey();
38297             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38298                 return;
38299             }
38300             var c = e.getCharCode();
38301             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38302                 e.stopEvent();
38303             }
38304         };
38305         this.el.on("keypress", keyPress, this);
38306     },
38307
38308     // private
38309     validateValue : function(value){
38310         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38311             return false;
38312         }
38313         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38314              return true;
38315         }
38316         var num = this.parseValue(value);
38317         if(isNaN(num)){
38318             this.markInvalid(String.format(this.nanText, value));
38319             return false;
38320         }
38321         if(num < this.minValue){
38322             this.markInvalid(String.format(this.minText, this.minValue));
38323             return false;
38324         }
38325         if(num > this.maxValue){
38326             this.markInvalid(String.format(this.maxText, this.maxValue));
38327             return false;
38328         }
38329         return true;
38330     },
38331
38332     getValue : function(){
38333         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38334     },
38335
38336     // private
38337     parseValue : function(value){
38338         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38339         return isNaN(value) ? '' : value;
38340     },
38341
38342     // private
38343     fixPrecision : function(value){
38344         var nan = isNaN(value);
38345         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38346             return nan ? '' : value;
38347         }
38348         return parseFloat(value).toFixed(this.decimalPrecision);
38349     },
38350
38351     setValue : function(v){
38352         v = this.fixPrecision(v);
38353         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38354     },
38355
38356     // private
38357     decimalPrecisionFcn : function(v){
38358         return Math.floor(v);
38359     },
38360
38361     beforeBlur : function(){
38362         var v = this.parseValue(this.getRawValue());
38363         if(v){
38364             this.setValue(v);
38365         }
38366     }
38367 });/*
38368  * Based on:
38369  * Ext JS Library 1.1.1
38370  * Copyright(c) 2006-2007, Ext JS, LLC.
38371  *
38372  * Originally Released Under LGPL - original licence link has changed is not relivant.
38373  *
38374  * Fork - LGPL
38375  * <script type="text/javascript">
38376  */
38377  
38378 /**
38379  * @class Roo.form.DateField
38380  * @extends Roo.form.TriggerField
38381  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38382 * @constructor
38383 * Create a new DateField
38384 * @param {Object} config
38385  */
38386 Roo.form.DateField = function(config){
38387     Roo.form.DateField.superclass.constructor.call(this, config);
38388     
38389       this.addEvents({
38390          
38391         /**
38392          * @event select
38393          * Fires when a date is selected
38394              * @param {Roo.form.DateField} combo This combo box
38395              * @param {Date} date The date selected
38396              */
38397         'select' : true
38398          
38399     });
38400     
38401     
38402     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38403     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38404     this.ddMatch = null;
38405     if(this.disabledDates){
38406         var dd = this.disabledDates;
38407         var re = "(?:";
38408         for(var i = 0; i < dd.length; i++){
38409             re += dd[i];
38410             if(i != dd.length-1) re += "|";
38411         }
38412         this.ddMatch = new RegExp(re + ")");
38413     }
38414 };
38415
38416 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38417     /**
38418      * @cfg {String} format
38419      * The default date format string which can be overriden for localization support.  The format must be
38420      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38421      */
38422     format : "m/d/y",
38423     /**
38424      * @cfg {String} altFormats
38425      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38426      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38427      */
38428     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38429     /**
38430      * @cfg {Array} disabledDays
38431      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38432      */
38433     disabledDays : null,
38434     /**
38435      * @cfg {String} disabledDaysText
38436      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38437      */
38438     disabledDaysText : "Disabled",
38439     /**
38440      * @cfg {Array} disabledDates
38441      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38442      * expression so they are very powerful. Some examples:
38443      * <ul>
38444      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38445      * <li>["03/08", "09/16"] would disable those days for every year</li>
38446      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38447      * <li>["03/../2006"] would disable every day in March 2006</li>
38448      * <li>["^03"] would disable every day in every March</li>
38449      * </ul>
38450      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38451      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38452      */
38453     disabledDates : null,
38454     /**
38455      * @cfg {String} disabledDatesText
38456      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38457      */
38458     disabledDatesText : "Disabled",
38459     /**
38460      * @cfg {Date/String} minValue
38461      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38462      * valid format (defaults to null).
38463      */
38464     minValue : null,
38465     /**
38466      * @cfg {Date/String} maxValue
38467      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38468      * valid format (defaults to null).
38469      */
38470     maxValue : null,
38471     /**
38472      * @cfg {String} minText
38473      * The error text to display when the date in the cell is before minValue (defaults to
38474      * 'The date in this field must be after {minValue}').
38475      */
38476     minText : "The date in this field must be equal to or after {0}",
38477     /**
38478      * @cfg {String} maxText
38479      * The error text to display when the date in the cell is after maxValue (defaults to
38480      * 'The date in this field must be before {maxValue}').
38481      */
38482     maxText : "The date in this field must be equal to or before {0}",
38483     /**
38484      * @cfg {String} invalidText
38485      * The error text to display when the date in the field is invalid (defaults to
38486      * '{value} is not a valid date - it must be in the format {format}').
38487      */
38488     invalidText : "{0} is not a valid date - it must be in the format {1}",
38489     /**
38490      * @cfg {String} triggerClass
38491      * An additional CSS class used to style the trigger button.  The trigger will always get the
38492      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38493      * which displays a calendar icon).
38494      */
38495     triggerClass : 'x-form-date-trigger',
38496     
38497
38498     /**
38499      * @cfg {Boolean} useIso
38500      * if enabled, then the date field will use a hidden field to store the 
38501      * real value as iso formated date. default (false)
38502      */ 
38503     useIso : false,
38504     /**
38505      * @cfg {String/Object} autoCreate
38506      * A DomHelper element spec, or true for a default element spec (defaults to
38507      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38508      */ 
38509     // private
38510     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38511     
38512     // private
38513     hiddenField: false,
38514     
38515     onRender : function(ct, position)
38516     {
38517         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38518         if (this.useIso) {
38519             //this.el.dom.removeAttribute('name'); 
38520             Roo.log("Changing name?");
38521             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38522             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38523                     'before', true);
38524             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38525             // prevent input submission
38526             this.hiddenName = this.name;
38527         }
38528             
38529             
38530     },
38531     
38532     // private
38533     validateValue : function(value)
38534     {
38535         value = this.formatDate(value);
38536         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38537             Roo.log('super failed');
38538             return false;
38539         }
38540         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38541              return true;
38542         }
38543         var svalue = value;
38544         value = this.parseDate(value);
38545         if(!value){
38546             Roo.log('parse date failed' + svalue);
38547             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38548             return false;
38549         }
38550         var time = value.getTime();
38551         if(this.minValue && time < this.minValue.getTime()){
38552             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38553             return false;
38554         }
38555         if(this.maxValue && time > this.maxValue.getTime()){
38556             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38557             return false;
38558         }
38559         if(this.disabledDays){
38560             var day = value.getDay();
38561             for(var i = 0; i < this.disabledDays.length; i++) {
38562                 if(day === this.disabledDays[i]){
38563                     this.markInvalid(this.disabledDaysText);
38564                     return false;
38565                 }
38566             }
38567         }
38568         var fvalue = this.formatDate(value);
38569         if(this.ddMatch && this.ddMatch.test(fvalue)){
38570             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38571             return false;
38572         }
38573         return true;
38574     },
38575
38576     // private
38577     // Provides logic to override the default TriggerField.validateBlur which just returns true
38578     validateBlur : function(){
38579         return !this.menu || !this.menu.isVisible();
38580     },
38581     
38582     getName: function()
38583     {
38584         // returns hidden if it's set..
38585         if (!this.rendered) {return ''};
38586         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38587         
38588     },
38589
38590     /**
38591      * Returns the current date value of the date field.
38592      * @return {Date} The date value
38593      */
38594     getValue : function(){
38595         
38596         return  this.hiddenField ?
38597                 this.hiddenField.value :
38598                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38599     },
38600
38601     /**
38602      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38603      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38604      * (the default format used is "m/d/y").
38605      * <br />Usage:
38606      * <pre><code>
38607 //All of these calls set the same date value (May 4, 2006)
38608
38609 //Pass a date object:
38610 var dt = new Date('5/4/06');
38611 dateField.setValue(dt);
38612
38613 //Pass a date string (default format):
38614 dateField.setValue('5/4/06');
38615
38616 //Pass a date string (custom format):
38617 dateField.format = 'Y-m-d';
38618 dateField.setValue('2006-5-4');
38619 </code></pre>
38620      * @param {String/Date} date The date or valid date string
38621      */
38622     setValue : function(date){
38623         if (this.hiddenField) {
38624             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38625         }
38626         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38627         // make sure the value field is always stored as a date..
38628         this.value = this.parseDate(date);
38629         
38630         
38631     },
38632
38633     // private
38634     parseDate : function(value){
38635         if(!value || value instanceof Date){
38636             return value;
38637         }
38638         var v = Date.parseDate(value, this.format);
38639          if (!v && this.useIso) {
38640             v = Date.parseDate(value, 'Y-m-d');
38641         }
38642         if(!v && this.altFormats){
38643             if(!this.altFormatsArray){
38644                 this.altFormatsArray = this.altFormats.split("|");
38645             }
38646             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38647                 v = Date.parseDate(value, this.altFormatsArray[i]);
38648             }
38649         }
38650         return v;
38651     },
38652
38653     // private
38654     formatDate : function(date, fmt){
38655         return (!date || !(date instanceof Date)) ?
38656                date : date.dateFormat(fmt || this.format);
38657     },
38658
38659     // private
38660     menuListeners : {
38661         select: function(m, d){
38662             
38663             this.setValue(d);
38664             this.fireEvent('select', this, d);
38665         },
38666         show : function(){ // retain focus styling
38667             this.onFocus();
38668         },
38669         hide : function(){
38670             this.focus.defer(10, this);
38671             var ml = this.menuListeners;
38672             this.menu.un("select", ml.select,  this);
38673             this.menu.un("show", ml.show,  this);
38674             this.menu.un("hide", ml.hide,  this);
38675         }
38676     },
38677
38678     // private
38679     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38680     onTriggerClick : function(){
38681         if(this.disabled){
38682             return;
38683         }
38684         if(this.menu == null){
38685             this.menu = new Roo.menu.DateMenu();
38686         }
38687         Roo.apply(this.menu.picker,  {
38688             showClear: this.allowBlank,
38689             minDate : this.minValue,
38690             maxDate : this.maxValue,
38691             disabledDatesRE : this.ddMatch,
38692             disabledDatesText : this.disabledDatesText,
38693             disabledDays : this.disabledDays,
38694             disabledDaysText : this.disabledDaysText,
38695             format : this.useIso ? 'Y-m-d' : this.format,
38696             minText : String.format(this.minText, this.formatDate(this.minValue)),
38697             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38698         });
38699         this.menu.on(Roo.apply({}, this.menuListeners, {
38700             scope:this
38701         }));
38702         this.menu.picker.setValue(this.getValue() || new Date());
38703         this.menu.show(this.el, "tl-bl?");
38704     },
38705
38706     beforeBlur : function(){
38707         var v = this.parseDate(this.getRawValue());
38708         if(v){
38709             this.setValue(v);
38710         }
38711     },
38712
38713     /*@
38714      * overide
38715      * 
38716      */
38717     isDirty : function() {
38718         if(this.disabled) {
38719             return false;
38720         }
38721         
38722         if(typeof(this.startValue) === 'undefined'){
38723             return false;
38724         }
38725         
38726         return String(this.getValue()) !== String(this.startValue);
38727         
38728     }
38729 });/*
38730  * Based on:
38731  * Ext JS Library 1.1.1
38732  * Copyright(c) 2006-2007, Ext JS, LLC.
38733  *
38734  * Originally Released Under LGPL - original licence link has changed is not relivant.
38735  *
38736  * Fork - LGPL
38737  * <script type="text/javascript">
38738  */
38739  
38740 /**
38741  * @class Roo.form.MonthField
38742  * @extends Roo.form.TriggerField
38743  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38744 * @constructor
38745 * Create a new MonthField
38746 * @param {Object} config
38747  */
38748 Roo.form.MonthField = function(config){
38749     
38750     Roo.form.MonthField.superclass.constructor.call(this, config);
38751     
38752       this.addEvents({
38753          
38754         /**
38755          * @event select
38756          * Fires when a date is selected
38757              * @param {Roo.form.MonthFieeld} combo This combo box
38758              * @param {Date} date The date selected
38759              */
38760         'select' : true
38761          
38762     });
38763     
38764     
38765     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38766     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38767     this.ddMatch = null;
38768     if(this.disabledDates){
38769         var dd = this.disabledDates;
38770         var re = "(?:";
38771         for(var i = 0; i < dd.length; i++){
38772             re += dd[i];
38773             if(i != dd.length-1) re += "|";
38774         }
38775         this.ddMatch = new RegExp(re + ")");
38776     }
38777 };
38778
38779 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38780     /**
38781      * @cfg {String} format
38782      * The default date format string which can be overriden for localization support.  The format must be
38783      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38784      */
38785     format : "M Y",
38786     /**
38787      * @cfg {String} altFormats
38788      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38789      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38790      */
38791     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38792     /**
38793      * @cfg {Array} disabledDays
38794      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38795      */
38796     disabledDays : [0,1,2,3,4,5,6],
38797     /**
38798      * @cfg {String} disabledDaysText
38799      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38800      */
38801     disabledDaysText : "Disabled",
38802     /**
38803      * @cfg {Array} disabledDates
38804      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38805      * expression so they are very powerful. Some examples:
38806      * <ul>
38807      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38808      * <li>["03/08", "09/16"] would disable those days for every year</li>
38809      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38810      * <li>["03/../2006"] would disable every day in March 2006</li>
38811      * <li>["^03"] would disable every day in every March</li>
38812      * </ul>
38813      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38814      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38815      */
38816     disabledDates : null,
38817     /**
38818      * @cfg {String} disabledDatesText
38819      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38820      */
38821     disabledDatesText : "Disabled",
38822     /**
38823      * @cfg {Date/String} minValue
38824      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38825      * valid format (defaults to null).
38826      */
38827     minValue : null,
38828     /**
38829      * @cfg {Date/String} maxValue
38830      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38831      * valid format (defaults to null).
38832      */
38833     maxValue : null,
38834     /**
38835      * @cfg {String} minText
38836      * The error text to display when the date in the cell is before minValue (defaults to
38837      * 'The date in this field must be after {minValue}').
38838      */
38839     minText : "The date in this field must be equal to or after {0}",
38840     /**
38841      * @cfg {String} maxTextf
38842      * The error text to display when the date in the cell is after maxValue (defaults to
38843      * 'The date in this field must be before {maxValue}').
38844      */
38845     maxText : "The date in this field must be equal to or before {0}",
38846     /**
38847      * @cfg {String} invalidText
38848      * The error text to display when the date in the field is invalid (defaults to
38849      * '{value} is not a valid date - it must be in the format {format}').
38850      */
38851     invalidText : "{0} is not a valid date - it must be in the format {1}",
38852     /**
38853      * @cfg {String} triggerClass
38854      * An additional CSS class used to style the trigger button.  The trigger will always get the
38855      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38856      * which displays a calendar icon).
38857      */
38858     triggerClass : 'x-form-date-trigger',
38859     
38860
38861     /**
38862      * @cfg {Boolean} useIso
38863      * if enabled, then the date field will use a hidden field to store the 
38864      * real value as iso formated date. default (true)
38865      */ 
38866     useIso : true,
38867     /**
38868      * @cfg {String/Object} autoCreate
38869      * A DomHelper element spec, or true for a default element spec (defaults to
38870      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38871      */ 
38872     // private
38873     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38874     
38875     // private
38876     hiddenField: false,
38877     
38878     hideMonthPicker : false,
38879     
38880     onRender : function(ct, position)
38881     {
38882         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38883         if (this.useIso) {
38884             this.el.dom.removeAttribute('name'); 
38885             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38886                     'before', true);
38887             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38888             // prevent input submission
38889             this.hiddenName = this.name;
38890         }
38891             
38892             
38893     },
38894     
38895     // private
38896     validateValue : function(value)
38897     {
38898         value = this.formatDate(value);
38899         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38900             return false;
38901         }
38902         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38903              return true;
38904         }
38905         var svalue = value;
38906         value = this.parseDate(value);
38907         if(!value){
38908             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38909             return false;
38910         }
38911         var time = value.getTime();
38912         if(this.minValue && time < this.minValue.getTime()){
38913             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38914             return false;
38915         }
38916         if(this.maxValue && time > this.maxValue.getTime()){
38917             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38918             return false;
38919         }
38920         /*if(this.disabledDays){
38921             var day = value.getDay();
38922             for(var i = 0; i < this.disabledDays.length; i++) {
38923                 if(day === this.disabledDays[i]){
38924                     this.markInvalid(this.disabledDaysText);
38925                     return false;
38926                 }
38927             }
38928         }
38929         */
38930         var fvalue = this.formatDate(value);
38931         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38932             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38933             return false;
38934         }
38935         */
38936         return true;
38937     },
38938
38939     // private
38940     // Provides logic to override the default TriggerField.validateBlur which just returns true
38941     validateBlur : function(){
38942         return !this.menu || !this.menu.isVisible();
38943     },
38944
38945     /**
38946      * Returns the current date value of the date field.
38947      * @return {Date} The date value
38948      */
38949     getValue : function(){
38950         
38951         
38952         
38953         return  this.hiddenField ?
38954                 this.hiddenField.value :
38955                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38956     },
38957
38958     /**
38959      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38960      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38961      * (the default format used is "m/d/y").
38962      * <br />Usage:
38963      * <pre><code>
38964 //All of these calls set the same date value (May 4, 2006)
38965
38966 //Pass a date object:
38967 var dt = new Date('5/4/06');
38968 monthField.setValue(dt);
38969
38970 //Pass a date string (default format):
38971 monthField.setValue('5/4/06');
38972
38973 //Pass a date string (custom format):
38974 monthField.format = 'Y-m-d';
38975 monthField.setValue('2006-5-4');
38976 </code></pre>
38977      * @param {String/Date} date The date or valid date string
38978      */
38979     setValue : function(date){
38980         Roo.log('month setValue' + date);
38981         // can only be first of month..
38982         
38983         var val = this.parseDate(date);
38984         
38985         if (this.hiddenField) {
38986             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38987         }
38988         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38989         this.value = this.parseDate(date);
38990     },
38991
38992     // private
38993     parseDate : function(value){
38994         if(!value || value instanceof Date){
38995             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38996             return value;
38997         }
38998         var v = Date.parseDate(value, this.format);
38999         if (!v && this.useIso) {
39000             v = Date.parseDate(value, 'Y-m-d');
39001         }
39002         if (v) {
39003             // 
39004             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39005         }
39006         
39007         
39008         if(!v && this.altFormats){
39009             if(!this.altFormatsArray){
39010                 this.altFormatsArray = this.altFormats.split("|");
39011             }
39012             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39013                 v = Date.parseDate(value, this.altFormatsArray[i]);
39014             }
39015         }
39016         return v;
39017     },
39018
39019     // private
39020     formatDate : function(date, fmt){
39021         return (!date || !(date instanceof Date)) ?
39022                date : date.dateFormat(fmt || this.format);
39023     },
39024
39025     // private
39026     menuListeners : {
39027         select: function(m, d){
39028             this.setValue(d);
39029             this.fireEvent('select', this, d);
39030         },
39031         show : function(){ // retain focus styling
39032             this.onFocus();
39033         },
39034         hide : function(){
39035             this.focus.defer(10, this);
39036             var ml = this.menuListeners;
39037             this.menu.un("select", ml.select,  this);
39038             this.menu.un("show", ml.show,  this);
39039             this.menu.un("hide", ml.hide,  this);
39040         }
39041     },
39042     // private
39043     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39044     onTriggerClick : function(){
39045         if(this.disabled){
39046             return;
39047         }
39048         if(this.menu == null){
39049             this.menu = new Roo.menu.DateMenu();
39050            
39051         }
39052         
39053         Roo.apply(this.menu.picker,  {
39054             
39055             showClear: this.allowBlank,
39056             minDate : this.minValue,
39057             maxDate : this.maxValue,
39058             disabledDatesRE : this.ddMatch,
39059             disabledDatesText : this.disabledDatesText,
39060             
39061             format : this.useIso ? 'Y-m-d' : this.format,
39062             minText : String.format(this.minText, this.formatDate(this.minValue)),
39063             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39064             
39065         });
39066          this.menu.on(Roo.apply({}, this.menuListeners, {
39067             scope:this
39068         }));
39069        
39070         
39071         var m = this.menu;
39072         var p = m.picker;
39073         
39074         // hide month picker get's called when we called by 'before hide';
39075         
39076         var ignorehide = true;
39077         p.hideMonthPicker  = function(disableAnim){
39078             if (ignorehide) {
39079                 return;
39080             }
39081              if(this.monthPicker){
39082                 Roo.log("hideMonthPicker called");
39083                 if(disableAnim === true){
39084                     this.monthPicker.hide();
39085                 }else{
39086                     this.monthPicker.slideOut('t', {duration:.2});
39087                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39088                     p.fireEvent("select", this, this.value);
39089                     m.hide();
39090                 }
39091             }
39092         }
39093         
39094         Roo.log('picker set value');
39095         Roo.log(this.getValue());
39096         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39097         m.show(this.el, 'tl-bl?');
39098         ignorehide  = false;
39099         // this will trigger hideMonthPicker..
39100         
39101         
39102         // hidden the day picker
39103         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39104         
39105         
39106         
39107       
39108         
39109         p.showMonthPicker.defer(100, p);
39110     
39111         
39112        
39113     },
39114
39115     beforeBlur : function(){
39116         var v = this.parseDate(this.getRawValue());
39117         if(v){
39118             this.setValue(v);
39119         }
39120     }
39121
39122     /** @cfg {Boolean} grow @hide */
39123     /** @cfg {Number} growMin @hide */
39124     /** @cfg {Number} growMax @hide */
39125     /**
39126      * @hide
39127      * @method autoSize
39128      */
39129 });/*
39130  * Based on:
39131  * Ext JS Library 1.1.1
39132  * Copyright(c) 2006-2007, Ext JS, LLC.
39133  *
39134  * Originally Released Under LGPL - original licence link has changed is not relivant.
39135  *
39136  * Fork - LGPL
39137  * <script type="text/javascript">
39138  */
39139  
39140
39141 /**
39142  * @class Roo.form.ComboBox
39143  * @extends Roo.form.TriggerField
39144  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39145  * @constructor
39146  * Create a new ComboBox.
39147  * @param {Object} config Configuration options
39148  */
39149 Roo.form.ComboBox = function(config){
39150     Roo.form.ComboBox.superclass.constructor.call(this, config);
39151     this.addEvents({
39152         /**
39153          * @event expand
39154          * Fires when the dropdown list is expanded
39155              * @param {Roo.form.ComboBox} combo This combo box
39156              */
39157         'expand' : true,
39158         /**
39159          * @event collapse
39160          * Fires when the dropdown list is collapsed
39161              * @param {Roo.form.ComboBox} combo This combo box
39162              */
39163         'collapse' : true,
39164         /**
39165          * @event beforeselect
39166          * Fires before a list item is selected. Return false to cancel the selection.
39167              * @param {Roo.form.ComboBox} combo This combo box
39168              * @param {Roo.data.Record} record The data record returned from the underlying store
39169              * @param {Number} index The index of the selected item in the dropdown list
39170              */
39171         'beforeselect' : true,
39172         /**
39173          * @event select
39174          * Fires when a list item is selected
39175              * @param {Roo.form.ComboBox} combo This combo box
39176              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39177              * @param {Number} index The index of the selected item in the dropdown list
39178              */
39179         'select' : true,
39180         /**
39181          * @event beforequery
39182          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39183          * The event object passed has these properties:
39184              * @param {Roo.form.ComboBox} combo This combo box
39185              * @param {String} query The query
39186              * @param {Boolean} forceAll true to force "all" query
39187              * @param {Boolean} cancel true to cancel the query
39188              * @param {Object} e The query event object
39189              */
39190         'beforequery': true,
39191          /**
39192          * @event add
39193          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39194              * @param {Roo.form.ComboBox} combo This combo box
39195              */
39196         'add' : true,
39197         /**
39198          * @event edit
39199          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39200              * @param {Roo.form.ComboBox} combo This combo box
39201              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39202              */
39203         'edit' : true
39204         
39205         
39206     });
39207     if(this.transform){
39208         this.allowDomMove = false;
39209         var s = Roo.getDom(this.transform);
39210         if(!this.hiddenName){
39211             this.hiddenName = s.name;
39212         }
39213         if(!this.store){
39214             this.mode = 'local';
39215             var d = [], opts = s.options;
39216             for(var i = 0, len = opts.length;i < len; i++){
39217                 var o = opts[i];
39218                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39219                 if(o.selected) {
39220                     this.value = value;
39221                 }
39222                 d.push([value, o.text]);
39223             }
39224             this.store = new Roo.data.SimpleStore({
39225                 'id': 0,
39226                 fields: ['value', 'text'],
39227                 data : d
39228             });
39229             this.valueField = 'value';
39230             this.displayField = 'text';
39231         }
39232         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39233         if(!this.lazyRender){
39234             this.target = true;
39235             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39236             s.parentNode.removeChild(s); // remove it
39237             this.render(this.el.parentNode);
39238         }else{
39239             s.parentNode.removeChild(s); // remove it
39240         }
39241
39242     }
39243     if (this.store) {
39244         this.store = Roo.factory(this.store, Roo.data);
39245     }
39246     
39247     this.selectedIndex = -1;
39248     if(this.mode == 'local'){
39249         if(config.queryDelay === undefined){
39250             this.queryDelay = 10;
39251         }
39252         if(config.minChars === undefined){
39253             this.minChars = 0;
39254         }
39255     }
39256 };
39257
39258 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39259     /**
39260      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39261      */
39262     /**
39263      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39264      * rendering into an Roo.Editor, defaults to false)
39265      */
39266     /**
39267      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39268      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39269      */
39270     /**
39271      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39272      */
39273     /**
39274      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39275      * the dropdown list (defaults to undefined, with no header element)
39276      */
39277
39278      /**
39279      * @cfg {String/Roo.Template} tpl The template to use to render the output
39280      */
39281      
39282     // private
39283     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39284     /**
39285      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39286      */
39287     listWidth: undefined,
39288     /**
39289      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39290      * mode = 'remote' or 'text' if mode = 'local')
39291      */
39292     displayField: undefined,
39293     /**
39294      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39295      * mode = 'remote' or 'value' if mode = 'local'). 
39296      * Note: use of a valueField requires the user make a selection
39297      * in order for a value to be mapped.
39298      */
39299     valueField: undefined,
39300     
39301     
39302     /**
39303      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39304      * field's data value (defaults to the underlying DOM element's name)
39305      */
39306     hiddenName: undefined,
39307     /**
39308      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39309      */
39310     listClass: '',
39311     /**
39312      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39313      */
39314     selectedClass: 'x-combo-selected',
39315     /**
39316      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39317      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39318      * which displays a downward arrow icon).
39319      */
39320     triggerClass : 'x-form-arrow-trigger',
39321     /**
39322      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39323      */
39324     shadow:'sides',
39325     /**
39326      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39327      * anchor positions (defaults to 'tl-bl')
39328      */
39329     listAlign: 'tl-bl?',
39330     /**
39331      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39332      */
39333     maxHeight: 300,
39334     /**
39335      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39336      * query specified by the allQuery config option (defaults to 'query')
39337      */
39338     triggerAction: 'query',
39339     /**
39340      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39341      * (defaults to 4, does not apply if editable = false)
39342      */
39343     minChars : 4,
39344     /**
39345      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39346      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39347      */
39348     typeAhead: false,
39349     /**
39350      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39351      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39352      */
39353     queryDelay: 500,
39354     /**
39355      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39356      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39357      */
39358     pageSize: 0,
39359     /**
39360      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39361      * when editable = true (defaults to false)
39362      */
39363     selectOnFocus:false,
39364     /**
39365      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39366      */
39367     queryParam: 'query',
39368     /**
39369      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39370      * when mode = 'remote' (defaults to 'Loading...')
39371      */
39372     loadingText: 'Loading...',
39373     /**
39374      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39375      */
39376     resizable: false,
39377     /**
39378      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39379      */
39380     handleHeight : 8,
39381     /**
39382      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39383      * traditional select (defaults to true)
39384      */
39385     editable: true,
39386     /**
39387      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39388      */
39389     allQuery: '',
39390     /**
39391      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39392      */
39393     mode: 'remote',
39394     /**
39395      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39396      * listWidth has a higher value)
39397      */
39398     minListWidth : 70,
39399     /**
39400      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39401      * allow the user to set arbitrary text into the field (defaults to false)
39402      */
39403     forceSelection:false,
39404     /**
39405      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39406      * if typeAhead = true (defaults to 250)
39407      */
39408     typeAheadDelay : 250,
39409     /**
39410      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39411      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39412      */
39413     valueNotFoundText : undefined,
39414     /**
39415      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39416      */
39417     blockFocus : false,
39418     
39419     /**
39420      * @cfg {Boolean} disableClear Disable showing of clear button.
39421      */
39422     disableClear : false,
39423     /**
39424      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39425      */
39426     alwaysQuery : false,
39427     
39428     //private
39429     addicon : false,
39430     editicon: false,
39431     
39432     // element that contains real text value.. (when hidden is used..)
39433      
39434     // private
39435     onRender : function(ct, position){
39436         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39437         if(this.hiddenName){
39438             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39439                     'before', true);
39440             this.hiddenField.value =
39441                 this.hiddenValue !== undefined ? this.hiddenValue :
39442                 this.value !== undefined ? this.value : '';
39443
39444             // prevent input submission
39445             this.el.dom.removeAttribute('name');
39446              
39447              
39448         }
39449         if(Roo.isGecko){
39450             this.el.dom.setAttribute('autocomplete', 'off');
39451         }
39452
39453         var cls = 'x-combo-list';
39454
39455         this.list = new Roo.Layer({
39456             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39457         });
39458
39459         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39460         this.list.setWidth(lw);
39461         this.list.swallowEvent('mousewheel');
39462         this.assetHeight = 0;
39463
39464         if(this.title){
39465             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39466             this.assetHeight += this.header.getHeight();
39467         }
39468
39469         this.innerList = this.list.createChild({cls:cls+'-inner'});
39470         this.innerList.on('mouseover', this.onViewOver, this);
39471         this.innerList.on('mousemove', this.onViewMove, this);
39472         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39473         
39474         if(this.allowBlank && !this.pageSize && !this.disableClear){
39475             this.footer = this.list.createChild({cls:cls+'-ft'});
39476             this.pageTb = new Roo.Toolbar(this.footer);
39477            
39478         }
39479         if(this.pageSize){
39480             this.footer = this.list.createChild({cls:cls+'-ft'});
39481             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39482                     {pageSize: this.pageSize});
39483             
39484         }
39485         
39486         if (this.pageTb && this.allowBlank && !this.disableClear) {
39487             var _this = this;
39488             this.pageTb.add(new Roo.Toolbar.Fill(), {
39489                 cls: 'x-btn-icon x-btn-clear',
39490                 text: '&#160;',
39491                 handler: function()
39492                 {
39493                     _this.collapse();
39494                     _this.clearValue();
39495                     _this.onSelect(false, -1);
39496                 }
39497             });
39498         }
39499         if (this.footer) {
39500             this.assetHeight += this.footer.getHeight();
39501         }
39502         
39503
39504         if(!this.tpl){
39505             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39506         }
39507
39508         this.view = new Roo.View(this.innerList, this.tpl, {
39509             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39510         });
39511
39512         this.view.on('click', this.onViewClick, this);
39513
39514         this.store.on('beforeload', this.onBeforeLoad, this);
39515         this.store.on('load', this.onLoad, this);
39516         this.store.on('loadexception', this.onLoadException, this);
39517
39518         if(this.resizable){
39519             this.resizer = new Roo.Resizable(this.list,  {
39520                pinned:true, handles:'se'
39521             });
39522             this.resizer.on('resize', function(r, w, h){
39523                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39524                 this.listWidth = w;
39525                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39526                 this.restrictHeight();
39527             }, this);
39528             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39529         }
39530         if(!this.editable){
39531             this.editable = true;
39532             this.setEditable(false);
39533         }  
39534         
39535         
39536         if (typeof(this.events.add.listeners) != 'undefined') {
39537             
39538             this.addicon = this.wrap.createChild(
39539                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39540        
39541             this.addicon.on('click', function(e) {
39542                 this.fireEvent('add', this);
39543             }, this);
39544         }
39545         if (typeof(this.events.edit.listeners) != 'undefined') {
39546             
39547             this.editicon = this.wrap.createChild(
39548                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39549             if (this.addicon) {
39550                 this.editicon.setStyle('margin-left', '40px');
39551             }
39552             this.editicon.on('click', function(e) {
39553                 
39554                 // we fire even  if inothing is selected..
39555                 this.fireEvent('edit', this, this.lastData );
39556                 
39557             }, this);
39558         }
39559         
39560         
39561         
39562     },
39563
39564     // private
39565     initEvents : function(){
39566         Roo.form.ComboBox.superclass.initEvents.call(this);
39567
39568         this.keyNav = new Roo.KeyNav(this.el, {
39569             "up" : function(e){
39570                 this.inKeyMode = true;
39571                 this.selectPrev();
39572             },
39573
39574             "down" : function(e){
39575                 if(!this.isExpanded()){
39576                     this.onTriggerClick();
39577                 }else{
39578                     this.inKeyMode = true;
39579                     this.selectNext();
39580                 }
39581             },
39582
39583             "enter" : function(e){
39584                 this.onViewClick();
39585                 //return true;
39586             },
39587
39588             "esc" : function(e){
39589                 this.collapse();
39590             },
39591
39592             "tab" : function(e){
39593                 this.onViewClick(false);
39594                 this.fireEvent("specialkey", this, e);
39595                 return true;
39596             },
39597
39598             scope : this,
39599
39600             doRelay : function(foo, bar, hname){
39601                 if(hname == 'down' || this.scope.isExpanded()){
39602                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39603                 }
39604                 return true;
39605             },
39606
39607             forceKeyDown: true
39608         });
39609         this.queryDelay = Math.max(this.queryDelay || 10,
39610                 this.mode == 'local' ? 10 : 250);
39611         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39612         if(this.typeAhead){
39613             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39614         }
39615         if(this.editable !== false){
39616             this.el.on("keyup", this.onKeyUp, this);
39617         }
39618         if(this.forceSelection){
39619             this.on('blur', this.doForce, this);
39620         }
39621     },
39622
39623     onDestroy : function(){
39624         if(this.view){
39625             this.view.setStore(null);
39626             this.view.el.removeAllListeners();
39627             this.view.el.remove();
39628             this.view.purgeListeners();
39629         }
39630         if(this.list){
39631             this.list.destroy();
39632         }
39633         if(this.store){
39634             this.store.un('beforeload', this.onBeforeLoad, this);
39635             this.store.un('load', this.onLoad, this);
39636             this.store.un('loadexception', this.onLoadException, this);
39637         }
39638         Roo.form.ComboBox.superclass.onDestroy.call(this);
39639     },
39640
39641     // private
39642     fireKey : function(e){
39643         if(e.isNavKeyPress() && !this.list.isVisible()){
39644             this.fireEvent("specialkey", this, e);
39645         }
39646     },
39647
39648     // private
39649     onResize: function(w, h){
39650         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39651         
39652         if(typeof w != 'number'){
39653             // we do not handle it!?!?
39654             return;
39655         }
39656         var tw = this.trigger.getWidth();
39657         tw += this.addicon ? this.addicon.getWidth() : 0;
39658         tw += this.editicon ? this.editicon.getWidth() : 0;
39659         var x = w - tw;
39660         this.el.setWidth( this.adjustWidth('input', x));
39661             
39662         this.trigger.setStyle('left', x+'px');
39663         
39664         if(this.list && this.listWidth === undefined){
39665             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39666             this.list.setWidth(lw);
39667             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39668         }
39669         
39670     
39671         
39672     },
39673
39674     /**
39675      * Allow or prevent the user from directly editing the field text.  If false is passed,
39676      * the user will only be able to select from the items defined in the dropdown list.  This method
39677      * is the runtime equivalent of setting the 'editable' config option at config time.
39678      * @param {Boolean} value True to allow the user to directly edit the field text
39679      */
39680     setEditable : function(value){
39681         if(value == this.editable){
39682             return;
39683         }
39684         this.editable = value;
39685         if(!value){
39686             this.el.dom.setAttribute('readOnly', true);
39687             this.el.on('mousedown', this.onTriggerClick,  this);
39688             this.el.addClass('x-combo-noedit');
39689         }else{
39690             this.el.dom.setAttribute('readOnly', false);
39691             this.el.un('mousedown', this.onTriggerClick,  this);
39692             this.el.removeClass('x-combo-noedit');
39693         }
39694     },
39695
39696     // private
39697     onBeforeLoad : function(){
39698         if(!this.hasFocus){
39699             return;
39700         }
39701         this.innerList.update(this.loadingText ?
39702                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39703         this.restrictHeight();
39704         this.selectedIndex = -1;
39705     },
39706
39707     // private
39708     onLoad : function(){
39709         if(!this.hasFocus){
39710             return;
39711         }
39712         if(this.store.getCount() > 0){
39713             this.expand();
39714             this.restrictHeight();
39715             if(this.lastQuery == this.allQuery){
39716                 if(this.editable){
39717                     this.el.dom.select();
39718                 }
39719                 if(!this.selectByValue(this.value, true)){
39720                     this.select(0, true);
39721                 }
39722             }else{
39723                 this.selectNext();
39724                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39725                     this.taTask.delay(this.typeAheadDelay);
39726                 }
39727             }
39728         }else{
39729             this.onEmptyResults();
39730         }
39731         //this.el.focus();
39732     },
39733     // private
39734     onLoadException : function()
39735     {
39736         this.collapse();
39737         Roo.log(this.store.reader.jsonData);
39738         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39739             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39740         }
39741         
39742         
39743     },
39744     // private
39745     onTypeAhead : function(){
39746         if(this.store.getCount() > 0){
39747             var r = this.store.getAt(0);
39748             var newValue = r.data[this.displayField];
39749             var len = newValue.length;
39750             var selStart = this.getRawValue().length;
39751             if(selStart != len){
39752                 this.setRawValue(newValue);
39753                 this.selectText(selStart, newValue.length);
39754             }
39755         }
39756     },
39757
39758     // private
39759     onSelect : function(record, index){
39760         if(this.fireEvent('beforeselect', this, record, index) !== false){
39761             this.setFromData(index > -1 ? record.data : false);
39762             this.collapse();
39763             this.fireEvent('select', this, record, index);
39764         }
39765     },
39766
39767     /**
39768      * Returns the currently selected field value or empty string if no value is set.
39769      * @return {String} value The selected value
39770      */
39771     getValue : function(){
39772         if(this.valueField){
39773             return typeof this.value != 'undefined' ? this.value : '';
39774         }else{
39775             return Roo.form.ComboBox.superclass.getValue.call(this);
39776         }
39777     },
39778
39779     /**
39780      * Clears any text/value currently set in the field
39781      */
39782     clearValue : function(){
39783         if(this.hiddenField){
39784             this.hiddenField.value = '';
39785         }
39786         this.value = '';
39787         this.setRawValue('');
39788         this.lastSelectionText = '';
39789         
39790     },
39791
39792     /**
39793      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39794      * will be displayed in the field.  If the value does not match the data value of an existing item,
39795      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39796      * Otherwise the field will be blank (although the value will still be set).
39797      * @param {String} value The value to match
39798      */
39799     setValue : function(v){
39800         var text = v;
39801         if(this.valueField){
39802             var r = this.findRecord(this.valueField, v);
39803             if(r){
39804                 text = r.data[this.displayField];
39805             }else if(this.valueNotFoundText !== undefined){
39806                 text = this.valueNotFoundText;
39807             }
39808         }
39809         this.lastSelectionText = text;
39810         if(this.hiddenField){
39811             this.hiddenField.value = v;
39812         }
39813         Roo.form.ComboBox.superclass.setValue.call(this, text);
39814         this.value = v;
39815     },
39816     /**
39817      * @property {Object} the last set data for the element
39818      */
39819     
39820     lastData : false,
39821     /**
39822      * Sets the value of the field based on a object which is related to the record format for the store.
39823      * @param {Object} value the value to set as. or false on reset?
39824      */
39825     setFromData : function(o){
39826         var dv = ''; // display value
39827         var vv = ''; // value value..
39828         this.lastData = o;
39829         if (this.displayField) {
39830             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39831         } else {
39832             // this is an error condition!!!
39833             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39834         }
39835         
39836         if(this.valueField){
39837             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39838         }
39839         if(this.hiddenField){
39840             this.hiddenField.value = vv;
39841             
39842             this.lastSelectionText = dv;
39843             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39844             this.value = vv;
39845             return;
39846         }
39847         // no hidden field.. - we store the value in 'value', but still display
39848         // display field!!!!
39849         this.lastSelectionText = dv;
39850         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39851         this.value = vv;
39852         
39853         
39854     },
39855     // private
39856     reset : function(){
39857         // overridden so that last data is reset..
39858         this.setValue(this.resetValue);
39859         this.clearInvalid();
39860         this.lastData = false;
39861         if (this.view) {
39862             this.view.clearSelections();
39863         }
39864     },
39865     // private
39866     findRecord : function(prop, value){
39867         var record;
39868         if(this.store.getCount() > 0){
39869             this.store.each(function(r){
39870                 if(r.data[prop] == value){
39871                     record = r;
39872                     return false;
39873                 }
39874                 return true;
39875             });
39876         }
39877         return record;
39878     },
39879     
39880     getName: function()
39881     {
39882         // returns hidden if it's set..
39883         if (!this.rendered) {return ''};
39884         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39885         
39886     },
39887     // private
39888     onViewMove : function(e, t){
39889         this.inKeyMode = false;
39890     },
39891
39892     // private
39893     onViewOver : function(e, t){
39894         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39895             return;
39896         }
39897         var item = this.view.findItemFromChild(t);
39898         if(item){
39899             var index = this.view.indexOf(item);
39900             this.select(index, false);
39901         }
39902     },
39903
39904     // private
39905     onViewClick : function(doFocus)
39906     {
39907         var index = this.view.getSelectedIndexes()[0];
39908         var r = this.store.getAt(index);
39909         if(r){
39910             this.onSelect(r, index);
39911         }
39912         if(doFocus !== false && !this.blockFocus){
39913             this.el.focus();
39914         }
39915     },
39916
39917     // private
39918     restrictHeight : function(){
39919         this.innerList.dom.style.height = '';
39920         var inner = this.innerList.dom;
39921         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39922         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39923         this.list.beginUpdate();
39924         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39925         this.list.alignTo(this.el, this.listAlign);
39926         this.list.endUpdate();
39927     },
39928
39929     // private
39930     onEmptyResults : function(){
39931         this.collapse();
39932     },
39933
39934     /**
39935      * Returns true if the dropdown list is expanded, else false.
39936      */
39937     isExpanded : function(){
39938         return this.list.isVisible();
39939     },
39940
39941     /**
39942      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39943      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39944      * @param {String} value The data value of the item to select
39945      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39946      * selected item if it is not currently in view (defaults to true)
39947      * @return {Boolean} True if the value matched an item in the list, else false
39948      */
39949     selectByValue : function(v, scrollIntoView){
39950         if(v !== undefined && v !== null){
39951             var r = this.findRecord(this.valueField || this.displayField, v);
39952             if(r){
39953                 this.select(this.store.indexOf(r), scrollIntoView);
39954                 return true;
39955             }
39956         }
39957         return false;
39958     },
39959
39960     /**
39961      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39962      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39963      * @param {Number} index The zero-based index of the list item to select
39964      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39965      * selected item if it is not currently in view (defaults to true)
39966      */
39967     select : function(index, scrollIntoView){
39968         this.selectedIndex = index;
39969         this.view.select(index);
39970         if(scrollIntoView !== false){
39971             var el = this.view.getNode(index);
39972             if(el){
39973                 this.innerList.scrollChildIntoView(el, false);
39974             }
39975         }
39976     },
39977
39978     // private
39979     selectNext : function(){
39980         var ct = this.store.getCount();
39981         if(ct > 0){
39982             if(this.selectedIndex == -1){
39983                 this.select(0);
39984             }else if(this.selectedIndex < ct-1){
39985                 this.select(this.selectedIndex+1);
39986             }
39987         }
39988     },
39989
39990     // private
39991     selectPrev : function(){
39992         var ct = this.store.getCount();
39993         if(ct > 0){
39994             if(this.selectedIndex == -1){
39995                 this.select(0);
39996             }else if(this.selectedIndex != 0){
39997                 this.select(this.selectedIndex-1);
39998             }
39999         }
40000     },
40001
40002     // private
40003     onKeyUp : function(e){
40004         if(this.editable !== false && !e.isSpecialKey()){
40005             this.lastKey = e.getKey();
40006             this.dqTask.delay(this.queryDelay);
40007         }
40008     },
40009
40010     // private
40011     validateBlur : function(){
40012         return !this.list || !this.list.isVisible();   
40013     },
40014
40015     // private
40016     initQuery : function(){
40017         this.doQuery(this.getRawValue());
40018     },
40019
40020     // private
40021     doForce : function(){
40022         if(this.el.dom.value.length > 0){
40023             this.el.dom.value =
40024                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40025              
40026         }
40027     },
40028
40029     /**
40030      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40031      * query allowing the query action to be canceled if needed.
40032      * @param {String} query The SQL query to execute
40033      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40034      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40035      * saved in the current store (defaults to false)
40036      */
40037     doQuery : function(q, forceAll){
40038         if(q === undefined || q === null){
40039             q = '';
40040         }
40041         var qe = {
40042             query: q,
40043             forceAll: forceAll,
40044             combo: this,
40045             cancel:false
40046         };
40047         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40048             return false;
40049         }
40050         q = qe.query;
40051         forceAll = qe.forceAll;
40052         if(forceAll === true || (q.length >= this.minChars)){
40053             if(this.lastQuery != q || this.alwaysQuery){
40054                 this.lastQuery = q;
40055                 if(this.mode == 'local'){
40056                     this.selectedIndex = -1;
40057                     if(forceAll){
40058                         this.store.clearFilter();
40059                     }else{
40060                         this.store.filter(this.displayField, q);
40061                     }
40062                     this.onLoad();
40063                 }else{
40064                     this.store.baseParams[this.queryParam] = q;
40065                     this.store.load({
40066                         params: this.getParams(q)
40067                     });
40068                     this.expand();
40069                 }
40070             }else{
40071                 this.selectedIndex = -1;
40072                 this.onLoad();   
40073             }
40074         }
40075     },
40076
40077     // private
40078     getParams : function(q){
40079         var p = {};
40080         //p[this.queryParam] = q;
40081         if(this.pageSize){
40082             p.start = 0;
40083             p.limit = this.pageSize;
40084         }
40085         return p;
40086     },
40087
40088     /**
40089      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40090      */
40091     collapse : function(){
40092         if(!this.isExpanded()){
40093             return;
40094         }
40095         this.list.hide();
40096         Roo.get(document).un('mousedown', this.collapseIf, this);
40097         Roo.get(document).un('mousewheel', this.collapseIf, this);
40098         if (!this.editable) {
40099             Roo.get(document).un('keydown', this.listKeyPress, this);
40100         }
40101         this.fireEvent('collapse', this);
40102     },
40103
40104     // private
40105     collapseIf : function(e){
40106         if(!e.within(this.wrap) && !e.within(this.list)){
40107             this.collapse();
40108         }
40109     },
40110
40111     /**
40112      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40113      */
40114     expand : function(){
40115         if(this.isExpanded() || !this.hasFocus){
40116             return;
40117         }
40118         this.list.alignTo(this.el, this.listAlign);
40119         this.list.show();
40120         Roo.get(document).on('mousedown', this.collapseIf, this);
40121         Roo.get(document).on('mousewheel', this.collapseIf, this);
40122         if (!this.editable) {
40123             Roo.get(document).on('keydown', this.listKeyPress, this);
40124         }
40125         
40126         this.fireEvent('expand', this);
40127     },
40128
40129     // private
40130     // Implements the default empty TriggerField.onTriggerClick function
40131     onTriggerClick : function(){
40132         if(this.disabled){
40133             return;
40134         }
40135         if(this.isExpanded()){
40136             this.collapse();
40137             if (!this.blockFocus) {
40138                 this.el.focus();
40139             }
40140             
40141         }else {
40142             this.hasFocus = true;
40143             if(this.triggerAction == 'all') {
40144                 this.doQuery(this.allQuery, true);
40145             } else {
40146                 this.doQuery(this.getRawValue());
40147             }
40148             if (!this.blockFocus) {
40149                 this.el.focus();
40150             }
40151         }
40152     },
40153     listKeyPress : function(e)
40154     {
40155         //Roo.log('listkeypress');
40156         // scroll to first matching element based on key pres..
40157         if (e.isSpecialKey()) {
40158             return false;
40159         }
40160         var k = String.fromCharCode(e.getKey()).toUpperCase();
40161         //Roo.log(k);
40162         var match  = false;
40163         var csel = this.view.getSelectedNodes();
40164         var cselitem = false;
40165         if (csel.length) {
40166             var ix = this.view.indexOf(csel[0]);
40167             cselitem  = this.store.getAt(ix);
40168             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40169                 cselitem = false;
40170             }
40171             
40172         }
40173         
40174         this.store.each(function(v) { 
40175             if (cselitem) {
40176                 // start at existing selection.
40177                 if (cselitem.id == v.id) {
40178                     cselitem = false;
40179                 }
40180                 return;
40181             }
40182                 
40183             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40184                 match = this.store.indexOf(v);
40185                 return false;
40186             }
40187         }, this);
40188         
40189         if (match === false) {
40190             return true; // no more action?
40191         }
40192         // scroll to?
40193         this.view.select(match);
40194         var sn = Roo.get(this.view.getSelectedNodes()[0])
40195         sn.scrollIntoView(sn.dom.parentNode, false);
40196     }
40197
40198     /** 
40199     * @cfg {Boolean} grow 
40200     * @hide 
40201     */
40202     /** 
40203     * @cfg {Number} growMin 
40204     * @hide 
40205     */
40206     /** 
40207     * @cfg {Number} growMax 
40208     * @hide 
40209     */
40210     /**
40211      * @hide
40212      * @method autoSize
40213      */
40214 });/*
40215  * Copyright(c) 2010-2012, Roo J Solutions Limited
40216  *
40217  * Licence LGPL
40218  *
40219  */
40220
40221 /**
40222  * @class Roo.form.ComboBoxArray
40223  * @extends Roo.form.TextField
40224  * A facebook style adder... for lists of email / people / countries  etc...
40225  * pick multiple items from a combo box, and shows each one.
40226  *
40227  *  Fred [x]  Brian [x]  [Pick another |v]
40228  *
40229  *
40230  *  For this to work: it needs various extra information
40231  *    - normal combo problay has
40232  *      name, hiddenName
40233  *    + displayField, valueField
40234  *
40235  *    For our purpose...
40236  *
40237  *
40238  *   If we change from 'extends' to wrapping...
40239  *   
40240  *  
40241  *
40242  
40243  
40244  * @constructor
40245  * Create a new ComboBoxArray.
40246  * @param {Object} config Configuration options
40247  */
40248  
40249
40250 Roo.form.ComboBoxArray = function(config)
40251 {
40252     this.addEvents({
40253         /**
40254          * @event remove
40255          * Fires when remove the value from the list
40256              * @param {Roo.form.ComboBoxArray} _self This combo box array
40257              * @param {Roo.form.ComboBoxArray.Item} item removed item
40258              */
40259         'remove' : true
40260         
40261         
40262     });
40263     
40264     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40265     
40266     this.items = new Roo.util.MixedCollection(false);
40267     
40268     // construct the child combo...
40269     
40270     
40271     
40272     
40273    
40274     
40275 }
40276
40277  
40278 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40279
40280     /**
40281      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40282      */
40283     
40284     lastData : false,
40285     
40286     // behavies liek a hiddne field
40287     inputType:      'hidden',
40288     /**
40289      * @cfg {Number} width The width of the box that displays the selected element
40290      */ 
40291     width:          300,
40292
40293     
40294     
40295     /**
40296      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40297      */
40298     name : false,
40299     /**
40300      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40301      */
40302     hiddenName : false,
40303     
40304     
40305     // private the array of items that are displayed..
40306     items  : false,
40307     // private - the hidden field el.
40308     hiddenEl : false,
40309     // private - the filed el..
40310     el : false,
40311     
40312     //validateValue : function() { return true; }, // all values are ok!
40313     //onAddClick: function() { },
40314     
40315     onRender : function(ct, position) 
40316     {
40317         
40318         // create the standard hidden element
40319         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40320         
40321         
40322         // give fake names to child combo;
40323         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40324         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40325         
40326         this.combo = Roo.factory(this.combo, Roo.form);
40327         this.combo.onRender(ct, position);
40328         if (typeof(this.combo.width) != 'undefined') {
40329             this.combo.onResize(this.combo.width,0);
40330         }
40331         
40332         this.combo.initEvents();
40333         
40334         // assigned so form know we need to do this..
40335         this.store          = this.combo.store;
40336         this.valueField     = this.combo.valueField;
40337         this.displayField   = this.combo.displayField ;
40338         
40339         
40340         this.combo.wrap.addClass('x-cbarray-grp');
40341         
40342         var cbwrap = this.combo.wrap.createChild(
40343             {tag: 'div', cls: 'x-cbarray-cb'},
40344             this.combo.el.dom
40345         );
40346         
40347              
40348         this.hiddenEl = this.combo.wrap.createChild({
40349             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40350         });
40351         this.el = this.combo.wrap.createChild({
40352             tag: 'input',  type:'hidden' , name: this.name, value : ''
40353         });
40354          //   this.el.dom.removeAttribute("name");
40355         
40356         
40357         this.outerWrap = this.combo.wrap;
40358         this.wrap = cbwrap;
40359         
40360         this.outerWrap.setWidth(this.width);
40361         this.outerWrap.dom.removeChild(this.el.dom);
40362         
40363         this.wrap.dom.appendChild(this.el.dom);
40364         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40365         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40366         
40367         this.combo.trigger.setStyle('position','relative');
40368         this.combo.trigger.setStyle('left', '0px');
40369         this.combo.trigger.setStyle('top', '2px');
40370         
40371         this.combo.el.setStyle('vertical-align', 'text-bottom');
40372         
40373         //this.trigger.setStyle('vertical-align', 'top');
40374         
40375         // this should use the code from combo really... on('add' ....)
40376         if (this.adder) {
40377             
40378         
40379             this.adder = this.outerWrap.createChild(
40380                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40381             var _t = this;
40382             this.adder.on('click', function(e) {
40383                 _t.fireEvent('adderclick', this, e);
40384             }, _t);
40385         }
40386         //var _t = this;
40387         //this.adder.on('click', this.onAddClick, _t);
40388         
40389         
40390         this.combo.on('select', function(cb, rec, ix) {
40391             this.addItem(rec.data);
40392             
40393             cb.setValue('');
40394             cb.el.dom.value = '';
40395             //cb.lastData = rec.data;
40396             // add to list
40397             
40398         }, this);
40399         
40400         
40401     },
40402     
40403     
40404     getName: function()
40405     {
40406         // returns hidden if it's set..
40407         if (!this.rendered) {return ''};
40408         return  this.hiddenName ? this.hiddenName : this.name;
40409         
40410     },
40411     
40412     
40413     onResize: function(w, h){
40414         
40415         return;
40416         // not sure if this is needed..
40417         //this.combo.onResize(w,h);
40418         
40419         if(typeof w != 'number'){
40420             // we do not handle it!?!?
40421             return;
40422         }
40423         var tw = this.combo.trigger.getWidth();
40424         tw += this.addicon ? this.addicon.getWidth() : 0;
40425         tw += this.editicon ? this.editicon.getWidth() : 0;
40426         var x = w - tw;
40427         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40428             
40429         this.combo.trigger.setStyle('left', '0px');
40430         
40431         if(this.list && this.listWidth === undefined){
40432             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40433             this.list.setWidth(lw);
40434             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40435         }
40436         
40437     
40438         
40439     },
40440     
40441     addItem: function(rec)
40442     {
40443         var valueField = this.combo.valueField;
40444         var displayField = this.combo.displayField;
40445         if (this.items.indexOfKey(rec[valueField]) > -1) {
40446             //console.log("GOT " + rec.data.id);
40447             return;
40448         }
40449         
40450         var x = new Roo.form.ComboBoxArray.Item({
40451             //id : rec[this.idField],
40452             data : rec,
40453             displayField : displayField ,
40454             tipField : displayField ,
40455             cb : this
40456         });
40457         // use the 
40458         this.items.add(rec[valueField],x);
40459         // add it before the element..
40460         this.updateHiddenEl();
40461         x.render(this.outerWrap, this.wrap.dom);
40462         // add the image handler..
40463     },
40464     
40465     updateHiddenEl : function()
40466     {
40467         this.validate();
40468         if (!this.hiddenEl) {
40469             return;
40470         }
40471         var ar = [];
40472         var idField = this.combo.valueField;
40473         
40474         this.items.each(function(f) {
40475             ar.push(f.data[idField]);
40476            
40477         });
40478         this.hiddenEl.dom.value = ar.join(',');
40479         this.validate();
40480     },
40481     
40482     reset : function()
40483     {
40484         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40485         this.items.each(function(f) {
40486            f.remove(); 
40487         });
40488         this.el.dom.value = '';
40489         if (this.hiddenEl) {
40490             this.hiddenEl.dom.value = '';
40491         }
40492         
40493     },
40494     getValue: function()
40495     {
40496         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40497     },
40498     setValue: function(v) // not a valid action - must use addItems..
40499     {
40500          
40501         this.reset();
40502         
40503         
40504         
40505         if (this.store.isLocal && (typeof(v) == 'string')) {
40506             // then we can use the store to find the values..
40507             // comma seperated at present.. this needs to allow JSON based encoding..
40508             this.hiddenEl.value  = v;
40509             var v_ar = [];
40510             Roo.each(v.split(','), function(k) {
40511                 Roo.log("CHECK " + this.valueField + ',' + k);
40512                 var li = this.store.query(this.valueField, k);
40513                 if (!li.length) {
40514                     return;
40515                 }
40516                 var add = {};
40517                 add[this.valueField] = k;
40518                 add[this.displayField] = li.item(0).data[this.displayField];
40519                 
40520                 this.addItem(add);
40521             }, this) 
40522              
40523         }
40524         if (typeof(v) == 'object') {
40525             // then let's assume it's an array of objects..
40526             Roo.each(v, function(l) {
40527                 this.addItem(l);
40528             }, this);
40529              
40530         }
40531         
40532         
40533     },
40534     setFromData: function(v)
40535     {
40536         // this recieves an object, if setValues is called.
40537         this.reset();
40538         this.el.dom.value = v[this.displayField];
40539         this.hiddenEl.dom.value = v[this.valueField];
40540         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40541             return;
40542         }
40543         var kv = v[this.valueField];
40544         var dv = v[this.displayField];
40545         kv = typeof(kv) != 'string' ? '' : kv;
40546         dv = typeof(dv) != 'string' ? '' : dv;
40547         
40548         
40549         var keys = kv.split(',');
40550         var display = dv.split(',');
40551         for (var i = 0 ; i < keys.length; i++) {
40552             
40553             add = {};
40554             add[this.valueField] = keys[i];
40555             add[this.displayField] = display[i];
40556             this.addItem(add);
40557         }
40558       
40559         
40560     },
40561     
40562     /**
40563      * Validates the combox array value
40564      * @return {Boolean} True if the value is valid, else false
40565      */
40566     validate : function(){
40567         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40568             this.clearInvalid();
40569             return true;
40570         }
40571         return false;
40572     },
40573     
40574     validateValue : function(value){
40575         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40576         
40577     },
40578     
40579     /*@
40580      * overide
40581      * 
40582      */
40583     isDirty : function() {
40584         if(this.disabled) {
40585             return false;
40586         }
40587         
40588         try {
40589             var d = Roo.decode(String(this.originalValue));
40590         } catch (e) {
40591             return String(this.getValue()) !== String(this.originalValue);
40592         }
40593         
40594         var originalValue = [];
40595         
40596         for (var i = 0; i < d.length; i++){
40597             originalValue.push(d[i][this.valueField]);
40598         }
40599         
40600         return String(this.getValue()) !== String(originalValue.join(','));
40601         
40602     }
40603     
40604 });
40605
40606
40607
40608 /**
40609  * @class Roo.form.ComboBoxArray.Item
40610  * @extends Roo.BoxComponent
40611  * A selected item in the list
40612  *  Fred [x]  Brian [x]  [Pick another |v]
40613  * 
40614  * @constructor
40615  * Create a new item.
40616  * @param {Object} config Configuration options
40617  */
40618  
40619 Roo.form.ComboBoxArray.Item = function(config) {
40620     config.id = Roo.id();
40621     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40622 }
40623
40624 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40625     data : {},
40626     cb: false,
40627     displayField : false,
40628     tipField : false,
40629     
40630     
40631     defaultAutoCreate : {
40632         tag: 'div',
40633         cls: 'x-cbarray-item',
40634         cn : [ 
40635             { tag: 'div' },
40636             {
40637                 tag: 'img',
40638                 width:16,
40639                 height : 16,
40640                 src : Roo.BLANK_IMAGE_URL ,
40641                 align: 'center'
40642             }
40643         ]
40644         
40645     },
40646     
40647  
40648     onRender : function(ct, position)
40649     {
40650         Roo.form.Field.superclass.onRender.call(this, ct, position);
40651         
40652         if(!this.el){
40653             var cfg = this.getAutoCreate();
40654             this.el = ct.createChild(cfg, position);
40655         }
40656         
40657         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40658         
40659         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40660             this.cb.renderer(this.data) :
40661             String.format('{0}',this.data[this.displayField]);
40662         
40663             
40664         this.el.child('div').dom.setAttribute('qtip',
40665                         String.format('{0}',this.data[this.tipField])
40666         );
40667         
40668         this.el.child('img').on('click', this.remove, this);
40669         
40670     },
40671    
40672     remove : function()
40673     {
40674         this.cb.items.remove(this);
40675         this.el.child('img').un('click', this.remove, this);
40676         this.el.remove();
40677         this.cb.updateHiddenEl();
40678         
40679         this.cb.fireEvent('remove', this.cb, this);
40680     }
40681 });/*
40682  * Based on:
40683  * Ext JS Library 1.1.1
40684  * Copyright(c) 2006-2007, Ext JS, LLC.
40685  *
40686  * Originally Released Under LGPL - original licence link has changed is not relivant.
40687  *
40688  * Fork - LGPL
40689  * <script type="text/javascript">
40690  */
40691 /**
40692  * @class Roo.form.Checkbox
40693  * @extends Roo.form.Field
40694  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40695  * @constructor
40696  * Creates a new Checkbox
40697  * @param {Object} config Configuration options
40698  */
40699 Roo.form.Checkbox = function(config){
40700     Roo.form.Checkbox.superclass.constructor.call(this, config);
40701     this.addEvents({
40702         /**
40703          * @event check
40704          * Fires when the checkbox is checked or unchecked.
40705              * @param {Roo.form.Checkbox} this This checkbox
40706              * @param {Boolean} checked The new checked value
40707              */
40708         check : true
40709     });
40710 };
40711
40712 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40713     /**
40714      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40715      */
40716     focusClass : undefined,
40717     /**
40718      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40719      */
40720     fieldClass: "x-form-field",
40721     /**
40722      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40723      */
40724     checked: false,
40725     /**
40726      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40727      * {tag: "input", type: "checkbox", autocomplete: "off"})
40728      */
40729     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40730     /**
40731      * @cfg {String} boxLabel The text that appears beside the checkbox
40732      */
40733     boxLabel : "",
40734     /**
40735      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40736      */  
40737     inputValue : '1',
40738     /**
40739      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40740      */
40741      valueOff: '0', // value when not checked..
40742
40743     actionMode : 'viewEl', 
40744     //
40745     // private
40746     itemCls : 'x-menu-check-item x-form-item',
40747     groupClass : 'x-menu-group-item',
40748     inputType : 'hidden',
40749     
40750     
40751     inSetChecked: false, // check that we are not calling self...
40752     
40753     inputElement: false, // real input element?
40754     basedOn: false, // ????
40755     
40756     isFormField: true, // not sure where this is needed!!!!
40757
40758     onResize : function(){
40759         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40760         if(!this.boxLabel){
40761             this.el.alignTo(this.wrap, 'c-c');
40762         }
40763     },
40764
40765     initEvents : function(){
40766         Roo.form.Checkbox.superclass.initEvents.call(this);
40767         this.el.on("click", this.onClick,  this);
40768         this.el.on("change", this.onClick,  this);
40769     },
40770
40771
40772     getResizeEl : function(){
40773         return this.wrap;
40774     },
40775
40776     getPositionEl : function(){
40777         return this.wrap;
40778     },
40779
40780     // private
40781     onRender : function(ct, position){
40782         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40783         /*
40784         if(this.inputValue !== undefined){
40785             this.el.dom.value = this.inputValue;
40786         }
40787         */
40788         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40789         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40790         var viewEl = this.wrap.createChild({ 
40791             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40792         this.viewEl = viewEl;   
40793         this.wrap.on('click', this.onClick,  this); 
40794         
40795         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40796         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40797         
40798         
40799         
40800         if(this.boxLabel){
40801             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40802         //    viewEl.on('click', this.onClick,  this); 
40803         }
40804         //if(this.checked){
40805             this.setChecked(this.checked);
40806         //}else{
40807             //this.checked = this.el.dom;
40808         //}
40809
40810     },
40811
40812     // private
40813     initValue : Roo.emptyFn,
40814
40815     /**
40816      * Returns the checked state of the checkbox.
40817      * @return {Boolean} True if checked, else false
40818      */
40819     getValue : function(){
40820         if(this.el){
40821             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40822         }
40823         return this.valueOff;
40824         
40825     },
40826
40827         // private
40828     onClick : function(){ 
40829         this.setChecked(!this.checked);
40830
40831         //if(this.el.dom.checked != this.checked){
40832         //    this.setValue(this.el.dom.checked);
40833        // }
40834     },
40835
40836     /**
40837      * Sets the checked state of the checkbox.
40838      * On is always based on a string comparison between inputValue and the param.
40839      * @param {Boolean/String} value - the value to set 
40840      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40841      */
40842     setValue : function(v,suppressEvent){
40843         
40844         
40845         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40846         //if(this.el && this.el.dom){
40847         //    this.el.dom.checked = this.checked;
40848         //    this.el.dom.defaultChecked = this.checked;
40849         //}
40850         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40851         //this.fireEvent("check", this, this.checked);
40852     },
40853     // private..
40854     setChecked : function(state,suppressEvent)
40855     {
40856         if (this.inSetChecked) {
40857             this.checked = state;
40858             return;
40859         }
40860         
40861     
40862         if(this.wrap){
40863             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40864         }
40865         this.checked = state;
40866         if(suppressEvent !== true){
40867             this.fireEvent('check', this, state);
40868         }
40869         this.inSetChecked = true;
40870         this.el.dom.value = state ? this.inputValue : this.valueOff;
40871         this.inSetChecked = false;
40872         
40873     },
40874     // handle setting of hidden value by some other method!!?!?
40875     setFromHidden: function()
40876     {
40877         if(!this.el){
40878             return;
40879         }
40880         //console.log("SET FROM HIDDEN");
40881         //alert('setFrom hidden');
40882         this.setValue(this.el.dom.value);
40883     },
40884     
40885     onDestroy : function()
40886     {
40887         if(this.viewEl){
40888             Roo.get(this.viewEl).remove();
40889         }
40890          
40891         Roo.form.Checkbox.superclass.onDestroy.call(this);
40892     }
40893
40894 });/*
40895  * Based on:
40896  * Ext JS Library 1.1.1
40897  * Copyright(c) 2006-2007, Ext JS, LLC.
40898  *
40899  * Originally Released Under LGPL - original licence link has changed is not relivant.
40900  *
40901  * Fork - LGPL
40902  * <script type="text/javascript">
40903  */
40904  
40905 /**
40906  * @class Roo.form.Radio
40907  * @extends Roo.form.Checkbox
40908  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40909  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40910  * @constructor
40911  * Creates a new Radio
40912  * @param {Object} config Configuration options
40913  */
40914 Roo.form.Radio = function(){
40915     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40916 };
40917 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40918     inputType: 'radio',
40919
40920     /**
40921      * If this radio is part of a group, it will return the selected value
40922      * @return {String}
40923      */
40924     getGroupValue : function(){
40925         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40926     },
40927     
40928     
40929     onRender : function(ct, position){
40930         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40931         
40932         if(this.inputValue !== undefined){
40933             this.el.dom.value = this.inputValue;
40934         }
40935          
40936         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40937         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40938         //var viewEl = this.wrap.createChild({ 
40939         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40940         //this.viewEl = viewEl;   
40941         //this.wrap.on('click', this.onClick,  this); 
40942         
40943         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40944         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40945         
40946         
40947         
40948         if(this.boxLabel){
40949             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40950         //    viewEl.on('click', this.onClick,  this); 
40951         }
40952          if(this.checked){
40953             this.el.dom.checked =   'checked' ;
40954         }
40955          
40956     } 
40957     
40958     
40959 });//<script type="text/javascript">
40960
40961 /*
40962  * Based  Ext JS Library 1.1.1
40963  * Copyright(c) 2006-2007, Ext JS, LLC.
40964  * LGPL
40965  *
40966  */
40967  
40968 /**
40969  * @class Roo.HtmlEditorCore
40970  * @extends Roo.Component
40971  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40972  *
40973  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40974  */
40975
40976 Roo.HtmlEditorCore = function(config){
40977     
40978     
40979     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40980     this.addEvents({
40981         /**
40982          * @event initialize
40983          * Fires when the editor is fully initialized (including the iframe)
40984          * @param {Roo.HtmlEditorCore} this
40985          */
40986         initialize: true,
40987         /**
40988          * @event activate
40989          * Fires when the editor is first receives the focus. Any insertion must wait
40990          * until after this event.
40991          * @param {Roo.HtmlEditorCore} this
40992          */
40993         activate: true,
40994          /**
40995          * @event beforesync
40996          * Fires before the textarea is updated with content from the editor iframe. Return false
40997          * to cancel the sync.
40998          * @param {Roo.HtmlEditorCore} this
40999          * @param {String} html
41000          */
41001         beforesync: true,
41002          /**
41003          * @event beforepush
41004          * Fires before the iframe editor is updated with content from the textarea. Return false
41005          * to cancel the push.
41006          * @param {Roo.HtmlEditorCore} this
41007          * @param {String} html
41008          */
41009         beforepush: true,
41010          /**
41011          * @event sync
41012          * Fires when the textarea is updated with content from the editor iframe.
41013          * @param {Roo.HtmlEditorCore} this
41014          * @param {String} html
41015          */
41016         sync: true,
41017          /**
41018          * @event push
41019          * Fires when the iframe editor is updated with content from the textarea.
41020          * @param {Roo.HtmlEditorCore} this
41021          * @param {String} html
41022          */
41023         push: true,
41024         
41025         /**
41026          * @event editorevent
41027          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41028          * @param {Roo.HtmlEditorCore} this
41029          */
41030         editorevent: true
41031     });
41032      
41033 };
41034
41035
41036 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41037
41038
41039      /**
41040      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41041      */
41042     
41043     owner : false,
41044     
41045      /**
41046      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41047      *                        Roo.resizable.
41048      */
41049     resizable : false,
41050      /**
41051      * @cfg {Number} height (in pixels)
41052      */   
41053     height: 300,
41054    /**
41055      * @cfg {Number} width (in pixels)
41056      */   
41057     width: 500,
41058     
41059     /**
41060      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41061      * 
41062      */
41063     stylesheets: false,
41064     
41065     // id of frame..
41066     frameId: false,
41067     
41068     // private properties
41069     validationEvent : false,
41070     deferHeight: true,
41071     initialized : false,
41072     activated : false,
41073     sourceEditMode : false,
41074     onFocus : Roo.emptyFn,
41075     iframePad:3,
41076     hideMode:'offsets',
41077     
41078     clearUp: true,
41079     
41080      
41081     
41082
41083     /**
41084      * Protected method that will not generally be called directly. It
41085      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41086      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41087      */
41088     getDocMarkup : function(){
41089         // body styles..
41090         var st = '';
41091         Roo.log(this.stylesheets);
41092         
41093         // inherit styels from page...?? 
41094         if (this.stylesheets === false) {
41095             
41096             Roo.get(document.head).select('style').each(function(node) {
41097                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41098             });
41099             
41100             Roo.get(document.head).select('link').each(function(node) { 
41101                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41102             });
41103             
41104         } else if (!this.stylesheets.length) {
41105                 // simple..
41106                 st = '<style type="text/css">' +
41107                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41108                    '</style>';
41109         } else {
41110             Roo.each(this.stylesheets, function(s) {
41111                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41112             });
41113             
41114         }
41115         
41116         st +=  '<style type="text/css">' +
41117             'IMG { cursor: pointer } ' +
41118         '</style>';
41119
41120         
41121         return '<html><head>' + st  +
41122             //<style type="text/css">' +
41123             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41124             //'</style>' +
41125             ' </head><body class="roo-htmleditor-body"></body></html>';
41126     },
41127
41128     // private
41129     onRender : function(ct, position)
41130     {
41131         var _t = this;
41132         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41133         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41134         
41135         
41136         this.el.dom.style.border = '0 none';
41137         this.el.dom.setAttribute('tabIndex', -1);
41138         this.el.addClass('x-hidden hide');
41139         
41140         
41141         
41142         if(Roo.isIE){ // fix IE 1px bogus margin
41143             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41144         }
41145        
41146         
41147         this.frameId = Roo.id();
41148         
41149          
41150         
41151         var iframe = this.owner.wrap.createChild({
41152             tag: 'iframe',
41153             cls: 'form-control', // bootstrap..
41154             id: this.frameId,
41155             name: this.frameId,
41156             frameBorder : 'no',
41157             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41158         }, this.el
41159         );
41160         
41161         
41162         this.iframe = iframe.dom;
41163
41164          this.assignDocWin();
41165         
41166         this.doc.designMode = 'on';
41167        
41168         this.doc.open();
41169         this.doc.write(this.getDocMarkup());
41170         this.doc.close();
41171
41172         
41173         var task = { // must defer to wait for browser to be ready
41174             run : function(){
41175                 //console.log("run task?" + this.doc.readyState);
41176                 this.assignDocWin();
41177                 if(this.doc.body || this.doc.readyState == 'complete'){
41178                     try {
41179                         this.doc.designMode="on";
41180                     } catch (e) {
41181                         return;
41182                     }
41183                     Roo.TaskMgr.stop(task);
41184                     this.initEditor.defer(10, this);
41185                 }
41186             },
41187             interval : 10,
41188             duration: 10000,
41189             scope: this
41190         };
41191         Roo.TaskMgr.start(task);
41192
41193         
41194          
41195     },
41196
41197     // private
41198     onResize : function(w, h)
41199     {
41200          Roo.log('resize: ' +w + ',' + h );
41201         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41202         if(!this.iframe){
41203             return;
41204         }
41205         if(typeof w == 'number'){
41206             
41207             this.iframe.style.width = w + 'px';
41208         }
41209         if(typeof h == 'number'){
41210             
41211             this.iframe.style.height = h + 'px';
41212             if(this.doc){
41213                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41214             }
41215         }
41216         
41217     },
41218
41219     /**
41220      * Toggles the editor between standard and source edit mode.
41221      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41222      */
41223     toggleSourceEdit : function(sourceEditMode){
41224         
41225         this.sourceEditMode = sourceEditMode === true;
41226         
41227         if(this.sourceEditMode){
41228  
41229             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41230             
41231         }else{
41232             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41233             //this.iframe.className = '';
41234             this.deferFocus();
41235         }
41236         //this.setSize(this.owner.wrap.getSize());
41237         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41238     },
41239
41240     
41241   
41242
41243     /**
41244      * Protected method that will not generally be called directly. If you need/want
41245      * custom HTML cleanup, this is the method you should override.
41246      * @param {String} html The HTML to be cleaned
41247      * return {String} The cleaned HTML
41248      */
41249     cleanHtml : function(html){
41250         html = String(html);
41251         if(html.length > 5){
41252             if(Roo.isSafari){ // strip safari nonsense
41253                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41254             }
41255         }
41256         if(html == '&nbsp;'){
41257             html = '';
41258         }
41259         return html;
41260     },
41261
41262     /**
41263      * HTML Editor -> Textarea
41264      * Protected method that will not generally be called directly. Syncs the contents
41265      * of the editor iframe with the textarea.
41266      */
41267     syncValue : function(){
41268         if(this.initialized){
41269             var bd = (this.doc.body || this.doc.documentElement);
41270             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41271             var html = bd.innerHTML;
41272             if(Roo.isSafari){
41273                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41274                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41275                 if(m && m[1]){
41276                     html = '<div style="'+m[0]+'">' + html + '</div>';
41277                 }
41278             }
41279             html = this.cleanHtml(html);
41280             // fix up the special chars.. normaly like back quotes in word...
41281             // however we do not want to do this with chinese..
41282             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41283                 var cc = b.charCodeAt();
41284                 if (
41285                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41286                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41287                     (cc >= 0xf900 && cc < 0xfb00 )
41288                 ) {
41289                         return b;
41290                 }
41291                 return "&#"+cc+";" 
41292             });
41293             if(this.owner.fireEvent('beforesync', this, html) !== false){
41294                 this.el.dom.value = html;
41295                 this.owner.fireEvent('sync', this, html);
41296             }
41297         }
41298     },
41299
41300     /**
41301      * Protected method that will not generally be called directly. Pushes the value of the textarea
41302      * into the iframe editor.
41303      */
41304     pushValue : function(){
41305         if(this.initialized){
41306             var v = this.el.dom.value.trim();
41307             
41308 //            if(v.length < 1){
41309 //                v = '&#160;';
41310 //            }
41311             
41312             if(this.owner.fireEvent('beforepush', this, v) !== false){
41313                 var d = (this.doc.body || this.doc.documentElement);
41314                 d.innerHTML = v;
41315                 this.cleanUpPaste();
41316                 this.el.dom.value = d.innerHTML;
41317                 this.owner.fireEvent('push', this, v);
41318             }
41319         }
41320     },
41321
41322     // private
41323     deferFocus : function(){
41324         this.focus.defer(10, this);
41325     },
41326
41327     // doc'ed in Field
41328     focus : function(){
41329         if(this.win && !this.sourceEditMode){
41330             this.win.focus();
41331         }else{
41332             this.el.focus();
41333         }
41334     },
41335     
41336     assignDocWin: function()
41337     {
41338         var iframe = this.iframe;
41339         
41340          if(Roo.isIE){
41341             this.doc = iframe.contentWindow.document;
41342             this.win = iframe.contentWindow;
41343         } else {
41344             if (!Roo.get(this.frameId)) {
41345                 return;
41346             }
41347             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41348             this.win = Roo.get(this.frameId).dom.contentWindow;
41349         }
41350     },
41351     
41352     // private
41353     initEditor : function(){
41354         //console.log("INIT EDITOR");
41355         this.assignDocWin();
41356         
41357         
41358         
41359         this.doc.designMode="on";
41360         this.doc.open();
41361         this.doc.write(this.getDocMarkup());
41362         this.doc.close();
41363         
41364         var dbody = (this.doc.body || this.doc.documentElement);
41365         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41366         // this copies styles from the containing element into thsi one..
41367         // not sure why we need all of this..
41368         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41369         
41370         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41371         //ss['background-attachment'] = 'fixed'; // w3c
41372         dbody.bgProperties = 'fixed'; // ie
41373         //Roo.DomHelper.applyStyles(dbody, ss);
41374         Roo.EventManager.on(this.doc, {
41375             //'mousedown': this.onEditorEvent,
41376             'mouseup': this.onEditorEvent,
41377             'dblclick': this.onEditorEvent,
41378             'click': this.onEditorEvent,
41379             'keyup': this.onEditorEvent,
41380             buffer:100,
41381             scope: this
41382         });
41383         if(Roo.isGecko){
41384             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41385         }
41386         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41387             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41388         }
41389         this.initialized = true;
41390
41391         this.owner.fireEvent('initialize', this);
41392         this.pushValue();
41393     },
41394
41395     // private
41396     onDestroy : function(){
41397         
41398         
41399         
41400         if(this.rendered){
41401             
41402             //for (var i =0; i < this.toolbars.length;i++) {
41403             //    // fixme - ask toolbars for heights?
41404             //    this.toolbars[i].onDestroy();
41405            // }
41406             
41407             //this.wrap.dom.innerHTML = '';
41408             //this.wrap.remove();
41409         }
41410     },
41411
41412     // private
41413     onFirstFocus : function(){
41414         
41415         this.assignDocWin();
41416         
41417         
41418         this.activated = true;
41419          
41420     
41421         if(Roo.isGecko){ // prevent silly gecko errors
41422             this.win.focus();
41423             var s = this.win.getSelection();
41424             if(!s.focusNode || s.focusNode.nodeType != 3){
41425                 var r = s.getRangeAt(0);
41426                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41427                 r.collapse(true);
41428                 this.deferFocus();
41429             }
41430             try{
41431                 this.execCmd('useCSS', true);
41432                 this.execCmd('styleWithCSS', false);
41433             }catch(e){}
41434         }
41435         this.owner.fireEvent('activate', this);
41436     },
41437
41438     // private
41439     adjustFont: function(btn){
41440         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41441         //if(Roo.isSafari){ // safari
41442         //    adjust *= 2;
41443        // }
41444         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41445         if(Roo.isSafari){ // safari
41446             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41447             v =  (v < 10) ? 10 : v;
41448             v =  (v > 48) ? 48 : v;
41449             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41450             
41451         }
41452         
41453         
41454         v = Math.max(1, v+adjust);
41455         
41456         this.execCmd('FontSize', v  );
41457     },
41458
41459     onEditorEvent : function(e){
41460         this.owner.fireEvent('editorevent', this, e);
41461       //  this.updateToolbar();
41462         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41463     },
41464
41465     insertTag : function(tg)
41466     {
41467         // could be a bit smarter... -> wrap the current selected tRoo..
41468         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41469             
41470             range = this.createRange(this.getSelection());
41471             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41472             wrappingNode.appendChild(range.extractContents());
41473             range.insertNode(wrappingNode);
41474
41475             return;
41476             
41477             
41478             
41479         }
41480         this.execCmd("formatblock",   tg);
41481         
41482     },
41483     
41484     insertText : function(txt)
41485     {
41486         
41487         
41488         var range = this.createRange();
41489         range.deleteContents();
41490                //alert(Sender.getAttribute('label'));
41491                
41492         range.insertNode(this.doc.createTextNode(txt));
41493     } ,
41494     
41495      
41496
41497     /**
41498      * Executes a Midas editor command on the editor document and performs necessary focus and
41499      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41500      * @param {String} cmd The Midas command
41501      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41502      */
41503     relayCmd : function(cmd, value){
41504         this.win.focus();
41505         this.execCmd(cmd, value);
41506         this.owner.fireEvent('editorevent', this);
41507         //this.updateToolbar();
41508         this.owner.deferFocus();
41509     },
41510
41511     /**
41512      * Executes a Midas editor command directly on the editor document.
41513      * For visual commands, you should use {@link #relayCmd} instead.
41514      * <b>This should only be called after the editor is initialized.</b>
41515      * @param {String} cmd The Midas command
41516      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41517      */
41518     execCmd : function(cmd, value){
41519         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41520         this.syncValue();
41521     },
41522  
41523  
41524    
41525     /**
41526      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41527      * to insert tRoo.
41528      * @param {String} text | dom node.. 
41529      */
41530     insertAtCursor : function(text)
41531     {
41532         
41533         
41534         
41535         if(!this.activated){
41536             return;
41537         }
41538         /*
41539         if(Roo.isIE){
41540             this.win.focus();
41541             var r = this.doc.selection.createRange();
41542             if(r){
41543                 r.collapse(true);
41544                 r.pasteHTML(text);
41545                 this.syncValue();
41546                 this.deferFocus();
41547             
41548             }
41549             return;
41550         }
41551         */
41552         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41553             this.win.focus();
41554             
41555             
41556             // from jquery ui (MIT licenced)
41557             var range, node;
41558             var win = this.win;
41559             
41560             if (win.getSelection && win.getSelection().getRangeAt) {
41561                 range = win.getSelection().getRangeAt(0);
41562                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41563                 range.insertNode(node);
41564             } else if (win.document.selection && win.document.selection.createRange) {
41565                 // no firefox support
41566                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41567                 win.document.selection.createRange().pasteHTML(txt);
41568             } else {
41569                 // no firefox support
41570                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41571                 this.execCmd('InsertHTML', txt);
41572             } 
41573             
41574             this.syncValue();
41575             
41576             this.deferFocus();
41577         }
41578     },
41579  // private
41580     mozKeyPress : function(e){
41581         if(e.ctrlKey){
41582             var c = e.getCharCode(), cmd;
41583           
41584             if(c > 0){
41585                 c = String.fromCharCode(c).toLowerCase();
41586                 switch(c){
41587                     case 'b':
41588                         cmd = 'bold';
41589                         break;
41590                     case 'i':
41591                         cmd = 'italic';
41592                         break;
41593                     
41594                     case 'u':
41595                         cmd = 'underline';
41596                         break;
41597                     
41598                     case 'v':
41599                         this.cleanUpPaste.defer(100, this);
41600                         return;
41601                         
41602                 }
41603                 if(cmd){
41604                     this.win.focus();
41605                     this.execCmd(cmd);
41606                     this.deferFocus();
41607                     e.preventDefault();
41608                 }
41609                 
41610             }
41611         }
41612     },
41613
41614     // private
41615     fixKeys : function(){ // load time branching for fastest keydown performance
41616         if(Roo.isIE){
41617             return function(e){
41618                 var k = e.getKey(), r;
41619                 if(k == e.TAB){
41620                     e.stopEvent();
41621                     r = this.doc.selection.createRange();
41622                     if(r){
41623                         r.collapse(true);
41624                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41625                         this.deferFocus();
41626                     }
41627                     return;
41628                 }
41629                 
41630                 if(k == e.ENTER){
41631                     r = this.doc.selection.createRange();
41632                     if(r){
41633                         var target = r.parentElement();
41634                         if(!target || target.tagName.toLowerCase() != 'li'){
41635                             e.stopEvent();
41636                             r.pasteHTML('<br />');
41637                             r.collapse(false);
41638                             r.select();
41639                         }
41640                     }
41641                 }
41642                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41643                     this.cleanUpPaste.defer(100, this);
41644                     return;
41645                 }
41646                 
41647                 
41648             };
41649         }else if(Roo.isOpera){
41650             return function(e){
41651                 var k = e.getKey();
41652                 if(k == e.TAB){
41653                     e.stopEvent();
41654                     this.win.focus();
41655                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41656                     this.deferFocus();
41657                 }
41658                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41659                     this.cleanUpPaste.defer(100, this);
41660                     return;
41661                 }
41662                 
41663             };
41664         }else if(Roo.isSafari){
41665             return function(e){
41666                 var k = e.getKey();
41667                 
41668                 if(k == e.TAB){
41669                     e.stopEvent();
41670                     this.execCmd('InsertText','\t');
41671                     this.deferFocus();
41672                     return;
41673                 }
41674                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41675                     this.cleanUpPaste.defer(100, this);
41676                     return;
41677                 }
41678                 
41679              };
41680         }
41681     }(),
41682     
41683     getAllAncestors: function()
41684     {
41685         var p = this.getSelectedNode();
41686         var a = [];
41687         if (!p) {
41688             a.push(p); // push blank onto stack..
41689             p = this.getParentElement();
41690         }
41691         
41692         
41693         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41694             a.push(p);
41695             p = p.parentNode;
41696         }
41697         a.push(this.doc.body);
41698         return a;
41699     },
41700     lastSel : false,
41701     lastSelNode : false,
41702     
41703     
41704     getSelection : function() 
41705     {
41706         this.assignDocWin();
41707         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41708     },
41709     
41710     getSelectedNode: function() 
41711     {
41712         // this may only work on Gecko!!!
41713         
41714         // should we cache this!!!!
41715         
41716         
41717         
41718          
41719         var range = this.createRange(this.getSelection()).cloneRange();
41720         
41721         if (Roo.isIE) {
41722             var parent = range.parentElement();
41723             while (true) {
41724                 var testRange = range.duplicate();
41725                 testRange.moveToElementText(parent);
41726                 if (testRange.inRange(range)) {
41727                     break;
41728                 }
41729                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41730                     break;
41731                 }
41732                 parent = parent.parentElement;
41733             }
41734             return parent;
41735         }
41736         
41737         // is ancestor a text element.
41738         var ac =  range.commonAncestorContainer;
41739         if (ac.nodeType == 3) {
41740             ac = ac.parentNode;
41741         }
41742         
41743         var ar = ac.childNodes;
41744          
41745         var nodes = [];
41746         var other_nodes = [];
41747         var has_other_nodes = false;
41748         for (var i=0;i<ar.length;i++) {
41749             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41750                 continue;
41751             }
41752             // fullly contained node.
41753             
41754             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41755                 nodes.push(ar[i]);
41756                 continue;
41757             }
41758             
41759             // probably selected..
41760             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41761                 other_nodes.push(ar[i]);
41762                 continue;
41763             }
41764             // outer..
41765             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41766                 continue;
41767             }
41768             
41769             
41770             has_other_nodes = true;
41771         }
41772         if (!nodes.length && other_nodes.length) {
41773             nodes= other_nodes;
41774         }
41775         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41776             return false;
41777         }
41778         
41779         return nodes[0];
41780     },
41781     createRange: function(sel)
41782     {
41783         // this has strange effects when using with 
41784         // top toolbar - not sure if it's a great idea.
41785         //this.editor.contentWindow.focus();
41786         if (typeof sel != "undefined") {
41787             try {
41788                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41789             } catch(e) {
41790                 return this.doc.createRange();
41791             }
41792         } else {
41793             return this.doc.createRange();
41794         }
41795     },
41796     getParentElement: function()
41797     {
41798         
41799         this.assignDocWin();
41800         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41801         
41802         var range = this.createRange(sel);
41803          
41804         try {
41805             var p = range.commonAncestorContainer;
41806             while (p.nodeType == 3) { // text node
41807                 p = p.parentNode;
41808             }
41809             return p;
41810         } catch (e) {
41811             return null;
41812         }
41813     
41814     },
41815     /***
41816      *
41817      * Range intersection.. the hard stuff...
41818      *  '-1' = before
41819      *  '0' = hits..
41820      *  '1' = after.
41821      *         [ -- selected range --- ]
41822      *   [fail]                        [fail]
41823      *
41824      *    basically..
41825      *      if end is before start or  hits it. fail.
41826      *      if start is after end or hits it fail.
41827      *
41828      *   if either hits (but other is outside. - then it's not 
41829      *   
41830      *    
41831      **/
41832     
41833     
41834     // @see http://www.thismuchiknow.co.uk/?p=64.
41835     rangeIntersectsNode : function(range, node)
41836     {
41837         var nodeRange = node.ownerDocument.createRange();
41838         try {
41839             nodeRange.selectNode(node);
41840         } catch (e) {
41841             nodeRange.selectNodeContents(node);
41842         }
41843     
41844         var rangeStartRange = range.cloneRange();
41845         rangeStartRange.collapse(true);
41846     
41847         var rangeEndRange = range.cloneRange();
41848         rangeEndRange.collapse(false);
41849     
41850         var nodeStartRange = nodeRange.cloneRange();
41851         nodeStartRange.collapse(true);
41852     
41853         var nodeEndRange = nodeRange.cloneRange();
41854         nodeEndRange.collapse(false);
41855     
41856         return rangeStartRange.compareBoundaryPoints(
41857                  Range.START_TO_START, nodeEndRange) == -1 &&
41858                rangeEndRange.compareBoundaryPoints(
41859                  Range.START_TO_START, nodeStartRange) == 1;
41860         
41861          
41862     },
41863     rangeCompareNode : function(range, node)
41864     {
41865         var nodeRange = node.ownerDocument.createRange();
41866         try {
41867             nodeRange.selectNode(node);
41868         } catch (e) {
41869             nodeRange.selectNodeContents(node);
41870         }
41871         
41872         
41873         range.collapse(true);
41874     
41875         nodeRange.collapse(true);
41876      
41877         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41878         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41879          
41880         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41881         
41882         var nodeIsBefore   =  ss == 1;
41883         var nodeIsAfter    = ee == -1;
41884         
41885         if (nodeIsBefore && nodeIsAfter)
41886             return 0; // outer
41887         if (!nodeIsBefore && nodeIsAfter)
41888             return 1; //right trailed.
41889         
41890         if (nodeIsBefore && !nodeIsAfter)
41891             return 2;  // left trailed.
41892         // fully contined.
41893         return 3;
41894     },
41895
41896     // private? - in a new class?
41897     cleanUpPaste :  function()
41898     {
41899         // cleans up the whole document..
41900         Roo.log('cleanuppaste');
41901         
41902         this.cleanUpChildren(this.doc.body);
41903         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41904         if (clean != this.doc.body.innerHTML) {
41905             this.doc.body.innerHTML = clean;
41906         }
41907         
41908     },
41909     
41910     cleanWordChars : function(input) {// change the chars to hex code
41911         var he = Roo.HtmlEditorCore;
41912         
41913         var output = input;
41914         Roo.each(he.swapCodes, function(sw) { 
41915             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41916             
41917             output = output.replace(swapper, sw[1]);
41918         });
41919         
41920         return output;
41921     },
41922     
41923     
41924     cleanUpChildren : function (n)
41925     {
41926         if (!n.childNodes.length) {
41927             return;
41928         }
41929         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41930            this.cleanUpChild(n.childNodes[i]);
41931         }
41932     },
41933     
41934     
41935         
41936     
41937     cleanUpChild : function (node)
41938     {
41939         var ed = this;
41940         //console.log(node);
41941         if (node.nodeName == "#text") {
41942             // clean up silly Windows -- stuff?
41943             return; 
41944         }
41945         if (node.nodeName == "#comment") {
41946             node.parentNode.removeChild(node);
41947             // clean up silly Windows -- stuff?
41948             return; 
41949         }
41950         
41951         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
41952             // remove node.
41953             node.parentNode.removeChild(node);
41954             return;
41955             
41956         }
41957         
41958         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41959         
41960         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41961         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41962         
41963         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41964         //    remove_keep_children = true;
41965         //}
41966         
41967         if (remove_keep_children) {
41968             this.cleanUpChildren(node);
41969             // inserts everything just before this node...
41970             while (node.childNodes.length) {
41971                 var cn = node.childNodes[0];
41972                 node.removeChild(cn);
41973                 node.parentNode.insertBefore(cn, node);
41974             }
41975             node.parentNode.removeChild(node);
41976             return;
41977         }
41978         
41979         if (!node.attributes || !node.attributes.length) {
41980             this.cleanUpChildren(node);
41981             return;
41982         }
41983         
41984         function cleanAttr(n,v)
41985         {
41986             
41987             if (v.match(/^\./) || v.match(/^\//)) {
41988                 return;
41989             }
41990             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41991                 return;
41992             }
41993             if (v.match(/^#/)) {
41994                 return;
41995             }
41996 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41997             node.removeAttribute(n);
41998             
41999         }
42000         
42001         function cleanStyle(n,v)
42002         {
42003             if (v.match(/expression/)) { //XSS?? should we even bother..
42004                 node.removeAttribute(n);
42005                 return;
42006             }
42007             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
42008             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
42009             
42010             
42011             var parts = v.split(/;/);
42012             var clean = [];
42013             
42014             Roo.each(parts, function(p) {
42015                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42016                 if (!p.length) {
42017                     return true;
42018                 }
42019                 var l = p.split(':').shift().replace(/\s+/g,'');
42020                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42021                 
42022                 if ( cblack.indexOf(l) > -1) {
42023 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42024                     //node.removeAttribute(n);
42025                     return true;
42026                 }
42027                 //Roo.log()
42028                 // only allow 'c whitelisted system attributes'
42029                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42030 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42031                     //node.removeAttribute(n);
42032                     return true;
42033                 }
42034                 
42035                 
42036                  
42037                 
42038                 clean.push(p);
42039                 return true;
42040             });
42041             if (clean.length) { 
42042                 node.setAttribute(n, clean.join(';'));
42043             } else {
42044                 node.removeAttribute(n);
42045             }
42046             
42047         }
42048         
42049         
42050         for (var i = node.attributes.length-1; i > -1 ; i--) {
42051             var a = node.attributes[i];
42052             //console.log(a);
42053             
42054             if (a.name.toLowerCase().substr(0,2)=='on')  {
42055                 node.removeAttribute(a.name);
42056                 continue;
42057             }
42058             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42059                 node.removeAttribute(a.name);
42060                 continue;
42061             }
42062             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42063                 cleanAttr(a.name,a.value); // fixme..
42064                 continue;
42065             }
42066             if (a.name == 'style') {
42067                 cleanStyle(a.name,a.value);
42068                 continue;
42069             }
42070             /// clean up MS crap..
42071             // tecnically this should be a list of valid class'es..
42072             
42073             
42074             if (a.name == 'class') {
42075                 if (a.value.match(/^Mso/)) {
42076                     node.className = '';
42077                 }
42078                 
42079                 if (a.value.match(/body/)) {
42080                     node.className = '';
42081                 }
42082                 continue;
42083             }
42084             
42085             // style cleanup!?
42086             // class cleanup?
42087             
42088         }
42089         
42090         
42091         this.cleanUpChildren(node);
42092         
42093         
42094     },
42095     /**
42096      * Clean up MS wordisms...
42097      */
42098     cleanWord : function(node)
42099     {
42100         var _t = this;
42101         var cleanWordChildren = function()
42102         {
42103             if (!node.childNodes.length) {
42104                 return;
42105             }
42106             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42107                _t.cleanWord(node.childNodes[i]);
42108             }
42109         }
42110         
42111         
42112         if (!node) {
42113             this.cleanWord(this.doc.body);
42114             return;
42115         }
42116         if (node.nodeName == "#text") {
42117             // clean up silly Windows -- stuff?
42118             return; 
42119         }
42120         if (node.nodeName == "#comment") {
42121             node.parentNode.removeChild(node);
42122             // clean up silly Windows -- stuff?
42123             return; 
42124         }
42125         
42126         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42127             node.parentNode.removeChild(node);
42128             return;
42129         }
42130         
42131         // remove - but keep children..
42132         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42133             while (node.childNodes.length) {
42134                 var cn = node.childNodes[0];
42135                 node.removeChild(cn);
42136                 node.parentNode.insertBefore(cn, node);
42137             }
42138             node.parentNode.removeChild(node);
42139             cleanWordChildren();
42140             return;
42141         }
42142         // clean styles
42143         if (node.className.length) {
42144             
42145             var cn = node.className.split(/\W+/);
42146             var cna = [];
42147             Roo.each(cn, function(cls) {
42148                 if (cls.match(/Mso[a-zA-Z]+/)) {
42149                     return;
42150                 }
42151                 cna.push(cls);
42152             });
42153             node.className = cna.length ? cna.join(' ') : '';
42154             if (!cna.length) {
42155                 node.removeAttribute("class");
42156             }
42157         }
42158         
42159         if (node.hasAttribute("lang")) {
42160             node.removeAttribute("lang");
42161         }
42162         
42163         if (node.hasAttribute("style")) {
42164             
42165             var styles = node.getAttribute("style").split(";");
42166             var nstyle = [];
42167             Roo.each(styles, function(s) {
42168                 if (!s.match(/:/)) {
42169                     return;
42170                 }
42171                 var kv = s.split(":");
42172                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42173                     return;
42174                 }
42175                 // what ever is left... we allow.
42176                 nstyle.push(s);
42177             });
42178             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42179             if (!nstyle.length) {
42180                 node.removeAttribute('style');
42181             }
42182         }
42183         
42184         cleanWordChildren();
42185         
42186         
42187     },
42188     domToHTML : function(currentElement, depth, nopadtext) {
42189         
42190             depth = depth || 0;
42191             nopadtext = nopadtext || false;
42192         
42193             if (!currentElement) {
42194                 return this.domToHTML(this.doc.body);
42195             }
42196             
42197             //Roo.log(currentElement);
42198             var j;
42199             var allText = false;
42200             var nodeName = currentElement.nodeName;
42201             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42202             
42203             if  (nodeName == '#text') {
42204                 return currentElement.nodeValue;
42205             }
42206             
42207             
42208             var ret = '';
42209             if (nodeName != 'BODY') {
42210                  
42211                 var i = 0;
42212                 // Prints the node tagName, such as <A>, <IMG>, etc
42213                 if (tagName) {
42214                     var attr = [];
42215                     for(i = 0; i < currentElement.attributes.length;i++) {
42216                         // quoting?
42217                         var aname = currentElement.attributes.item(i).name;
42218                         if (!currentElement.attributes.item(i).value.length) {
42219                             continue;
42220                         }
42221                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42222                     }
42223                     
42224                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42225                 } 
42226                 else {
42227                     
42228                     // eack
42229                 }
42230             } else {
42231                 tagName = false;
42232             }
42233             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42234                 return ret;
42235             }
42236             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42237                 nopadtext = true;
42238             }
42239             
42240             
42241             // Traverse the tree
42242             i = 0;
42243             var currentElementChild = currentElement.childNodes.item(i);
42244             var allText = true;
42245             var innerHTML  = '';
42246             lastnode = '';
42247             while (currentElementChild) {
42248                 // Formatting code (indent the tree so it looks nice on the screen)
42249                 var nopad = nopadtext;
42250                 if (lastnode == 'SPAN') {
42251                     nopad  = true;
42252                 }
42253                 // text
42254                 if  (currentElementChild.nodeName == '#text') {
42255                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42256                     if (!nopad && toadd.length > 80) {
42257                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42258                     }
42259                     innerHTML  += toadd;
42260                     
42261                     i++;
42262                     currentElementChild = currentElement.childNodes.item(i);
42263                     lastNode = '';
42264                     continue;
42265                 }
42266                 allText = false;
42267                 
42268                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42269                     
42270                 // Recursively traverse the tree structure of the child node
42271                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42272                 lastnode = currentElementChild.nodeName;
42273                 i++;
42274                 currentElementChild=currentElement.childNodes.item(i);
42275             }
42276             
42277             ret += innerHTML;
42278             
42279             if (!allText) {
42280                     // The remaining code is mostly for formatting the tree
42281                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42282             }
42283             
42284             
42285             if (tagName) {
42286                 ret+= "</"+tagName+">";
42287             }
42288             return ret;
42289             
42290         }
42291     
42292     // hide stuff that is not compatible
42293     /**
42294      * @event blur
42295      * @hide
42296      */
42297     /**
42298      * @event change
42299      * @hide
42300      */
42301     /**
42302      * @event focus
42303      * @hide
42304      */
42305     /**
42306      * @event specialkey
42307      * @hide
42308      */
42309     /**
42310      * @cfg {String} fieldClass @hide
42311      */
42312     /**
42313      * @cfg {String} focusClass @hide
42314      */
42315     /**
42316      * @cfg {String} autoCreate @hide
42317      */
42318     /**
42319      * @cfg {String} inputType @hide
42320      */
42321     /**
42322      * @cfg {String} invalidClass @hide
42323      */
42324     /**
42325      * @cfg {String} invalidText @hide
42326      */
42327     /**
42328      * @cfg {String} msgFx @hide
42329      */
42330     /**
42331      * @cfg {String} validateOnBlur @hide
42332      */
42333 });
42334
42335 Roo.HtmlEditorCore.white = [
42336         'area', 'br', 'img', 'input', 'hr', 'wbr',
42337         
42338        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42339        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42340        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42341        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42342        'table',   'ul',         'xmp', 
42343        
42344        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42345       'thead',   'tr', 
42346      
42347       'dir', 'menu', 'ol', 'ul', 'dl',
42348        
42349       'embed',  'object'
42350 ];
42351
42352
42353 Roo.HtmlEditorCore.black = [
42354     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42355         'applet', // 
42356         'base',   'basefont', 'bgsound', 'blink',  'body', 
42357         'frame',  'frameset', 'head',    'html',   'ilayer', 
42358         'iframe', 'layer',  'link',     'meta',    'object',   
42359         'script', 'style' ,'title',  'xml' // clean later..
42360 ];
42361 Roo.HtmlEditorCore.clean = [
42362     'script', 'style', 'title', 'xml'
42363 ];
42364 Roo.HtmlEditorCore.remove = [
42365     'font'
42366 ];
42367 // attributes..
42368
42369 Roo.HtmlEditorCore.ablack = [
42370     'on'
42371 ];
42372     
42373 Roo.HtmlEditorCore.aclean = [ 
42374     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42375 ];
42376
42377 // protocols..
42378 Roo.HtmlEditorCore.pwhite= [
42379         'http',  'https',  'mailto'
42380 ];
42381
42382 // white listed style attributes.
42383 Roo.HtmlEditorCore.cwhite= [
42384       //  'text-align', /// default is to allow most things..
42385       
42386          
42387 //        'font-size'//??
42388 ];
42389
42390 // black listed style attributes.
42391 Roo.HtmlEditorCore.cblack= [
42392       //  'font-size' -- this can be set by the project 
42393 ];
42394
42395
42396 Roo.HtmlEditorCore.swapCodes   =[ 
42397     [    8211, "--" ], 
42398     [    8212, "--" ], 
42399     [    8216,  "'" ],  
42400     [    8217, "'" ],  
42401     [    8220, '"' ],  
42402     [    8221, '"' ],  
42403     [    8226, "*" ],  
42404     [    8230, "..." ]
42405 ]; 
42406
42407     //<script type="text/javascript">
42408
42409 /*
42410  * Ext JS Library 1.1.1
42411  * Copyright(c) 2006-2007, Ext JS, LLC.
42412  * Licence LGPL
42413  * 
42414  */
42415  
42416  
42417 Roo.form.HtmlEditor = function(config){
42418     
42419     
42420     
42421     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42422     
42423     if (!this.toolbars) {
42424         this.toolbars = [];
42425     }
42426     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42427     
42428     
42429 };
42430
42431 /**
42432  * @class Roo.form.HtmlEditor
42433  * @extends Roo.form.Field
42434  * Provides a lightweight HTML Editor component.
42435  *
42436  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42437  * 
42438  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42439  * supported by this editor.</b><br/><br/>
42440  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42441  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42442  */
42443 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42444     /**
42445      * @cfg {Boolean} clearUp
42446      */
42447     clearUp : true,
42448       /**
42449      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42450      */
42451     toolbars : false,
42452    
42453      /**
42454      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42455      *                        Roo.resizable.
42456      */
42457     resizable : false,
42458      /**
42459      * @cfg {Number} height (in pixels)
42460      */   
42461     height: 300,
42462    /**
42463      * @cfg {Number} width (in pixels)
42464      */   
42465     width: 500,
42466     
42467     /**
42468      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42469      * 
42470      */
42471     stylesheets: false,
42472     
42473     // id of frame..
42474     frameId: false,
42475     
42476     // private properties
42477     validationEvent : false,
42478     deferHeight: true,
42479     initialized : false,
42480     activated : false,
42481     
42482     onFocus : Roo.emptyFn,
42483     iframePad:3,
42484     hideMode:'offsets',
42485     
42486     defaultAutoCreate : { // modified by initCompnoent..
42487         tag: "textarea",
42488         style:"width:500px;height:300px;",
42489         autocomplete: "off"
42490     },
42491
42492     // private
42493     initComponent : function(){
42494         this.addEvents({
42495             /**
42496              * @event initialize
42497              * Fires when the editor is fully initialized (including the iframe)
42498              * @param {HtmlEditor} this
42499              */
42500             initialize: true,
42501             /**
42502              * @event activate
42503              * Fires when the editor is first receives the focus. Any insertion must wait
42504              * until after this event.
42505              * @param {HtmlEditor} this
42506              */
42507             activate: true,
42508              /**
42509              * @event beforesync
42510              * Fires before the textarea is updated with content from the editor iframe. Return false
42511              * to cancel the sync.
42512              * @param {HtmlEditor} this
42513              * @param {String} html
42514              */
42515             beforesync: true,
42516              /**
42517              * @event beforepush
42518              * Fires before the iframe editor is updated with content from the textarea. Return false
42519              * to cancel the push.
42520              * @param {HtmlEditor} this
42521              * @param {String} html
42522              */
42523             beforepush: true,
42524              /**
42525              * @event sync
42526              * Fires when the textarea is updated with content from the editor iframe.
42527              * @param {HtmlEditor} this
42528              * @param {String} html
42529              */
42530             sync: true,
42531              /**
42532              * @event push
42533              * Fires when the iframe editor is updated with content from the textarea.
42534              * @param {HtmlEditor} this
42535              * @param {String} html
42536              */
42537             push: true,
42538              /**
42539              * @event editmodechange
42540              * Fires when the editor switches edit modes
42541              * @param {HtmlEditor} this
42542              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42543              */
42544             editmodechange: true,
42545             /**
42546              * @event editorevent
42547              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42548              * @param {HtmlEditor} this
42549              */
42550             editorevent: true,
42551             /**
42552              * @event firstfocus
42553              * Fires when on first focus - needed by toolbars..
42554              * @param {HtmlEditor} this
42555              */
42556             firstfocus: true,
42557             /**
42558              * @event autosave
42559              * Auto save the htmlEditor value as a file into Events
42560              * @param {HtmlEditor} this
42561              */
42562             autosave: true,
42563             /**
42564              * @event savedpreview
42565              * preview the saved version of htmlEditor
42566              * @param {HtmlEditor} this
42567              */
42568             savedpreview: true
42569         });
42570         this.defaultAutoCreate =  {
42571             tag: "textarea",
42572             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42573             autocomplete: "off"
42574         };
42575     },
42576
42577     /**
42578      * Protected method that will not generally be called directly. It
42579      * is called when the editor creates its toolbar. Override this method if you need to
42580      * add custom toolbar buttons.
42581      * @param {HtmlEditor} editor
42582      */
42583     createToolbar : function(editor){
42584         Roo.log("create toolbars");
42585         if (!editor.toolbars || !editor.toolbars.length) {
42586             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42587         }
42588         
42589         for (var i =0 ; i < editor.toolbars.length;i++) {
42590             editor.toolbars[i] = Roo.factory(
42591                     typeof(editor.toolbars[i]) == 'string' ?
42592                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42593                 Roo.form.HtmlEditor);
42594             editor.toolbars[i].init(editor);
42595         }
42596          
42597         
42598     },
42599
42600      
42601     // private
42602     onRender : function(ct, position)
42603     {
42604         var _t = this;
42605         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42606         
42607         this.wrap = this.el.wrap({
42608             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42609         });
42610         
42611         this.editorcore.onRender(ct, position);
42612          
42613         if (this.resizable) {
42614             this.resizeEl = new Roo.Resizable(this.wrap, {
42615                 pinned : true,
42616                 wrap: true,
42617                 dynamic : true,
42618                 minHeight : this.height,
42619                 height: this.height,
42620                 handles : this.resizable,
42621                 width: this.width,
42622                 listeners : {
42623                     resize : function(r, w, h) {
42624                         _t.onResize(w,h); // -something
42625                     }
42626                 }
42627             });
42628             
42629         }
42630         this.createToolbar(this);
42631        
42632         
42633         if(!this.width){
42634             this.setSize(this.wrap.getSize());
42635         }
42636         if (this.resizeEl) {
42637             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42638             // should trigger onReize..
42639         }
42640         
42641 //        if(this.autosave && this.w){
42642 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42643 //        }
42644     },
42645
42646     // private
42647     onResize : function(w, h)
42648     {
42649         //Roo.log('resize: ' +w + ',' + h );
42650         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42651         var ew = false;
42652         var eh = false;
42653         
42654         if(this.el ){
42655             if(typeof w == 'number'){
42656                 var aw = w - this.wrap.getFrameWidth('lr');
42657                 this.el.setWidth(this.adjustWidth('textarea', aw));
42658                 ew = aw;
42659             }
42660             if(typeof h == 'number'){
42661                 var tbh = 0;
42662                 for (var i =0; i < this.toolbars.length;i++) {
42663                     // fixme - ask toolbars for heights?
42664                     tbh += this.toolbars[i].tb.el.getHeight();
42665                     if (this.toolbars[i].footer) {
42666                         tbh += this.toolbars[i].footer.el.getHeight();
42667                     }
42668                 }
42669                 
42670                 
42671                 
42672                 
42673                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42674                 ah -= 5; // knock a few pixes off for look..
42675                 this.el.setHeight(this.adjustWidth('textarea', ah));
42676                 var eh = ah;
42677             }
42678         }
42679         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42680         this.editorcore.onResize(ew,eh);
42681         
42682     },
42683
42684     /**
42685      * Toggles the editor between standard and source edit mode.
42686      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42687      */
42688     toggleSourceEdit : function(sourceEditMode)
42689     {
42690         this.editorcore.toggleSourceEdit(sourceEditMode);
42691         
42692         if(this.editorcore.sourceEditMode){
42693             Roo.log('editor - showing textarea');
42694             
42695 //            Roo.log('in');
42696 //            Roo.log(this.syncValue());
42697             this.editorcore.syncValue();
42698             this.el.removeClass('x-hidden');
42699             this.el.dom.removeAttribute('tabIndex');
42700             this.el.focus();
42701         }else{
42702             Roo.log('editor - hiding textarea');
42703 //            Roo.log('out')
42704 //            Roo.log(this.pushValue()); 
42705             this.editorcore.pushValue();
42706             
42707             this.el.addClass('x-hidden');
42708             this.el.dom.setAttribute('tabIndex', -1);
42709             //this.deferFocus();
42710         }
42711          
42712         this.setSize(this.wrap.getSize());
42713         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42714     },
42715  
42716     // private (for BoxComponent)
42717     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42718
42719     // private (for BoxComponent)
42720     getResizeEl : function(){
42721         return this.wrap;
42722     },
42723
42724     // private (for BoxComponent)
42725     getPositionEl : function(){
42726         return this.wrap;
42727     },
42728
42729     // private
42730     initEvents : function(){
42731         this.originalValue = this.getValue();
42732     },
42733
42734     /**
42735      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42736      * @method
42737      */
42738     markInvalid : Roo.emptyFn,
42739     /**
42740      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42741      * @method
42742      */
42743     clearInvalid : Roo.emptyFn,
42744
42745     setValue : function(v){
42746         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42747         this.editorcore.pushValue();
42748     },
42749
42750      
42751     // private
42752     deferFocus : function(){
42753         this.focus.defer(10, this);
42754     },
42755
42756     // doc'ed in Field
42757     focus : function(){
42758         this.editorcore.focus();
42759         
42760     },
42761       
42762
42763     // private
42764     onDestroy : function(){
42765         
42766         
42767         
42768         if(this.rendered){
42769             
42770             for (var i =0; i < this.toolbars.length;i++) {
42771                 // fixme - ask toolbars for heights?
42772                 this.toolbars[i].onDestroy();
42773             }
42774             
42775             this.wrap.dom.innerHTML = '';
42776             this.wrap.remove();
42777         }
42778     },
42779
42780     // private
42781     onFirstFocus : function(){
42782         //Roo.log("onFirstFocus");
42783         this.editorcore.onFirstFocus();
42784          for (var i =0; i < this.toolbars.length;i++) {
42785             this.toolbars[i].onFirstFocus();
42786         }
42787         
42788     },
42789     
42790     // private
42791     syncValue : function()
42792     {
42793         this.editorcore.syncValue();
42794     },
42795     
42796     pushValue : function()
42797     {
42798         this.editorcore.pushValue();
42799     }
42800      
42801     
42802     // hide stuff that is not compatible
42803     /**
42804      * @event blur
42805      * @hide
42806      */
42807     /**
42808      * @event change
42809      * @hide
42810      */
42811     /**
42812      * @event focus
42813      * @hide
42814      */
42815     /**
42816      * @event specialkey
42817      * @hide
42818      */
42819     /**
42820      * @cfg {String} fieldClass @hide
42821      */
42822     /**
42823      * @cfg {String} focusClass @hide
42824      */
42825     /**
42826      * @cfg {String} autoCreate @hide
42827      */
42828     /**
42829      * @cfg {String} inputType @hide
42830      */
42831     /**
42832      * @cfg {String} invalidClass @hide
42833      */
42834     /**
42835      * @cfg {String} invalidText @hide
42836      */
42837     /**
42838      * @cfg {String} msgFx @hide
42839      */
42840     /**
42841      * @cfg {String} validateOnBlur @hide
42842      */
42843 });
42844  
42845     // <script type="text/javascript">
42846 /*
42847  * Based on
42848  * Ext JS Library 1.1.1
42849  * Copyright(c) 2006-2007, Ext JS, LLC.
42850  *  
42851  
42852  */
42853
42854 /**
42855  * @class Roo.form.HtmlEditorToolbar1
42856  * Basic Toolbar
42857  * 
42858  * Usage:
42859  *
42860  new Roo.form.HtmlEditor({
42861     ....
42862     toolbars : [
42863         new Roo.form.HtmlEditorToolbar1({
42864             disable : { fonts: 1 , format: 1, ..., ... , ...],
42865             btns : [ .... ]
42866         })
42867     }
42868      
42869  * 
42870  * @cfg {Object} disable List of elements to disable..
42871  * @cfg {Array} btns List of additional buttons.
42872  * 
42873  * 
42874  * NEEDS Extra CSS? 
42875  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42876  */
42877  
42878 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42879 {
42880     
42881     Roo.apply(this, config);
42882     
42883     // default disabled, based on 'good practice'..
42884     this.disable = this.disable || {};
42885     Roo.applyIf(this.disable, {
42886         fontSize : true,
42887         colors : true,
42888         specialElements : true
42889     });
42890     
42891     
42892     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42893     // dont call parent... till later.
42894 }
42895
42896 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42897     
42898     tb: false,
42899     
42900     rendered: false,
42901     
42902     editor : false,
42903     editorcore : false,
42904     /**
42905      * @cfg {Object} disable  List of toolbar elements to disable
42906          
42907      */
42908     disable : false,
42909     
42910     
42911      /**
42912      * @cfg {String} createLinkText The default text for the create link prompt
42913      */
42914     createLinkText : 'Please enter the URL for the link:',
42915     /**
42916      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42917      */
42918     defaultLinkValue : 'http:/'+'/',
42919    
42920     
42921       /**
42922      * @cfg {Array} fontFamilies An array of available font families
42923      */
42924     fontFamilies : [
42925         'Arial',
42926         'Courier New',
42927         'Tahoma',
42928         'Times New Roman',
42929         'Verdana'
42930     ],
42931     
42932     specialChars : [
42933            "&#169;",
42934           "&#174;",     
42935           "&#8482;",    
42936           "&#163;" ,    
42937          // "&#8212;",    
42938           "&#8230;",    
42939           "&#247;" ,    
42940         //  "&#225;" ,     ?? a acute?
42941            "&#8364;"    , //Euro
42942        //   "&#8220;"    ,
42943         //  "&#8221;"    ,
42944         //  "&#8226;"    ,
42945           "&#176;"  //   , // degrees
42946
42947          // "&#233;"     , // e ecute
42948          // "&#250;"     , // u ecute?
42949     ],
42950     
42951     specialElements : [
42952         {
42953             text: "Insert Table",
42954             xtype: 'MenuItem',
42955             xns : Roo.Menu,
42956             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42957                 
42958         },
42959         {    
42960             text: "Insert Image",
42961             xtype: 'MenuItem',
42962             xns : Roo.Menu,
42963             ihtml : '<img src="about:blank"/>'
42964             
42965         }
42966         
42967          
42968     ],
42969     
42970     
42971     inputElements : [ 
42972             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42973             "input:submit", "input:button", "select", "textarea", "label" ],
42974     formats : [
42975         ["p"] ,  
42976         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42977         ["pre"],[ "code"], 
42978         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42979         ['div'],['span']
42980     ],
42981     
42982     cleanStyles : [
42983         "font-size"
42984     ],
42985      /**
42986      * @cfg {String} defaultFont default font to use.
42987      */
42988     defaultFont: 'tahoma',
42989    
42990     fontSelect : false,
42991     
42992     
42993     formatCombo : false,
42994     
42995     init : function(editor)
42996     {
42997         this.editor = editor;
42998         this.editorcore = editor.editorcore ? editor.editorcore : editor;
42999         var editorcore = this.editorcore;
43000         
43001         var _t = this;
43002         
43003         var fid = editorcore.frameId;
43004         var etb = this;
43005         function btn(id, toggle, handler){
43006             var xid = fid + '-'+ id ;
43007             return {
43008                 id : xid,
43009                 cmd : id,
43010                 cls : 'x-btn-icon x-edit-'+id,
43011                 enableToggle:toggle !== false,
43012                 scope: _t, // was editor...
43013                 handler:handler||_t.relayBtnCmd,
43014                 clickEvent:'mousedown',
43015                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43016                 tabIndex:-1
43017             };
43018         }
43019         
43020         
43021         
43022         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43023         this.tb = tb;
43024          // stop form submits
43025         tb.el.on('click', function(e){
43026             e.preventDefault(); // what does this do?
43027         });
43028
43029         if(!this.disable.font) { // && !Roo.isSafari){
43030             /* why no safari for fonts 
43031             editor.fontSelect = tb.el.createChild({
43032                 tag:'select',
43033                 tabIndex: -1,
43034                 cls:'x-font-select',
43035                 html: this.createFontOptions()
43036             });
43037             
43038             editor.fontSelect.on('change', function(){
43039                 var font = editor.fontSelect.dom.value;
43040                 editor.relayCmd('fontname', font);
43041                 editor.deferFocus();
43042             }, editor);
43043             
43044             tb.add(
43045                 editor.fontSelect.dom,
43046                 '-'
43047             );
43048             */
43049             
43050         };
43051         if(!this.disable.formats){
43052             this.formatCombo = new Roo.form.ComboBox({
43053                 store: new Roo.data.SimpleStore({
43054                     id : 'tag',
43055                     fields: ['tag'],
43056                     data : this.formats // from states.js
43057                 }),
43058                 blockFocus : true,
43059                 name : '',
43060                 //autoCreate : {tag: "div",  size: "20"},
43061                 displayField:'tag',
43062                 typeAhead: false,
43063                 mode: 'local',
43064                 editable : false,
43065                 triggerAction: 'all',
43066                 emptyText:'Add tag',
43067                 selectOnFocus:true,
43068                 width:135,
43069                 listeners : {
43070                     'select': function(c, r, i) {
43071                         editorcore.insertTag(r.get('tag'));
43072                         editor.focus();
43073                     }
43074                 }
43075
43076             });
43077             tb.addField(this.formatCombo);
43078             
43079         }
43080         
43081         if(!this.disable.format){
43082             tb.add(
43083                 btn('bold'),
43084                 btn('italic'),
43085                 btn('underline')
43086             );
43087         };
43088         if(!this.disable.fontSize){
43089             tb.add(
43090                 '-',
43091                 
43092                 
43093                 btn('increasefontsize', false, editorcore.adjustFont),
43094                 btn('decreasefontsize', false, editorcore.adjustFont)
43095             );
43096         };
43097         
43098         
43099         if(!this.disable.colors){
43100             tb.add(
43101                 '-', {
43102                     id:editorcore.frameId +'-forecolor',
43103                     cls:'x-btn-icon x-edit-forecolor',
43104                     clickEvent:'mousedown',
43105                     tooltip: this.buttonTips['forecolor'] || undefined,
43106                     tabIndex:-1,
43107                     menu : new Roo.menu.ColorMenu({
43108                         allowReselect: true,
43109                         focus: Roo.emptyFn,
43110                         value:'000000',
43111                         plain:true,
43112                         selectHandler: function(cp, color){
43113                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43114                             editor.deferFocus();
43115                         },
43116                         scope: editorcore,
43117                         clickEvent:'mousedown'
43118                     })
43119                 }, {
43120                     id:editorcore.frameId +'backcolor',
43121                     cls:'x-btn-icon x-edit-backcolor',
43122                     clickEvent:'mousedown',
43123                     tooltip: this.buttonTips['backcolor'] || undefined,
43124                     tabIndex:-1,
43125                     menu : new Roo.menu.ColorMenu({
43126                         focus: Roo.emptyFn,
43127                         value:'FFFFFF',
43128                         plain:true,
43129                         allowReselect: true,
43130                         selectHandler: function(cp, color){
43131                             if(Roo.isGecko){
43132                                 editorcore.execCmd('useCSS', false);
43133                                 editorcore.execCmd('hilitecolor', color);
43134                                 editorcore.execCmd('useCSS', true);
43135                                 editor.deferFocus();
43136                             }else{
43137                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43138                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43139                                 editor.deferFocus();
43140                             }
43141                         },
43142                         scope:editorcore,
43143                         clickEvent:'mousedown'
43144                     })
43145                 }
43146             );
43147         };
43148         // now add all the items...
43149         
43150
43151         if(!this.disable.alignments){
43152             tb.add(
43153                 '-',
43154                 btn('justifyleft'),
43155                 btn('justifycenter'),
43156                 btn('justifyright')
43157             );
43158         };
43159
43160         //if(!Roo.isSafari){
43161             if(!this.disable.links){
43162                 tb.add(
43163                     '-',
43164                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43165                 );
43166             };
43167
43168             if(!this.disable.lists){
43169                 tb.add(
43170                     '-',
43171                     btn('insertorderedlist'),
43172                     btn('insertunorderedlist')
43173                 );
43174             }
43175             if(!this.disable.sourceEdit){
43176                 tb.add(
43177                     '-',
43178                     btn('sourceedit', true, function(btn){
43179                         Roo.log(this);
43180                         this.toggleSourceEdit(btn.pressed);
43181                     })
43182                 );
43183             }
43184         //}
43185         
43186         var smenu = { };
43187         // special menu.. - needs to be tidied up..
43188         if (!this.disable.special) {
43189             smenu = {
43190                 text: "&#169;",
43191                 cls: 'x-edit-none',
43192                 
43193                 menu : {
43194                     items : []
43195                 }
43196             };
43197             for (var i =0; i < this.specialChars.length; i++) {
43198                 smenu.menu.items.push({
43199                     
43200                     html: this.specialChars[i],
43201                     handler: function(a,b) {
43202                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43203                         //editor.insertAtCursor(a.html);
43204                         
43205                     },
43206                     tabIndex:-1
43207                 });
43208             }
43209             
43210             
43211             tb.add(smenu);
43212             
43213             
43214         }
43215         
43216         var cmenu = { };
43217         if (!this.disable.cleanStyles) {
43218             cmenu = {
43219                 cls: 'x-btn-icon x-btn-clear',
43220                 
43221                 menu : {
43222                     items : []
43223                 }
43224             };
43225             for (var i =0; i < this.cleanStyles.length; i++) {
43226                 cmenu.menu.items.push({
43227                     actiontype : this.cleanStyles[i],
43228                     html: 'Remove ' + this.cleanStyles[i],
43229                     handler: function(a,b) {
43230                         Roo.log(a);
43231                         Roo.log(b);
43232                         var c = Roo.get(editorcore.doc.body);
43233                         c.select('[style]').each(function(s) {
43234                             s.dom.style.removeProperty(a.actiontype);
43235                         });
43236                         editorcore.syncValue();
43237                     },
43238                     tabIndex:-1
43239                 });
43240             }
43241             cmenu.menu.items.push({
43242                 actiontype : 'word',
43243                 html: 'Remove MS Word Formating',
43244                 handler: function(a,b) {
43245                     editorcore.cleanWord();
43246                     editorcore.syncValue();
43247                 },
43248                 tabIndex:-1
43249             });
43250             
43251             cmenu.menu.items.push({
43252                 actiontype : 'all',
43253                 html: 'Remove All Styles',
43254                 handler: function(a,b) {
43255                     
43256                     var c = Roo.get(editorcore.doc.body);
43257                     c.select('[style]').each(function(s) {
43258                         s.dom.removeAttribute('style');
43259                     });
43260                     editorcore.syncValue();
43261                 },
43262                 tabIndex:-1
43263             });
43264              cmenu.menu.items.push({
43265                 actiontype : 'word',
43266                 html: 'Tidy HTML Source',
43267                 handler: function(a,b) {
43268                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43269                     editorcore.syncValue();
43270                 },
43271                 tabIndex:-1
43272             });
43273             
43274             
43275             tb.add(cmenu);
43276         }
43277          
43278         if (!this.disable.specialElements) {
43279             var semenu = {
43280                 text: "Other;",
43281                 cls: 'x-edit-none',
43282                 menu : {
43283                     items : []
43284                 }
43285             };
43286             for (var i =0; i < this.specialElements.length; i++) {
43287                 semenu.menu.items.push(
43288                     Roo.apply({ 
43289                         handler: function(a,b) {
43290                             editor.insertAtCursor(this.ihtml);
43291                         }
43292                     }, this.specialElements[i])
43293                 );
43294                     
43295             }
43296             
43297             tb.add(semenu);
43298             
43299             
43300         }
43301          
43302         
43303         if (this.btns) {
43304             for(var i =0; i< this.btns.length;i++) {
43305                 var b = Roo.factory(this.btns[i],Roo.form);
43306                 b.cls =  'x-edit-none';
43307                 b.scope = editorcore;
43308                 tb.add(b);
43309             }
43310         
43311         }
43312         
43313         
43314         
43315         // disable everything...
43316         
43317         this.tb.items.each(function(item){
43318            if(item.id != editorcore.frameId+ '-sourceedit'){
43319                 item.disable();
43320             }
43321         });
43322         this.rendered = true;
43323         
43324         // the all the btns;
43325         editor.on('editorevent', this.updateToolbar, this);
43326         // other toolbars need to implement this..
43327         //editor.on('editmodechange', this.updateToolbar, this);
43328     },
43329     
43330     
43331     relayBtnCmd : function(btn) {
43332         this.editorcore.relayCmd(btn.cmd);
43333     },
43334     // private used internally
43335     createLink : function(){
43336         Roo.log("create link?");
43337         var url = prompt(this.createLinkText, this.defaultLinkValue);
43338         if(url && url != 'http:/'+'/'){
43339             this.editorcore.relayCmd('createlink', url);
43340         }
43341     },
43342
43343     
43344     /**
43345      * Protected method that will not generally be called directly. It triggers
43346      * a toolbar update by reading the markup state of the current selection in the editor.
43347      */
43348     updateToolbar: function(){
43349
43350         if(!this.editorcore.activated){
43351             this.editor.onFirstFocus();
43352             return;
43353         }
43354
43355         var btns = this.tb.items.map, 
43356             doc = this.editorcore.doc,
43357             frameId = this.editorcore.frameId;
43358
43359         if(!this.disable.font && !Roo.isSafari){
43360             /*
43361             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43362             if(name != this.fontSelect.dom.value){
43363                 this.fontSelect.dom.value = name;
43364             }
43365             */
43366         }
43367         if(!this.disable.format){
43368             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43369             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43370             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43371         }
43372         if(!this.disable.alignments){
43373             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43374             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43375             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43376         }
43377         if(!Roo.isSafari && !this.disable.lists){
43378             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43379             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43380         }
43381         
43382         var ans = this.editorcore.getAllAncestors();
43383         if (this.formatCombo) {
43384             
43385             
43386             var store = this.formatCombo.store;
43387             this.formatCombo.setValue("");
43388             for (var i =0; i < ans.length;i++) {
43389                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43390                     // select it..
43391                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43392                     break;
43393                 }
43394             }
43395         }
43396         
43397         
43398         
43399         // hides menus... - so this cant be on a menu...
43400         Roo.menu.MenuMgr.hideAll();
43401
43402         //this.editorsyncValue();
43403     },
43404    
43405     
43406     createFontOptions : function(){
43407         var buf = [], fs = this.fontFamilies, ff, lc;
43408         
43409         
43410         
43411         for(var i = 0, len = fs.length; i< len; i++){
43412             ff = fs[i];
43413             lc = ff.toLowerCase();
43414             buf.push(
43415                 '<option value="',lc,'" style="font-family:',ff,';"',
43416                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43417                     ff,
43418                 '</option>'
43419             );
43420         }
43421         return buf.join('');
43422     },
43423     
43424     toggleSourceEdit : function(sourceEditMode){
43425         
43426         Roo.log("toolbar toogle");
43427         if(sourceEditMode === undefined){
43428             sourceEditMode = !this.sourceEditMode;
43429         }
43430         this.sourceEditMode = sourceEditMode === true;
43431         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43432         // just toggle the button?
43433         if(btn.pressed !== this.sourceEditMode){
43434             btn.toggle(this.sourceEditMode);
43435             return;
43436         }
43437         
43438         if(sourceEditMode){
43439             Roo.log("disabling buttons");
43440             this.tb.items.each(function(item){
43441                 if(item.cmd != 'sourceedit'){
43442                     item.disable();
43443                 }
43444             });
43445           
43446         }else{
43447             Roo.log("enabling buttons");
43448             if(this.editorcore.initialized){
43449                 this.tb.items.each(function(item){
43450                     item.enable();
43451                 });
43452             }
43453             
43454         }
43455         Roo.log("calling toggole on editor");
43456         // tell the editor that it's been pressed..
43457         this.editor.toggleSourceEdit(sourceEditMode);
43458        
43459     },
43460      /**
43461      * Object collection of toolbar tooltips for the buttons in the editor. The key
43462      * is the command id associated with that button and the value is a valid QuickTips object.
43463      * For example:
43464 <pre><code>
43465 {
43466     bold : {
43467         title: 'Bold (Ctrl+B)',
43468         text: 'Make the selected text bold.',
43469         cls: 'x-html-editor-tip'
43470     },
43471     italic : {
43472         title: 'Italic (Ctrl+I)',
43473         text: 'Make the selected text italic.',
43474         cls: 'x-html-editor-tip'
43475     },
43476     ...
43477 </code></pre>
43478     * @type Object
43479      */
43480     buttonTips : {
43481         bold : {
43482             title: 'Bold (Ctrl+B)',
43483             text: 'Make the selected text bold.',
43484             cls: 'x-html-editor-tip'
43485         },
43486         italic : {
43487             title: 'Italic (Ctrl+I)',
43488             text: 'Make the selected text italic.',
43489             cls: 'x-html-editor-tip'
43490         },
43491         underline : {
43492             title: 'Underline (Ctrl+U)',
43493             text: 'Underline the selected text.',
43494             cls: 'x-html-editor-tip'
43495         },
43496         increasefontsize : {
43497             title: 'Grow Text',
43498             text: 'Increase the font size.',
43499             cls: 'x-html-editor-tip'
43500         },
43501         decreasefontsize : {
43502             title: 'Shrink Text',
43503             text: 'Decrease the font size.',
43504             cls: 'x-html-editor-tip'
43505         },
43506         backcolor : {
43507             title: 'Text Highlight Color',
43508             text: 'Change the background color of the selected text.',
43509             cls: 'x-html-editor-tip'
43510         },
43511         forecolor : {
43512             title: 'Font Color',
43513             text: 'Change the color of the selected text.',
43514             cls: 'x-html-editor-tip'
43515         },
43516         justifyleft : {
43517             title: 'Align Text Left',
43518             text: 'Align text to the left.',
43519             cls: 'x-html-editor-tip'
43520         },
43521         justifycenter : {
43522             title: 'Center Text',
43523             text: 'Center text in the editor.',
43524             cls: 'x-html-editor-tip'
43525         },
43526         justifyright : {
43527             title: 'Align Text Right',
43528             text: 'Align text to the right.',
43529             cls: 'x-html-editor-tip'
43530         },
43531         insertunorderedlist : {
43532             title: 'Bullet List',
43533             text: 'Start a bulleted list.',
43534             cls: 'x-html-editor-tip'
43535         },
43536         insertorderedlist : {
43537             title: 'Numbered List',
43538             text: 'Start a numbered list.',
43539             cls: 'x-html-editor-tip'
43540         },
43541         createlink : {
43542             title: 'Hyperlink',
43543             text: 'Make the selected text a hyperlink.',
43544             cls: 'x-html-editor-tip'
43545         },
43546         sourceedit : {
43547             title: 'Source Edit',
43548             text: 'Switch to source editing mode.',
43549             cls: 'x-html-editor-tip'
43550         }
43551     },
43552     // private
43553     onDestroy : function(){
43554         if(this.rendered){
43555             
43556             this.tb.items.each(function(item){
43557                 if(item.menu){
43558                     item.menu.removeAll();
43559                     if(item.menu.el){
43560                         item.menu.el.destroy();
43561                     }
43562                 }
43563                 item.destroy();
43564             });
43565              
43566         }
43567     },
43568     onFirstFocus: function() {
43569         this.tb.items.each(function(item){
43570            item.enable();
43571         });
43572     }
43573 });
43574
43575
43576
43577
43578 // <script type="text/javascript">
43579 /*
43580  * Based on
43581  * Ext JS Library 1.1.1
43582  * Copyright(c) 2006-2007, Ext JS, LLC.
43583  *  
43584  
43585  */
43586
43587  
43588 /**
43589  * @class Roo.form.HtmlEditor.ToolbarContext
43590  * Context Toolbar
43591  * 
43592  * Usage:
43593  *
43594  new Roo.form.HtmlEditor({
43595     ....
43596     toolbars : [
43597         { xtype: 'ToolbarStandard', styles : {} }
43598         { xtype: 'ToolbarContext', disable : {} }
43599     ]
43600 })
43601
43602      
43603  * 
43604  * @config : {Object} disable List of elements to disable.. (not done yet.)
43605  * @config : {Object} styles  Map of styles available.
43606  * 
43607  */
43608
43609 Roo.form.HtmlEditor.ToolbarContext = function(config)
43610 {
43611     
43612     Roo.apply(this, config);
43613     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43614     // dont call parent... till later.
43615     this.styles = this.styles || {};
43616 }
43617
43618  
43619
43620 Roo.form.HtmlEditor.ToolbarContext.types = {
43621     'IMG' : {
43622         width : {
43623             title: "Width",
43624             width: 40
43625         },
43626         height:  {
43627             title: "Height",
43628             width: 40
43629         },
43630         align: {
43631             title: "Align",
43632             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43633             width : 80
43634             
43635         },
43636         border: {
43637             title: "Border",
43638             width: 40
43639         },
43640         alt: {
43641             title: "Alt",
43642             width: 120
43643         },
43644         src : {
43645             title: "Src",
43646             width: 220
43647         }
43648         
43649     },
43650     'A' : {
43651         name : {
43652             title: "Name",
43653             width: 50
43654         },
43655         target:  {
43656             title: "Target",
43657             width: 120
43658         },
43659         href:  {
43660             title: "Href",
43661             width: 220
43662         } // border?
43663         
43664     },
43665     'TABLE' : {
43666         rows : {
43667             title: "Rows",
43668             width: 20
43669         },
43670         cols : {
43671             title: "Cols",
43672             width: 20
43673         },
43674         width : {
43675             title: "Width",
43676             width: 40
43677         },
43678         height : {
43679             title: "Height",
43680             width: 40
43681         },
43682         border : {
43683             title: "Border",
43684             width: 20
43685         }
43686     },
43687     'TD' : {
43688         width : {
43689             title: "Width",
43690             width: 40
43691         },
43692         height : {
43693             title: "Height",
43694             width: 40
43695         },   
43696         align: {
43697             title: "Align",
43698             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43699             width: 80
43700         },
43701         valign: {
43702             title: "Valign",
43703             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43704             width: 80
43705         },
43706         colspan: {
43707             title: "Colspan",
43708             width: 20
43709             
43710         },
43711          'font-family'  : {
43712             title : "Font",
43713             style : 'fontFamily',
43714             displayField: 'display',
43715             optname : 'font-family',
43716             width: 140
43717         }
43718     },
43719     'INPUT' : {
43720         name : {
43721             title: "name",
43722             width: 120
43723         },
43724         value : {
43725             title: "Value",
43726             width: 120
43727         },
43728         width : {
43729             title: "Width",
43730             width: 40
43731         }
43732     },
43733     'LABEL' : {
43734         'for' : {
43735             title: "For",
43736             width: 120
43737         }
43738     },
43739     'TEXTAREA' : {
43740           name : {
43741             title: "name",
43742             width: 120
43743         },
43744         rows : {
43745             title: "Rows",
43746             width: 20
43747         },
43748         cols : {
43749             title: "Cols",
43750             width: 20
43751         }
43752     },
43753     'SELECT' : {
43754         name : {
43755             title: "name",
43756             width: 120
43757         },
43758         selectoptions : {
43759             title: "Options",
43760             width: 200
43761         }
43762     },
43763     
43764     // should we really allow this??
43765     // should this just be 
43766     'BODY' : {
43767         title : {
43768             title: "Title",
43769             width: 200,
43770             disabled : true
43771         }
43772     },
43773     'SPAN' : {
43774         'font-family'  : {
43775             title : "Font",
43776             style : 'fontFamily',
43777             displayField: 'display',
43778             optname : 'font-family',
43779             width: 140
43780         }
43781     },
43782     'DIV' : {
43783         'font-family'  : {
43784             title : "Font",
43785             style : 'fontFamily',
43786             displayField: 'display',
43787             optname : 'font-family',
43788             width: 140
43789         }
43790     },
43791      'P' : {
43792         'font-family'  : {
43793             title : "Font",
43794             style : 'fontFamily',
43795             displayField: 'display',
43796             optname : 'font-family',
43797             width: 140
43798         }
43799     },
43800     
43801     '*' : {
43802         // empty..
43803     }
43804
43805 };
43806
43807 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43808 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43809
43810 Roo.form.HtmlEditor.ToolbarContext.options = {
43811         'font-family'  : [ 
43812                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43813                 [ 'Courier New', 'Courier New'],
43814                 [ 'Tahoma', 'Tahoma'],
43815                 [ 'Times New Roman,serif', 'Times'],
43816                 [ 'Verdana','Verdana' ]
43817         ]
43818 };
43819
43820 // fixme - these need to be configurable..
43821  
43822
43823 Roo.form.HtmlEditor.ToolbarContext.types
43824
43825
43826 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43827     
43828     tb: false,
43829     
43830     rendered: false,
43831     
43832     editor : false,
43833     editorcore : false,
43834     /**
43835      * @cfg {Object} disable  List of toolbar elements to disable
43836          
43837      */
43838     disable : false,
43839     /**
43840      * @cfg {Object} styles List of styles 
43841      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43842      *
43843      * These must be defined in the page, so they get rendered correctly..
43844      * .headline { }
43845      * TD.underline { }
43846      * 
43847      */
43848     styles : false,
43849     
43850     options: false,
43851     
43852     toolbars : false,
43853     
43854     init : function(editor)
43855     {
43856         this.editor = editor;
43857         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43858         var editorcore = this.editorcore;
43859         
43860         var fid = editorcore.frameId;
43861         var etb = this;
43862         function btn(id, toggle, handler){
43863             var xid = fid + '-'+ id ;
43864             return {
43865                 id : xid,
43866                 cmd : id,
43867                 cls : 'x-btn-icon x-edit-'+id,
43868                 enableToggle:toggle !== false,
43869                 scope: editorcore, // was editor...
43870                 handler:handler||editorcore.relayBtnCmd,
43871                 clickEvent:'mousedown',
43872                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43873                 tabIndex:-1
43874             };
43875         }
43876         // create a new element.
43877         var wdiv = editor.wrap.createChild({
43878                 tag: 'div'
43879             }, editor.wrap.dom.firstChild.nextSibling, true);
43880         
43881         // can we do this more than once??
43882         
43883          // stop form submits
43884       
43885  
43886         // disable everything...
43887         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43888         this.toolbars = {};
43889            
43890         for (var i in  ty) {
43891           
43892             this.toolbars[i] = this.buildToolbar(ty[i],i);
43893         }
43894         this.tb = this.toolbars.BODY;
43895         this.tb.el.show();
43896         this.buildFooter();
43897         this.footer.show();
43898         editor.on('hide', function( ) { this.footer.hide() }, this);
43899         editor.on('show', function( ) { this.footer.show() }, this);
43900         
43901          
43902         this.rendered = true;
43903         
43904         // the all the btns;
43905         editor.on('editorevent', this.updateToolbar, this);
43906         // other toolbars need to implement this..
43907         //editor.on('editmodechange', this.updateToolbar, this);
43908     },
43909     
43910     
43911     
43912     /**
43913      * Protected method that will not generally be called directly. It triggers
43914      * a toolbar update by reading the markup state of the current selection in the editor.
43915      */
43916     updateToolbar: function(editor,ev,sel){
43917
43918         //Roo.log(ev);
43919         // capture mouse up - this is handy for selecting images..
43920         // perhaps should go somewhere else...
43921         if(!this.editorcore.activated){
43922              this.editor.onFirstFocus();
43923             return;
43924         }
43925         
43926         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43927         // selectNode - might want to handle IE?
43928         if (ev &&
43929             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43930             ev.target && ev.target.tagName == 'IMG') {
43931             // they have click on an image...
43932             // let's see if we can change the selection...
43933             sel = ev.target;
43934          
43935               var nodeRange = sel.ownerDocument.createRange();
43936             try {
43937                 nodeRange.selectNode(sel);
43938             } catch (e) {
43939                 nodeRange.selectNodeContents(sel);
43940             }
43941             //nodeRange.collapse(true);
43942             var s = this.editorcore.win.getSelection();
43943             s.removeAllRanges();
43944             s.addRange(nodeRange);
43945         }  
43946         
43947       
43948         var updateFooter = sel ? false : true;
43949         
43950         
43951         var ans = this.editorcore.getAllAncestors();
43952         
43953         // pick
43954         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43955         
43956         if (!sel) { 
43957             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43958             sel = sel ? sel : this.editorcore.doc.body;
43959             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43960             
43961         }
43962         // pick a menu that exists..
43963         var tn = sel.tagName.toUpperCase();
43964         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43965         
43966         tn = sel.tagName.toUpperCase();
43967         
43968         var lastSel = this.tb.selectedNode
43969         
43970         this.tb.selectedNode = sel;
43971         
43972         // if current menu does not match..
43973         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43974                 
43975             this.tb.el.hide();
43976             ///console.log("show: " + tn);
43977             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43978             this.tb.el.show();
43979             // update name
43980             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43981             
43982             
43983             // update attributes
43984             if (this.tb.fields) {
43985                 this.tb.fields.each(function(e) {
43986                     if (e.stylename) {
43987                         e.setValue(sel.style[e.stylename]);
43988                         return;
43989                     } 
43990                    e.setValue(sel.getAttribute(e.attrname));
43991                 });
43992             }
43993             
43994             var hasStyles = false;
43995             for(var i in this.styles) {
43996                 hasStyles = true;
43997                 break;
43998             }
43999             
44000             // update styles
44001             if (hasStyles) { 
44002                 var st = this.tb.fields.item(0);
44003                 
44004                 st.store.removeAll();
44005                
44006                 
44007                 var cn = sel.className.split(/\s+/);
44008                 
44009                 var avs = [];
44010                 if (this.styles['*']) {
44011                     
44012                     Roo.each(this.styles['*'], function(v) {
44013                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44014                     });
44015                 }
44016                 if (this.styles[tn]) { 
44017                     Roo.each(this.styles[tn], function(v) {
44018                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44019                     });
44020                 }
44021                 
44022                 st.store.loadData(avs);
44023                 st.collapse();
44024                 st.setValue(cn);
44025             }
44026             // flag our selected Node.
44027             this.tb.selectedNode = sel;
44028            
44029            
44030             Roo.menu.MenuMgr.hideAll();
44031
44032         }
44033         
44034         if (!updateFooter) {
44035             //this.footDisp.dom.innerHTML = ''; 
44036             return;
44037         }
44038         // update the footer
44039         //
44040         var html = '';
44041         
44042         this.footerEls = ans.reverse();
44043         Roo.each(this.footerEls, function(a,i) {
44044             if (!a) { return; }
44045             html += html.length ? ' &gt; '  :  '';
44046             
44047             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44048             
44049         });
44050        
44051         // 
44052         var sz = this.footDisp.up('td').getSize();
44053         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44054         this.footDisp.dom.style.marginLeft = '5px';
44055         
44056         this.footDisp.dom.style.overflow = 'hidden';
44057         
44058         this.footDisp.dom.innerHTML = html;
44059             
44060         //this.editorsyncValue();
44061     },
44062      
44063     
44064    
44065        
44066     // private
44067     onDestroy : function(){
44068         if(this.rendered){
44069             
44070             this.tb.items.each(function(item){
44071                 if(item.menu){
44072                     item.menu.removeAll();
44073                     if(item.menu.el){
44074                         item.menu.el.destroy();
44075                     }
44076                 }
44077                 item.destroy();
44078             });
44079              
44080         }
44081     },
44082     onFirstFocus: function() {
44083         // need to do this for all the toolbars..
44084         this.tb.items.each(function(item){
44085            item.enable();
44086         });
44087     },
44088     buildToolbar: function(tlist, nm)
44089     {
44090         var editor = this.editor;
44091         var editorcore = this.editorcore;
44092          // create a new element.
44093         var wdiv = editor.wrap.createChild({
44094                 tag: 'div'
44095             }, editor.wrap.dom.firstChild.nextSibling, true);
44096         
44097        
44098         var tb = new Roo.Toolbar(wdiv);
44099         // add the name..
44100         
44101         tb.add(nm+ ":&nbsp;");
44102         
44103         var styles = [];
44104         for(var i in this.styles) {
44105             styles.push(i);
44106         }
44107         
44108         // styles...
44109         if (styles && styles.length) {
44110             
44111             // this needs a multi-select checkbox...
44112             tb.addField( new Roo.form.ComboBox({
44113                 store: new Roo.data.SimpleStore({
44114                     id : 'val',
44115                     fields: ['val', 'selected'],
44116                     data : [] 
44117                 }),
44118                 name : '-roo-edit-className',
44119                 attrname : 'className',
44120                 displayField: 'val',
44121                 typeAhead: false,
44122                 mode: 'local',
44123                 editable : false,
44124                 triggerAction: 'all',
44125                 emptyText:'Select Style',
44126                 selectOnFocus:true,
44127                 width: 130,
44128                 listeners : {
44129                     'select': function(c, r, i) {
44130                         // initial support only for on class per el..
44131                         tb.selectedNode.className =  r ? r.get('val') : '';
44132                         editorcore.syncValue();
44133                     }
44134                 }
44135     
44136             }));
44137         }
44138         
44139         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44140         var tbops = tbc.options;
44141         
44142         for (var i in tlist) {
44143             
44144             var item = tlist[i];
44145             tb.add(item.title + ":&nbsp;");
44146             
44147             
44148             //optname == used so you can configure the options available..
44149             var opts = item.opts ? item.opts : false;
44150             if (item.optname) {
44151                 opts = tbops[item.optname];
44152            
44153             }
44154             
44155             if (opts) {
44156                 // opts == pulldown..
44157                 tb.addField( new Roo.form.ComboBox({
44158                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44159                         id : 'val',
44160                         fields: ['val', 'display'],
44161                         data : opts  
44162                     }),
44163                     name : '-roo-edit-' + i,
44164                     attrname : i,
44165                     stylename : item.style ? item.style : false,
44166                     displayField: item.displayField ? item.displayField : 'val',
44167                     valueField :  'val',
44168                     typeAhead: false,
44169                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44170                     editable : false,
44171                     triggerAction: 'all',
44172                     emptyText:'Select',
44173                     selectOnFocus:true,
44174                     width: item.width ? item.width  : 130,
44175                     listeners : {
44176                         'select': function(c, r, i) {
44177                             if (c.stylename) {
44178                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44179                                 return;
44180                             }
44181                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44182                         }
44183                     }
44184
44185                 }));
44186                 continue;
44187                     
44188                  
44189                 
44190                 tb.addField( new Roo.form.TextField({
44191                     name: i,
44192                     width: 100,
44193                     //allowBlank:false,
44194                     value: ''
44195                 }));
44196                 continue;
44197             }
44198             tb.addField( new Roo.form.TextField({
44199                 name: '-roo-edit-' + i,
44200                 attrname : i,
44201                 
44202                 width: item.width,
44203                 //allowBlank:true,
44204                 value: '',
44205                 listeners: {
44206                     'change' : function(f, nv, ov) {
44207                         tb.selectedNode.setAttribute(f.attrname, nv);
44208                     }
44209                 }
44210             }));
44211              
44212         }
44213         tb.addFill();
44214         var _this = this;
44215         tb.addButton( {
44216             text: 'Remove Tag',
44217     
44218             listeners : {
44219                 click : function ()
44220                 {
44221                     // remove
44222                     // undo does not work.
44223                      
44224                     var sn = tb.selectedNode;
44225                     
44226                     var pn = sn.parentNode;
44227                     
44228                     var stn =  sn.childNodes[0];
44229                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44230                     while (sn.childNodes.length) {
44231                         var node = sn.childNodes[0];
44232                         sn.removeChild(node);
44233                         //Roo.log(node);
44234                         pn.insertBefore(node, sn);
44235                         
44236                     }
44237                     pn.removeChild(sn);
44238                     var range = editorcore.createRange();
44239         
44240                     range.setStart(stn,0);
44241                     range.setEnd(en,0); //????
44242                     //range.selectNode(sel);
44243                     
44244                     
44245                     var selection = editorcore.getSelection();
44246                     selection.removeAllRanges();
44247                     selection.addRange(range);
44248                     
44249                     
44250                     
44251                     //_this.updateToolbar(null, null, pn);
44252                     _this.updateToolbar(null, null, null);
44253                     _this.footDisp.dom.innerHTML = ''; 
44254                 }
44255             }
44256             
44257                     
44258                 
44259             
44260         });
44261         
44262         
44263         tb.el.on('click', function(e){
44264             e.preventDefault(); // what does this do?
44265         });
44266         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44267         tb.el.hide();
44268         tb.name = nm;
44269         // dont need to disable them... as they will get hidden
44270         return tb;
44271          
44272         
44273     },
44274     buildFooter : function()
44275     {
44276         
44277         var fel = this.editor.wrap.createChild();
44278         this.footer = new Roo.Toolbar(fel);
44279         // toolbar has scrolly on left / right?
44280         var footDisp= new Roo.Toolbar.Fill();
44281         var _t = this;
44282         this.footer.add(
44283             {
44284                 text : '&lt;',
44285                 xtype: 'Button',
44286                 handler : function() {
44287                     _t.footDisp.scrollTo('left',0,true)
44288                 }
44289             }
44290         );
44291         this.footer.add( footDisp );
44292         this.footer.add( 
44293             {
44294                 text : '&gt;',
44295                 xtype: 'Button',
44296                 handler : function() {
44297                     // no animation..
44298                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44299                 }
44300             }
44301         );
44302         var fel = Roo.get(footDisp.el);
44303         fel.addClass('x-editor-context');
44304         this.footDispWrap = fel; 
44305         this.footDispWrap.overflow  = 'hidden';
44306         
44307         this.footDisp = fel.createChild();
44308         this.footDispWrap.on('click', this.onContextClick, this)
44309         
44310         
44311     },
44312     onContextClick : function (ev,dom)
44313     {
44314         ev.preventDefault();
44315         var  cn = dom.className;
44316         //Roo.log(cn);
44317         if (!cn.match(/x-ed-loc-/)) {
44318             return;
44319         }
44320         var n = cn.split('-').pop();
44321         var ans = this.footerEls;
44322         var sel = ans[n];
44323         
44324          // pick
44325         var range = this.editorcore.createRange();
44326         
44327         range.selectNodeContents(sel);
44328         //range.selectNode(sel);
44329         
44330         
44331         var selection = this.editorcore.getSelection();
44332         selection.removeAllRanges();
44333         selection.addRange(range);
44334         
44335         
44336         
44337         this.updateToolbar(null, null, sel);
44338         
44339         
44340     }
44341     
44342     
44343     
44344     
44345     
44346 });
44347
44348
44349
44350
44351
44352 /*
44353  * Based on:
44354  * Ext JS Library 1.1.1
44355  * Copyright(c) 2006-2007, Ext JS, LLC.
44356  *
44357  * Originally Released Under LGPL - original licence link has changed is not relivant.
44358  *
44359  * Fork - LGPL
44360  * <script type="text/javascript">
44361  */
44362  
44363 /**
44364  * @class Roo.form.BasicForm
44365  * @extends Roo.util.Observable
44366  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44367  * @constructor
44368  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44369  * @param {Object} config Configuration options
44370  */
44371 Roo.form.BasicForm = function(el, config){
44372     this.allItems = [];
44373     this.childForms = [];
44374     Roo.apply(this, config);
44375     /*
44376      * The Roo.form.Field items in this form.
44377      * @type MixedCollection
44378      */
44379      
44380      
44381     this.items = new Roo.util.MixedCollection(false, function(o){
44382         return o.id || (o.id = Roo.id());
44383     });
44384     this.addEvents({
44385         /**
44386          * @event beforeaction
44387          * Fires before any action is performed. Return false to cancel the action.
44388          * @param {Form} this
44389          * @param {Action} action The action to be performed
44390          */
44391         beforeaction: true,
44392         /**
44393          * @event actionfailed
44394          * Fires when an action fails.
44395          * @param {Form} this
44396          * @param {Action} action The action that failed
44397          */
44398         actionfailed : true,
44399         /**
44400          * @event actioncomplete
44401          * Fires when an action is completed.
44402          * @param {Form} this
44403          * @param {Action} action The action that completed
44404          */
44405         actioncomplete : true
44406     });
44407     if(el){
44408         this.initEl(el);
44409     }
44410     Roo.form.BasicForm.superclass.constructor.call(this);
44411 };
44412
44413 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44414     /**
44415      * @cfg {String} method
44416      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44417      */
44418     /**
44419      * @cfg {DataReader} reader
44420      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44421      * This is optional as there is built-in support for processing JSON.
44422      */
44423     /**
44424      * @cfg {DataReader} errorReader
44425      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44426      * This is completely optional as there is built-in support for processing JSON.
44427      */
44428     /**
44429      * @cfg {String} url
44430      * The URL to use for form actions if one isn't supplied in the action options.
44431      */
44432     /**
44433      * @cfg {Boolean} fileUpload
44434      * Set to true if this form is a file upload.
44435      */
44436      
44437     /**
44438      * @cfg {Object} baseParams
44439      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44440      */
44441      /**
44442      
44443     /**
44444      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44445      */
44446     timeout: 30,
44447
44448     // private
44449     activeAction : null,
44450
44451     /**
44452      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44453      * or setValues() data instead of when the form was first created.
44454      */
44455     trackResetOnLoad : false,
44456     
44457     
44458     /**
44459      * childForms - used for multi-tab forms
44460      * @type {Array}
44461      */
44462     childForms : false,
44463     
44464     /**
44465      * allItems - full list of fields.
44466      * @type {Array}
44467      */
44468     allItems : false,
44469     
44470     /**
44471      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44472      * element by passing it or its id or mask the form itself by passing in true.
44473      * @type Mixed
44474      */
44475     waitMsgTarget : false,
44476
44477     // private
44478     initEl : function(el){
44479         this.el = Roo.get(el);
44480         this.id = this.el.id || Roo.id();
44481         this.el.on('submit', this.onSubmit, this);
44482         this.el.addClass('x-form');
44483     },
44484
44485     // private
44486     onSubmit : function(e){
44487         e.stopEvent();
44488     },
44489
44490     /**
44491      * Returns true if client-side validation on the form is successful.
44492      * @return Boolean
44493      */
44494     isValid : function(){
44495         var valid = true;
44496         this.items.each(function(f){
44497            if(!f.validate()){
44498                valid = false;
44499            }
44500         });
44501         return valid;
44502     },
44503
44504     /**
44505      * Returns true if any fields in this form have changed since their original load.
44506      * @return Boolean
44507      */
44508     isDirty : function(){
44509         var dirty = false;
44510         this.items.each(function(f){
44511            if(f.isDirty()){
44512                dirty = true;
44513                return false;
44514            }
44515         });
44516         return dirty;
44517     },
44518
44519     /**
44520      * Performs a predefined action (submit or load) or custom actions you define on this form.
44521      * @param {String} actionName The name of the action type
44522      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44523      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44524      * accept other config options):
44525      * <pre>
44526 Property          Type             Description
44527 ----------------  ---------------  ----------------------------------------------------------------------------------
44528 url               String           The url for the action (defaults to the form's url)
44529 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44530 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44531 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44532                                    validate the form on the client (defaults to false)
44533      * </pre>
44534      * @return {BasicForm} this
44535      */
44536     doAction : function(action, options){
44537         if(typeof action == 'string'){
44538             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44539         }
44540         if(this.fireEvent('beforeaction', this, action) !== false){
44541             this.beforeAction(action);
44542             action.run.defer(100, action);
44543         }
44544         return this;
44545     },
44546
44547     /**
44548      * Shortcut to do a submit action.
44549      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44550      * @return {BasicForm} this
44551      */
44552     submit : function(options){
44553         this.doAction('submit', options);
44554         return this;
44555     },
44556
44557     /**
44558      * Shortcut to do a load action.
44559      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44560      * @return {BasicForm} this
44561      */
44562     load : function(options){
44563         this.doAction('load', options);
44564         return this;
44565     },
44566
44567     /**
44568      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44569      * @param {Record} record The record to edit
44570      * @return {BasicForm} this
44571      */
44572     updateRecord : function(record){
44573         record.beginEdit();
44574         var fs = record.fields;
44575         fs.each(function(f){
44576             var field = this.findField(f.name);
44577             if(field){
44578                 record.set(f.name, field.getValue());
44579             }
44580         }, this);
44581         record.endEdit();
44582         return this;
44583     },
44584
44585     /**
44586      * Loads an Roo.data.Record into this form.
44587      * @param {Record} record The record to load
44588      * @return {BasicForm} this
44589      */
44590     loadRecord : function(record){
44591         this.setValues(record.data);
44592         return this;
44593     },
44594
44595     // private
44596     beforeAction : function(action){
44597         var o = action.options;
44598         
44599        
44600         if(this.waitMsgTarget === true){
44601             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44602         }else if(this.waitMsgTarget){
44603             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44604             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44605         }else {
44606             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44607         }
44608          
44609     },
44610
44611     // private
44612     afterAction : function(action, success){
44613         this.activeAction = null;
44614         var o = action.options;
44615         
44616         if(this.waitMsgTarget === true){
44617             this.el.unmask();
44618         }else if(this.waitMsgTarget){
44619             this.waitMsgTarget.unmask();
44620         }else{
44621             Roo.MessageBox.updateProgress(1);
44622             Roo.MessageBox.hide();
44623         }
44624          
44625         if(success){
44626             if(o.reset){
44627                 this.reset();
44628             }
44629             Roo.callback(o.success, o.scope, [this, action]);
44630             this.fireEvent('actioncomplete', this, action);
44631             
44632         }else{
44633             
44634             // failure condition..
44635             // we have a scenario where updates need confirming.
44636             // eg. if a locking scenario exists..
44637             // we look for { errors : { needs_confirm : true }} in the response.
44638             if (
44639                 (typeof(action.result) != 'undefined')  &&
44640                 (typeof(action.result.errors) != 'undefined')  &&
44641                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44642            ){
44643                 var _t = this;
44644                 Roo.MessageBox.confirm(
44645                     "Change requires confirmation",
44646                     action.result.errorMsg,
44647                     function(r) {
44648                         if (r != 'yes') {
44649                             return;
44650                         }
44651                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44652                     }
44653                     
44654                 );
44655                 
44656                 
44657                 
44658                 return;
44659             }
44660             
44661             Roo.callback(o.failure, o.scope, [this, action]);
44662             // show an error message if no failed handler is set..
44663             if (!this.hasListener('actionfailed')) {
44664                 Roo.MessageBox.alert("Error",
44665                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44666                         action.result.errorMsg :
44667                         "Saving Failed, please check your entries or try again"
44668                 );
44669             }
44670             
44671             this.fireEvent('actionfailed', this, action);
44672         }
44673         
44674     },
44675
44676     /**
44677      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44678      * @param {String} id The value to search for
44679      * @return Field
44680      */
44681     findField : function(id){
44682         var field = this.items.get(id);
44683         if(!field){
44684             this.items.each(function(f){
44685                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44686                     field = f;
44687                     return false;
44688                 }
44689             });
44690         }
44691         return field || null;
44692     },
44693
44694     /**
44695      * Add a secondary form to this one, 
44696      * Used to provide tabbed forms. One form is primary, with hidden values 
44697      * which mirror the elements from the other forms.
44698      * 
44699      * @param {Roo.form.Form} form to add.
44700      * 
44701      */
44702     addForm : function(form)
44703     {
44704        
44705         if (this.childForms.indexOf(form) > -1) {
44706             // already added..
44707             return;
44708         }
44709         this.childForms.push(form);
44710         var n = '';
44711         Roo.each(form.allItems, function (fe) {
44712             
44713             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44714             if (this.findField(n)) { // already added..
44715                 return;
44716             }
44717             var add = new Roo.form.Hidden({
44718                 name : n
44719             });
44720             add.render(this.el);
44721             
44722             this.add( add );
44723         }, this);
44724         
44725     },
44726     /**
44727      * Mark fields in this form invalid in bulk.
44728      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44729      * @return {BasicForm} this
44730      */
44731     markInvalid : function(errors){
44732         if(errors instanceof Array){
44733             for(var i = 0, len = errors.length; i < len; i++){
44734                 var fieldError = errors[i];
44735                 var f = this.findField(fieldError.id);
44736                 if(f){
44737                     f.markInvalid(fieldError.msg);
44738                 }
44739             }
44740         }else{
44741             var field, id;
44742             for(id in errors){
44743                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44744                     field.markInvalid(errors[id]);
44745                 }
44746             }
44747         }
44748         Roo.each(this.childForms || [], function (f) {
44749             f.markInvalid(errors);
44750         });
44751         
44752         return this;
44753     },
44754
44755     /**
44756      * Set values for fields in this form in bulk.
44757      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44758      * @return {BasicForm} this
44759      */
44760     setValues : function(values){
44761         if(values instanceof Array){ // array of objects
44762             for(var i = 0, len = values.length; i < len; i++){
44763                 var v = values[i];
44764                 var f = this.findField(v.id);
44765                 if(f){
44766                     f.setValue(v.value);
44767                     if(this.trackResetOnLoad){
44768                         f.originalValue = f.getValue();
44769                     }
44770                 }
44771             }
44772         }else{ // object hash
44773             var field, id;
44774             for(id in values){
44775                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44776                     
44777                     if (field.setFromData && 
44778                         field.valueField && 
44779                         field.displayField &&
44780                         // combos' with local stores can 
44781                         // be queried via setValue()
44782                         // to set their value..
44783                         (field.store && !field.store.isLocal)
44784                         ) {
44785                         // it's a combo
44786                         var sd = { };
44787                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44788                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44789                         field.setFromData(sd);
44790                         
44791                     } else {
44792                         field.setValue(values[id]);
44793                     }
44794                     
44795                     
44796                     if(this.trackResetOnLoad){
44797                         field.originalValue = field.getValue();
44798                     }
44799                 }
44800             }
44801         }
44802          
44803         Roo.each(this.childForms || [], function (f) {
44804             f.setValues(values);
44805         });
44806                 
44807         return this;
44808     },
44809
44810     /**
44811      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44812      * they are returned as an array.
44813      * @param {Boolean} asString
44814      * @return {Object}
44815      */
44816     getValues : function(asString){
44817         if (this.childForms) {
44818             // copy values from the child forms
44819             Roo.each(this.childForms, function (f) {
44820                 this.setValues(f.getValues());
44821             }, this);
44822         }
44823         
44824         
44825         
44826         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44827         if(asString === true){
44828             return fs;
44829         }
44830         return Roo.urlDecode(fs);
44831     },
44832     
44833     /**
44834      * Returns the fields in this form as an object with key/value pairs. 
44835      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44836      * @return {Object}
44837      */
44838     getFieldValues : function(with_hidden)
44839     {
44840         if (this.childForms) {
44841             // copy values from the child forms
44842             // should this call getFieldValues - probably not as we do not currently copy
44843             // hidden fields when we generate..
44844             Roo.each(this.childForms, function (f) {
44845                 this.setValues(f.getValues());
44846             }, this);
44847         }
44848         
44849         var ret = {};
44850         this.items.each(function(f){
44851             if (!f.getName()) {
44852                 return;
44853             }
44854             var v = f.getValue();
44855             if (f.inputType =='radio') {
44856                 if (typeof(ret[f.getName()]) == 'undefined') {
44857                     ret[f.getName()] = ''; // empty..
44858                 }
44859                 
44860                 if (!f.el.dom.checked) {
44861                     return;
44862                     
44863                 }
44864                 v = f.el.dom.value;
44865                 
44866             }
44867             
44868             // not sure if this supported any more..
44869             if ((typeof(v) == 'object') && f.getRawValue) {
44870                 v = f.getRawValue() ; // dates..
44871             }
44872             // combo boxes where name != hiddenName...
44873             if (f.name != f.getName()) {
44874                 ret[f.name] = f.getRawValue();
44875             }
44876             ret[f.getName()] = v;
44877         });
44878         
44879         return ret;
44880     },
44881
44882     /**
44883      * Clears all invalid messages in this form.
44884      * @return {BasicForm} this
44885      */
44886     clearInvalid : function(){
44887         this.items.each(function(f){
44888            f.clearInvalid();
44889         });
44890         
44891         Roo.each(this.childForms || [], function (f) {
44892             f.clearInvalid();
44893         });
44894         
44895         
44896         return this;
44897     },
44898
44899     /**
44900      * Resets this form.
44901      * @return {BasicForm} this
44902      */
44903     reset : function(){
44904         this.items.each(function(f){
44905             f.reset();
44906         });
44907         
44908         Roo.each(this.childForms || [], function (f) {
44909             f.reset();
44910         });
44911        
44912         
44913         return this;
44914     },
44915
44916     /**
44917      * Add Roo.form components to this form.
44918      * @param {Field} field1
44919      * @param {Field} field2 (optional)
44920      * @param {Field} etc (optional)
44921      * @return {BasicForm} this
44922      */
44923     add : function(){
44924         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44925         return this;
44926     },
44927
44928
44929     /**
44930      * Removes a field from the items collection (does NOT remove its markup).
44931      * @param {Field} field
44932      * @return {BasicForm} this
44933      */
44934     remove : function(field){
44935         this.items.remove(field);
44936         return this;
44937     },
44938
44939     /**
44940      * Looks at the fields in this form, checks them for an id attribute,
44941      * and calls applyTo on the existing dom element with that id.
44942      * @return {BasicForm} this
44943      */
44944     render : function(){
44945         this.items.each(function(f){
44946             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44947                 f.applyTo(f.id);
44948             }
44949         });
44950         return this;
44951     },
44952
44953     /**
44954      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44955      * @param {Object} values
44956      * @return {BasicForm} this
44957      */
44958     applyToFields : function(o){
44959         this.items.each(function(f){
44960            Roo.apply(f, o);
44961         });
44962         return this;
44963     },
44964
44965     /**
44966      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44967      * @param {Object} values
44968      * @return {BasicForm} this
44969      */
44970     applyIfToFields : function(o){
44971         this.items.each(function(f){
44972            Roo.applyIf(f, o);
44973         });
44974         return this;
44975     }
44976 });
44977
44978 // back compat
44979 Roo.BasicForm = Roo.form.BasicForm;/*
44980  * Based on:
44981  * Ext JS Library 1.1.1
44982  * Copyright(c) 2006-2007, Ext JS, LLC.
44983  *
44984  * Originally Released Under LGPL - original licence link has changed is not relivant.
44985  *
44986  * Fork - LGPL
44987  * <script type="text/javascript">
44988  */
44989
44990 /**
44991  * @class Roo.form.Form
44992  * @extends Roo.form.BasicForm
44993  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44994  * @constructor
44995  * @param {Object} config Configuration options
44996  */
44997 Roo.form.Form = function(config){
44998     var xitems =  [];
44999     if (config.items) {
45000         xitems = config.items;
45001         delete config.items;
45002     }
45003    
45004     
45005     Roo.form.Form.superclass.constructor.call(this, null, config);
45006     this.url = this.url || this.action;
45007     if(!this.root){
45008         this.root = new Roo.form.Layout(Roo.applyIf({
45009             id: Roo.id()
45010         }, config));
45011     }
45012     this.active = this.root;
45013     /**
45014      * Array of all the buttons that have been added to this form via {@link addButton}
45015      * @type Array
45016      */
45017     this.buttons = [];
45018     this.allItems = [];
45019     this.addEvents({
45020         /**
45021          * @event clientvalidation
45022          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45023          * @param {Form} this
45024          * @param {Boolean} valid true if the form has passed client-side validation
45025          */
45026         clientvalidation: true,
45027         /**
45028          * @event rendered
45029          * Fires when the form is rendered
45030          * @param {Roo.form.Form} form
45031          */
45032         rendered : true
45033     });
45034     
45035     if (this.progressUrl) {
45036             // push a hidden field onto the list of fields..
45037             this.addxtype( {
45038                     xns: Roo.form, 
45039                     xtype : 'Hidden', 
45040                     name : 'UPLOAD_IDENTIFIER' 
45041             });
45042         }
45043         
45044     
45045     Roo.each(xitems, this.addxtype, this);
45046     
45047     
45048     
45049 };
45050
45051 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45052     /**
45053      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45054      */
45055     /**
45056      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45057      */
45058     /**
45059      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45060      */
45061     buttonAlign:'center',
45062
45063     /**
45064      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45065      */
45066     minButtonWidth:75,
45067
45068     /**
45069      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45070      * This property cascades to child containers if not set.
45071      */
45072     labelAlign:'left',
45073
45074     /**
45075      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45076      * fires a looping event with that state. This is required to bind buttons to the valid
45077      * state using the config value formBind:true on the button.
45078      */
45079     monitorValid : false,
45080
45081     /**
45082      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45083      */
45084     monitorPoll : 200,
45085     
45086     /**
45087      * @cfg {String} progressUrl - Url to return progress data 
45088      */
45089     
45090     progressUrl : false,
45091   
45092     /**
45093      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45094      * fields are added and the column is closed. If no fields are passed the column remains open
45095      * until end() is called.
45096      * @param {Object} config The config to pass to the column
45097      * @param {Field} field1 (optional)
45098      * @param {Field} field2 (optional)
45099      * @param {Field} etc (optional)
45100      * @return Column The column container object
45101      */
45102     column : function(c){
45103         var col = new Roo.form.Column(c);
45104         this.start(col);
45105         if(arguments.length > 1){ // duplicate code required because of Opera
45106             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45107             this.end();
45108         }
45109         return col;
45110     },
45111
45112     /**
45113      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45114      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45115      * until end() is called.
45116      * @param {Object} config The config to pass to the fieldset
45117      * @param {Field} field1 (optional)
45118      * @param {Field} field2 (optional)
45119      * @param {Field} etc (optional)
45120      * @return FieldSet The fieldset container object
45121      */
45122     fieldset : function(c){
45123         var fs = new Roo.form.FieldSet(c);
45124         this.start(fs);
45125         if(arguments.length > 1){ // duplicate code required because of Opera
45126             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45127             this.end();
45128         }
45129         return fs;
45130     },
45131
45132     /**
45133      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45134      * fields are added and the container is closed. If no fields are passed the container remains open
45135      * until end() is called.
45136      * @param {Object} config The config to pass to the Layout
45137      * @param {Field} field1 (optional)
45138      * @param {Field} field2 (optional)
45139      * @param {Field} etc (optional)
45140      * @return Layout The container object
45141      */
45142     container : function(c){
45143         var l = new Roo.form.Layout(c);
45144         this.start(l);
45145         if(arguments.length > 1){ // duplicate code required because of Opera
45146             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45147             this.end();
45148         }
45149         return l;
45150     },
45151
45152     /**
45153      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45154      * @param {Object} container A Roo.form.Layout or subclass of Layout
45155      * @return {Form} this
45156      */
45157     start : function(c){
45158         // cascade label info
45159         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45160         this.active.stack.push(c);
45161         c.ownerCt = this.active;
45162         this.active = c;
45163         return this;
45164     },
45165
45166     /**
45167      * Closes the current open container
45168      * @return {Form} this
45169      */
45170     end : function(){
45171         if(this.active == this.root){
45172             return this;
45173         }
45174         this.active = this.active.ownerCt;
45175         return this;
45176     },
45177
45178     /**
45179      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45180      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45181      * as the label of the field.
45182      * @param {Field} field1
45183      * @param {Field} field2 (optional)
45184      * @param {Field} etc. (optional)
45185      * @return {Form} this
45186      */
45187     add : function(){
45188         this.active.stack.push.apply(this.active.stack, arguments);
45189         this.allItems.push.apply(this.allItems,arguments);
45190         var r = [];
45191         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45192             if(a[i].isFormField){
45193                 r.push(a[i]);
45194             }
45195         }
45196         if(r.length > 0){
45197             Roo.form.Form.superclass.add.apply(this, r);
45198         }
45199         return this;
45200     },
45201     
45202
45203     
45204     
45205     
45206      /**
45207      * Find any element that has been added to a form, using it's ID or name
45208      * This can include framesets, columns etc. along with regular fields..
45209      * @param {String} id - id or name to find.
45210      
45211      * @return {Element} e - or false if nothing found.
45212      */
45213     findbyId : function(id)
45214     {
45215         var ret = false;
45216         if (!id) {
45217             return ret;
45218         }
45219         Roo.each(this.allItems, function(f){
45220             if (f.id == id || f.name == id ){
45221                 ret = f;
45222                 return false;
45223             }
45224         });
45225         return ret;
45226     },
45227
45228     
45229     
45230     /**
45231      * Render this form into the passed container. This should only be called once!
45232      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45233      * @return {Form} this
45234      */
45235     render : function(ct)
45236     {
45237         
45238         
45239         
45240         ct = Roo.get(ct);
45241         var o = this.autoCreate || {
45242             tag: 'form',
45243             method : this.method || 'POST',
45244             id : this.id || Roo.id()
45245         };
45246         this.initEl(ct.createChild(o));
45247
45248         this.root.render(this.el);
45249         
45250        
45251              
45252         this.items.each(function(f){
45253             f.render('x-form-el-'+f.id);
45254         });
45255
45256         if(this.buttons.length > 0){
45257             // tables are required to maintain order and for correct IE layout
45258             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45259                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45260                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45261             }}, null, true);
45262             var tr = tb.getElementsByTagName('tr')[0];
45263             for(var i = 0, len = this.buttons.length; i < len; i++) {
45264                 var b = this.buttons[i];
45265                 var td = document.createElement('td');
45266                 td.className = 'x-form-btn-td';
45267                 b.render(tr.appendChild(td));
45268             }
45269         }
45270         if(this.monitorValid){ // initialize after render
45271             this.startMonitoring();
45272         }
45273         this.fireEvent('rendered', this);
45274         return this;
45275     },
45276
45277     /**
45278      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45279      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45280      * object or a valid Roo.DomHelper element config
45281      * @param {Function} handler The function called when the button is clicked
45282      * @param {Object} scope (optional) The scope of the handler function
45283      * @return {Roo.Button}
45284      */
45285     addButton : function(config, handler, scope){
45286         var bc = {
45287             handler: handler,
45288             scope: scope,
45289             minWidth: this.minButtonWidth,
45290             hideParent:true
45291         };
45292         if(typeof config == "string"){
45293             bc.text = config;
45294         }else{
45295             Roo.apply(bc, config);
45296         }
45297         var btn = new Roo.Button(null, bc);
45298         this.buttons.push(btn);
45299         return btn;
45300     },
45301
45302      /**
45303      * Adds a series of form elements (using the xtype property as the factory method.
45304      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45305      * @param {Object} config 
45306      */
45307     
45308     addxtype : function()
45309     {
45310         var ar = Array.prototype.slice.call(arguments, 0);
45311         var ret = false;
45312         for(var i = 0; i < ar.length; i++) {
45313             if (!ar[i]) {
45314                 continue; // skip -- if this happends something invalid got sent, we 
45315                 // should ignore it, as basically that interface element will not show up
45316                 // and that should be pretty obvious!!
45317             }
45318             
45319             if (Roo.form[ar[i].xtype]) {
45320                 ar[i].form = this;
45321                 var fe = Roo.factory(ar[i], Roo.form);
45322                 if (!ret) {
45323                     ret = fe;
45324                 }
45325                 fe.form = this;
45326                 if (fe.store) {
45327                     fe.store.form = this;
45328                 }
45329                 if (fe.isLayout) {  
45330                          
45331                     this.start(fe);
45332                     this.allItems.push(fe);
45333                     if (fe.items && fe.addxtype) {
45334                         fe.addxtype.apply(fe, fe.items);
45335                         delete fe.items;
45336                     }
45337                      this.end();
45338                     continue;
45339                 }
45340                 
45341                 
45342                  
45343                 this.add(fe);
45344               //  console.log('adding ' + ar[i].xtype);
45345             }
45346             if (ar[i].xtype == 'Button') {  
45347                 //console.log('adding button');
45348                 //console.log(ar[i]);
45349                 this.addButton(ar[i]);
45350                 this.allItems.push(fe);
45351                 continue;
45352             }
45353             
45354             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45355                 alert('end is not supported on xtype any more, use items');
45356             //    this.end();
45357             //    //console.log('adding end');
45358             }
45359             
45360         }
45361         return ret;
45362     },
45363     
45364     /**
45365      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45366      * option "monitorValid"
45367      */
45368     startMonitoring : function(){
45369         if(!this.bound){
45370             this.bound = true;
45371             Roo.TaskMgr.start({
45372                 run : this.bindHandler,
45373                 interval : this.monitorPoll || 200,
45374                 scope: this
45375             });
45376         }
45377     },
45378
45379     /**
45380      * Stops monitoring of the valid state of this form
45381      */
45382     stopMonitoring : function(){
45383         this.bound = false;
45384     },
45385
45386     // private
45387     bindHandler : function(){
45388         if(!this.bound){
45389             return false; // stops binding
45390         }
45391         var valid = true;
45392         this.items.each(function(f){
45393             if(!f.isValid(true)){
45394                 valid = false;
45395                 return false;
45396             }
45397         });
45398         for(var i = 0, len = this.buttons.length; i < len; i++){
45399             var btn = this.buttons[i];
45400             if(btn.formBind === true && btn.disabled === valid){
45401                 btn.setDisabled(!valid);
45402             }
45403         }
45404         this.fireEvent('clientvalidation', this, valid);
45405     }
45406     
45407     
45408     
45409     
45410     
45411     
45412     
45413     
45414 });
45415
45416
45417 // back compat
45418 Roo.Form = Roo.form.Form;
45419 /*
45420  * Based on:
45421  * Ext JS Library 1.1.1
45422  * Copyright(c) 2006-2007, Ext JS, LLC.
45423  *
45424  * Originally Released Under LGPL - original licence link has changed is not relivant.
45425  *
45426  * Fork - LGPL
45427  * <script type="text/javascript">
45428  */
45429
45430 // as we use this in bootstrap.
45431 Roo.namespace('Roo.form');
45432  /**
45433  * @class Roo.form.Action
45434  * Internal Class used to handle form actions
45435  * @constructor
45436  * @param {Roo.form.BasicForm} el The form element or its id
45437  * @param {Object} config Configuration options
45438  */
45439
45440  
45441  
45442 // define the action interface
45443 Roo.form.Action = function(form, options){
45444     this.form = form;
45445     this.options = options || {};
45446 };
45447 /**
45448  * Client Validation Failed
45449  * @const 
45450  */
45451 Roo.form.Action.CLIENT_INVALID = 'client';
45452 /**
45453  * Server Validation Failed
45454  * @const 
45455  */
45456 Roo.form.Action.SERVER_INVALID = 'server';
45457  /**
45458  * Connect to Server Failed
45459  * @const 
45460  */
45461 Roo.form.Action.CONNECT_FAILURE = 'connect';
45462 /**
45463  * Reading Data from Server Failed
45464  * @const 
45465  */
45466 Roo.form.Action.LOAD_FAILURE = 'load';
45467
45468 Roo.form.Action.prototype = {
45469     type : 'default',
45470     failureType : undefined,
45471     response : undefined,
45472     result : undefined,
45473
45474     // interface method
45475     run : function(options){
45476
45477     },
45478
45479     // interface method
45480     success : function(response){
45481
45482     },
45483
45484     // interface method
45485     handleResponse : function(response){
45486
45487     },
45488
45489     // default connection failure
45490     failure : function(response){
45491         
45492         this.response = response;
45493         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45494         this.form.afterAction(this, false);
45495     },
45496
45497     processResponse : function(response){
45498         this.response = response;
45499         if(!response.responseText){
45500             return true;
45501         }
45502         this.result = this.handleResponse(response);
45503         return this.result;
45504     },
45505
45506     // utility functions used internally
45507     getUrl : function(appendParams){
45508         var url = this.options.url || this.form.url || this.form.el.dom.action;
45509         if(appendParams){
45510             var p = this.getParams();
45511             if(p){
45512                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45513             }
45514         }
45515         return url;
45516     },
45517
45518     getMethod : function(){
45519         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45520     },
45521
45522     getParams : function(){
45523         var bp = this.form.baseParams;
45524         var p = this.options.params;
45525         if(p){
45526             if(typeof p == "object"){
45527                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45528             }else if(typeof p == 'string' && bp){
45529                 p += '&' + Roo.urlEncode(bp);
45530             }
45531         }else if(bp){
45532             p = Roo.urlEncode(bp);
45533         }
45534         return p;
45535     },
45536
45537     createCallback : function(){
45538         return {
45539             success: this.success,
45540             failure: this.failure,
45541             scope: this,
45542             timeout: (this.form.timeout*1000),
45543             upload: this.form.fileUpload ? this.success : undefined
45544         };
45545     }
45546 };
45547
45548 Roo.form.Action.Submit = function(form, options){
45549     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45550 };
45551
45552 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45553     type : 'submit',
45554
45555     haveProgress : false,
45556     uploadComplete : false,
45557     
45558     // uploadProgress indicator.
45559     uploadProgress : function()
45560     {
45561         if (!this.form.progressUrl) {
45562             return;
45563         }
45564         
45565         if (!this.haveProgress) {
45566             Roo.MessageBox.progress("Uploading", "Uploading");
45567         }
45568         if (this.uploadComplete) {
45569            Roo.MessageBox.hide();
45570            return;
45571         }
45572         
45573         this.haveProgress = true;
45574    
45575         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45576         
45577         var c = new Roo.data.Connection();
45578         c.request({
45579             url : this.form.progressUrl,
45580             params: {
45581                 id : uid
45582             },
45583             method: 'GET',
45584             success : function(req){
45585                //console.log(data);
45586                 var rdata = false;
45587                 var edata;
45588                 try  {
45589                    rdata = Roo.decode(req.responseText)
45590                 } catch (e) {
45591                     Roo.log("Invalid data from server..");
45592                     Roo.log(edata);
45593                     return;
45594                 }
45595                 if (!rdata || !rdata.success) {
45596                     Roo.log(rdata);
45597                     Roo.MessageBox.alert(Roo.encode(rdata));
45598                     return;
45599                 }
45600                 var data = rdata.data;
45601                 
45602                 if (this.uploadComplete) {
45603                    Roo.MessageBox.hide();
45604                    return;
45605                 }
45606                    
45607                 if (data){
45608                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45609                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45610                     );
45611                 }
45612                 this.uploadProgress.defer(2000,this);
45613             },
45614        
45615             failure: function(data) {
45616                 Roo.log('progress url failed ');
45617                 Roo.log(data);
45618             },
45619             scope : this
45620         });
45621            
45622     },
45623     
45624     
45625     run : function()
45626     {
45627         // run get Values on the form, so it syncs any secondary forms.
45628         this.form.getValues();
45629         
45630         var o = this.options;
45631         var method = this.getMethod();
45632         var isPost = method == 'POST';
45633         if(o.clientValidation === false || this.form.isValid()){
45634             
45635             if (this.form.progressUrl) {
45636                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45637                     (new Date() * 1) + '' + Math.random());
45638                     
45639             } 
45640             
45641             
45642             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45643                 form:this.form.el.dom,
45644                 url:this.getUrl(!isPost),
45645                 method: method,
45646                 params:isPost ? this.getParams() : null,
45647                 isUpload: this.form.fileUpload
45648             }));
45649             
45650             this.uploadProgress();
45651
45652         }else if (o.clientValidation !== false){ // client validation failed
45653             this.failureType = Roo.form.Action.CLIENT_INVALID;
45654             this.form.afterAction(this, false);
45655         }
45656     },
45657
45658     success : function(response)
45659     {
45660         this.uploadComplete= true;
45661         if (this.haveProgress) {
45662             Roo.MessageBox.hide();
45663         }
45664         
45665         
45666         var result = this.processResponse(response);
45667         if(result === true || result.success){
45668             this.form.afterAction(this, true);
45669             return;
45670         }
45671         if(result.errors){
45672             this.form.markInvalid(result.errors);
45673             this.failureType = Roo.form.Action.SERVER_INVALID;
45674         }
45675         this.form.afterAction(this, false);
45676     },
45677     failure : function(response)
45678     {
45679         this.uploadComplete= true;
45680         if (this.haveProgress) {
45681             Roo.MessageBox.hide();
45682         }
45683         
45684         this.response = response;
45685         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45686         this.form.afterAction(this, false);
45687     },
45688     
45689     handleResponse : function(response){
45690         if(this.form.errorReader){
45691             var rs = this.form.errorReader.read(response);
45692             var errors = [];
45693             if(rs.records){
45694                 for(var i = 0, len = rs.records.length; i < len; i++) {
45695                     var r = rs.records[i];
45696                     errors[i] = r.data;
45697                 }
45698             }
45699             if(errors.length < 1){
45700                 errors = null;
45701             }
45702             return {
45703                 success : rs.success,
45704                 errors : errors
45705             };
45706         }
45707         var ret = false;
45708         try {
45709             ret = Roo.decode(response.responseText);
45710         } catch (e) {
45711             ret = {
45712                 success: false,
45713                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45714                 errors : []
45715             };
45716         }
45717         return ret;
45718         
45719     }
45720 });
45721
45722
45723 Roo.form.Action.Load = function(form, options){
45724     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45725     this.reader = this.form.reader;
45726 };
45727
45728 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45729     type : 'load',
45730
45731     run : function(){
45732         
45733         Roo.Ajax.request(Roo.apply(
45734                 this.createCallback(), {
45735                     method:this.getMethod(),
45736                     url:this.getUrl(false),
45737                     params:this.getParams()
45738         }));
45739     },
45740
45741     success : function(response){
45742         
45743         var result = this.processResponse(response);
45744         if(result === true || !result.success || !result.data){
45745             this.failureType = Roo.form.Action.LOAD_FAILURE;
45746             this.form.afterAction(this, false);
45747             return;
45748         }
45749         this.form.clearInvalid();
45750         this.form.setValues(result.data);
45751         this.form.afterAction(this, true);
45752     },
45753
45754     handleResponse : function(response){
45755         if(this.form.reader){
45756             var rs = this.form.reader.read(response);
45757             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45758             return {
45759                 success : rs.success,
45760                 data : data
45761             };
45762         }
45763         return Roo.decode(response.responseText);
45764     }
45765 });
45766
45767 Roo.form.Action.ACTION_TYPES = {
45768     'load' : Roo.form.Action.Load,
45769     'submit' : Roo.form.Action.Submit
45770 };/*
45771  * Based on:
45772  * Ext JS Library 1.1.1
45773  * Copyright(c) 2006-2007, Ext JS, LLC.
45774  *
45775  * Originally Released Under LGPL - original licence link has changed is not relivant.
45776  *
45777  * Fork - LGPL
45778  * <script type="text/javascript">
45779  */
45780  
45781 /**
45782  * @class Roo.form.Layout
45783  * @extends Roo.Component
45784  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45785  * @constructor
45786  * @param {Object} config Configuration options
45787  */
45788 Roo.form.Layout = function(config){
45789     var xitems = [];
45790     if (config.items) {
45791         xitems = config.items;
45792         delete config.items;
45793     }
45794     Roo.form.Layout.superclass.constructor.call(this, config);
45795     this.stack = [];
45796     Roo.each(xitems, this.addxtype, this);
45797      
45798 };
45799
45800 Roo.extend(Roo.form.Layout, Roo.Component, {
45801     /**
45802      * @cfg {String/Object} autoCreate
45803      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45804      */
45805     /**
45806      * @cfg {String/Object/Function} style
45807      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45808      * a function which returns such a specification.
45809      */
45810     /**
45811      * @cfg {String} labelAlign
45812      * Valid values are "left," "top" and "right" (defaults to "left")
45813      */
45814     /**
45815      * @cfg {Number} labelWidth
45816      * Fixed width in pixels of all field labels (defaults to undefined)
45817      */
45818     /**
45819      * @cfg {Boolean} clear
45820      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45821      */
45822     clear : true,
45823     /**
45824      * @cfg {String} labelSeparator
45825      * The separator to use after field labels (defaults to ':')
45826      */
45827     labelSeparator : ':',
45828     /**
45829      * @cfg {Boolean} hideLabels
45830      * True to suppress the display of field labels in this layout (defaults to false)
45831      */
45832     hideLabels : false,
45833
45834     // private
45835     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45836     
45837     isLayout : true,
45838     
45839     // private
45840     onRender : function(ct, position){
45841         if(this.el){ // from markup
45842             this.el = Roo.get(this.el);
45843         }else {  // generate
45844             var cfg = this.getAutoCreate();
45845             this.el = ct.createChild(cfg, position);
45846         }
45847         if(this.style){
45848             this.el.applyStyles(this.style);
45849         }
45850         if(this.labelAlign){
45851             this.el.addClass('x-form-label-'+this.labelAlign);
45852         }
45853         if(this.hideLabels){
45854             this.labelStyle = "display:none";
45855             this.elementStyle = "padding-left:0;";
45856         }else{
45857             if(typeof this.labelWidth == 'number'){
45858                 this.labelStyle = "width:"+this.labelWidth+"px;";
45859                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45860             }
45861             if(this.labelAlign == 'top'){
45862                 this.labelStyle = "width:auto;";
45863                 this.elementStyle = "padding-left:0;";
45864             }
45865         }
45866         var stack = this.stack;
45867         var slen = stack.length;
45868         if(slen > 0){
45869             if(!this.fieldTpl){
45870                 var t = new Roo.Template(
45871                     '<div class="x-form-item {5}">',
45872                         '<label for="{0}" style="{2}">{1}{4}</label>',
45873                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45874                         '</div>',
45875                     '</div><div class="x-form-clear-left"></div>'
45876                 );
45877                 t.disableFormats = true;
45878                 t.compile();
45879                 Roo.form.Layout.prototype.fieldTpl = t;
45880             }
45881             for(var i = 0; i < slen; i++) {
45882                 if(stack[i].isFormField){
45883                     this.renderField(stack[i]);
45884                 }else{
45885                     this.renderComponent(stack[i]);
45886                 }
45887             }
45888         }
45889         if(this.clear){
45890             this.el.createChild({cls:'x-form-clear'});
45891         }
45892     },
45893
45894     // private
45895     renderField : function(f){
45896         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45897                f.id, //0
45898                f.fieldLabel, //1
45899                f.labelStyle||this.labelStyle||'', //2
45900                this.elementStyle||'', //3
45901                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45902                f.itemCls||this.itemCls||''  //5
45903        ], true).getPrevSibling());
45904     },
45905
45906     // private
45907     renderComponent : function(c){
45908         c.render(c.isLayout ? this.el : this.el.createChild());    
45909     },
45910     /**
45911      * Adds a object form elements (using the xtype property as the factory method.)
45912      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45913      * @param {Object} config 
45914      */
45915     addxtype : function(o)
45916     {
45917         // create the lement.
45918         o.form = this.form;
45919         var fe = Roo.factory(o, Roo.form);
45920         this.form.allItems.push(fe);
45921         this.stack.push(fe);
45922         
45923         if (fe.isFormField) {
45924             this.form.items.add(fe);
45925         }
45926          
45927         return fe;
45928     }
45929 });
45930
45931 /**
45932  * @class Roo.form.Column
45933  * @extends Roo.form.Layout
45934  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45935  * @constructor
45936  * @param {Object} config Configuration options
45937  */
45938 Roo.form.Column = function(config){
45939     Roo.form.Column.superclass.constructor.call(this, config);
45940 };
45941
45942 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45943     /**
45944      * @cfg {Number/String} width
45945      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45946      */
45947     /**
45948      * @cfg {String/Object} autoCreate
45949      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45950      */
45951
45952     // private
45953     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45954
45955     // private
45956     onRender : function(ct, position){
45957         Roo.form.Column.superclass.onRender.call(this, ct, position);
45958         if(this.width){
45959             this.el.setWidth(this.width);
45960         }
45961     }
45962 });
45963
45964
45965 /**
45966  * @class Roo.form.Row
45967  * @extends Roo.form.Layout
45968  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45969  * @constructor
45970  * @param {Object} config Configuration options
45971  */
45972
45973  
45974 Roo.form.Row = function(config){
45975     Roo.form.Row.superclass.constructor.call(this, config);
45976 };
45977  
45978 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45979       /**
45980      * @cfg {Number/String} width
45981      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45982      */
45983     /**
45984      * @cfg {Number/String} height
45985      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45986      */
45987     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45988     
45989     padWidth : 20,
45990     // private
45991     onRender : function(ct, position){
45992         //console.log('row render');
45993         if(!this.rowTpl){
45994             var t = new Roo.Template(
45995                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45996                     '<label for="{0}" style="{2}">{1}{4}</label>',
45997                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45998                     '</div>',
45999                 '</div>'
46000             );
46001             t.disableFormats = true;
46002             t.compile();
46003             Roo.form.Layout.prototype.rowTpl = t;
46004         }
46005         this.fieldTpl = this.rowTpl;
46006         
46007         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46008         var labelWidth = 100;
46009         
46010         if ((this.labelAlign != 'top')) {
46011             if (typeof this.labelWidth == 'number') {
46012                 labelWidth = this.labelWidth
46013             }
46014             this.padWidth =  20 + labelWidth;
46015             
46016         }
46017         
46018         Roo.form.Column.superclass.onRender.call(this, ct, position);
46019         if(this.width){
46020             this.el.setWidth(this.width);
46021         }
46022         if(this.height){
46023             this.el.setHeight(this.height);
46024         }
46025     },
46026     
46027     // private
46028     renderField : function(f){
46029         f.fieldEl = this.fieldTpl.append(this.el, [
46030                f.id, f.fieldLabel,
46031                f.labelStyle||this.labelStyle||'',
46032                this.elementStyle||'',
46033                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46034                f.itemCls||this.itemCls||'',
46035                f.width ? f.width + this.padWidth : 160 + this.padWidth
46036        ],true);
46037     }
46038 });
46039  
46040
46041 /**
46042  * @class Roo.form.FieldSet
46043  * @extends Roo.form.Layout
46044  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46045  * @constructor
46046  * @param {Object} config Configuration options
46047  */
46048 Roo.form.FieldSet = function(config){
46049     Roo.form.FieldSet.superclass.constructor.call(this, config);
46050 };
46051
46052 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46053     /**
46054      * @cfg {String} legend
46055      * The text to display as the legend for the FieldSet (defaults to '')
46056      */
46057     /**
46058      * @cfg {String/Object} autoCreate
46059      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46060      */
46061
46062     // private
46063     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46064
46065     // private
46066     onRender : function(ct, position){
46067         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46068         if(this.legend){
46069             this.setLegend(this.legend);
46070         }
46071     },
46072
46073     // private
46074     setLegend : function(text){
46075         if(this.rendered){
46076             this.el.child('legend').update(text);
46077         }
46078     }
46079 });/*
46080  * Based on:
46081  * Ext JS Library 1.1.1
46082  * Copyright(c) 2006-2007, Ext JS, LLC.
46083  *
46084  * Originally Released Under LGPL - original licence link has changed is not relivant.
46085  *
46086  * Fork - LGPL
46087  * <script type="text/javascript">
46088  */
46089 /**
46090  * @class Roo.form.VTypes
46091  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46092  * @singleton
46093  */
46094 Roo.form.VTypes = function(){
46095     // closure these in so they are only created once.
46096     var alpha = /^[a-zA-Z_]+$/;
46097     var alphanum = /^[a-zA-Z0-9_]+$/;
46098     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46099     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46100
46101     // All these messages and functions are configurable
46102     return {
46103         /**
46104          * The function used to validate email addresses
46105          * @param {String} value The email address
46106          */
46107         'email' : function(v){
46108             return email.test(v);
46109         },
46110         /**
46111          * The error text to display when the email validation function returns false
46112          * @type String
46113          */
46114         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46115         /**
46116          * The keystroke filter mask to be applied on email input
46117          * @type RegExp
46118          */
46119         'emailMask' : /[a-z0-9_\.\-@]/i,
46120
46121         /**
46122          * The function used to validate URLs
46123          * @param {String} value The URL
46124          */
46125         'url' : function(v){
46126             return url.test(v);
46127         },
46128         /**
46129          * The error text to display when the url validation function returns false
46130          * @type String
46131          */
46132         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46133         
46134         /**
46135          * The function used to validate alpha values
46136          * @param {String} value The value
46137          */
46138         'alpha' : function(v){
46139             return alpha.test(v);
46140         },
46141         /**
46142          * The error text to display when the alpha validation function returns false
46143          * @type String
46144          */
46145         'alphaText' : 'This field should only contain letters and _',
46146         /**
46147          * The keystroke filter mask to be applied on alpha input
46148          * @type RegExp
46149          */
46150         'alphaMask' : /[a-z_]/i,
46151
46152         /**
46153          * The function used to validate alphanumeric values
46154          * @param {String} value The value
46155          */
46156         'alphanum' : function(v){
46157             return alphanum.test(v);
46158         },
46159         /**
46160          * The error text to display when the alphanumeric validation function returns false
46161          * @type String
46162          */
46163         'alphanumText' : 'This field should only contain letters, numbers and _',
46164         /**
46165          * The keystroke filter mask to be applied on alphanumeric input
46166          * @type RegExp
46167          */
46168         'alphanumMask' : /[a-z0-9_]/i
46169     };
46170 }();//<script type="text/javascript">
46171
46172 /**
46173  * @class Roo.form.FCKeditor
46174  * @extends Roo.form.TextArea
46175  * Wrapper around the FCKEditor http://www.fckeditor.net
46176  * @constructor
46177  * Creates a new FCKeditor
46178  * @param {Object} config Configuration options
46179  */
46180 Roo.form.FCKeditor = function(config){
46181     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46182     this.addEvents({
46183          /**
46184          * @event editorinit
46185          * Fired when the editor is initialized - you can add extra handlers here..
46186          * @param {FCKeditor} this
46187          * @param {Object} the FCK object.
46188          */
46189         editorinit : true
46190     });
46191     
46192     
46193 };
46194 Roo.form.FCKeditor.editors = { };
46195 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46196 {
46197     //defaultAutoCreate : {
46198     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46199     //},
46200     // private
46201     /**
46202      * @cfg {Object} fck options - see fck manual for details.
46203      */
46204     fckconfig : false,
46205     
46206     /**
46207      * @cfg {Object} fck toolbar set (Basic or Default)
46208      */
46209     toolbarSet : 'Basic',
46210     /**
46211      * @cfg {Object} fck BasePath
46212      */ 
46213     basePath : '/fckeditor/',
46214     
46215     
46216     frame : false,
46217     
46218     value : '',
46219     
46220    
46221     onRender : function(ct, position)
46222     {
46223         if(!this.el){
46224             this.defaultAutoCreate = {
46225                 tag: "textarea",
46226                 style:"width:300px;height:60px;",
46227                 autocomplete: "off"
46228             };
46229         }
46230         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46231         /*
46232         if(this.grow){
46233             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46234             if(this.preventScrollbars){
46235                 this.el.setStyle("overflow", "hidden");
46236             }
46237             this.el.setHeight(this.growMin);
46238         }
46239         */
46240         //console.log('onrender' + this.getId() );
46241         Roo.form.FCKeditor.editors[this.getId()] = this;
46242          
46243
46244         this.replaceTextarea() ;
46245         
46246     },
46247     
46248     getEditor : function() {
46249         return this.fckEditor;
46250     },
46251     /**
46252      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46253      * @param {Mixed} value The value to set
46254      */
46255     
46256     
46257     setValue : function(value)
46258     {
46259         //console.log('setValue: ' + value);
46260         
46261         if(typeof(value) == 'undefined') { // not sure why this is happending...
46262             return;
46263         }
46264         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46265         
46266         //if(!this.el || !this.getEditor()) {
46267         //    this.value = value;
46268             //this.setValue.defer(100,this,[value]);    
46269         //    return;
46270         //} 
46271         
46272         if(!this.getEditor()) {
46273             return;
46274         }
46275         
46276         this.getEditor().SetData(value);
46277         
46278         //
46279
46280     },
46281
46282     /**
46283      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46284      * @return {Mixed} value The field value
46285      */
46286     getValue : function()
46287     {
46288         
46289         if (this.frame && this.frame.dom.style.display == 'none') {
46290             return Roo.form.FCKeditor.superclass.getValue.call(this);
46291         }
46292         
46293         if(!this.el || !this.getEditor()) {
46294            
46295            // this.getValue.defer(100,this); 
46296             return this.value;
46297         }
46298        
46299         
46300         var value=this.getEditor().GetData();
46301         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46302         return Roo.form.FCKeditor.superclass.getValue.call(this);
46303         
46304
46305     },
46306
46307     /**
46308      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46309      * @return {Mixed} value The field value
46310      */
46311     getRawValue : function()
46312     {
46313         if (this.frame && this.frame.dom.style.display == 'none') {
46314             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46315         }
46316         
46317         if(!this.el || !this.getEditor()) {
46318             //this.getRawValue.defer(100,this); 
46319             return this.value;
46320             return;
46321         }
46322         
46323         
46324         
46325         var value=this.getEditor().GetData();
46326         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46327         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46328          
46329     },
46330     
46331     setSize : function(w,h) {
46332         
46333         
46334         
46335         //if (this.frame && this.frame.dom.style.display == 'none') {
46336         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46337         //    return;
46338         //}
46339         //if(!this.el || !this.getEditor()) {
46340         //    this.setSize.defer(100,this, [w,h]); 
46341         //    return;
46342         //}
46343         
46344         
46345         
46346         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46347         
46348         this.frame.dom.setAttribute('width', w);
46349         this.frame.dom.setAttribute('height', h);
46350         this.frame.setSize(w,h);
46351         
46352     },
46353     
46354     toggleSourceEdit : function(value) {
46355         
46356       
46357          
46358         this.el.dom.style.display = value ? '' : 'none';
46359         this.frame.dom.style.display = value ?  'none' : '';
46360         
46361     },
46362     
46363     
46364     focus: function(tag)
46365     {
46366         if (this.frame.dom.style.display == 'none') {
46367             return Roo.form.FCKeditor.superclass.focus.call(this);
46368         }
46369         if(!this.el || !this.getEditor()) {
46370             this.focus.defer(100,this, [tag]); 
46371             return;
46372         }
46373         
46374         
46375         
46376         
46377         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46378         this.getEditor().Focus();
46379         if (tgs.length) {
46380             if (!this.getEditor().Selection.GetSelection()) {
46381                 this.focus.defer(100,this, [tag]); 
46382                 return;
46383             }
46384             
46385             
46386             var r = this.getEditor().EditorDocument.createRange();
46387             r.setStart(tgs[0],0);
46388             r.setEnd(tgs[0],0);
46389             this.getEditor().Selection.GetSelection().removeAllRanges();
46390             this.getEditor().Selection.GetSelection().addRange(r);
46391             this.getEditor().Focus();
46392         }
46393         
46394     },
46395     
46396     
46397     
46398     replaceTextarea : function()
46399     {
46400         if ( document.getElementById( this.getId() + '___Frame' ) )
46401             return ;
46402         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46403         //{
46404             // We must check the elements firstly using the Id and then the name.
46405         var oTextarea = document.getElementById( this.getId() );
46406         
46407         var colElementsByName = document.getElementsByName( this.getId() ) ;
46408          
46409         oTextarea.style.display = 'none' ;
46410
46411         if ( oTextarea.tabIndex ) {            
46412             this.TabIndex = oTextarea.tabIndex ;
46413         }
46414         
46415         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46416         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46417         this.frame = Roo.get(this.getId() + '___Frame')
46418     },
46419     
46420     _getConfigHtml : function()
46421     {
46422         var sConfig = '' ;
46423
46424         for ( var o in this.fckconfig ) {
46425             sConfig += sConfig.length > 0  ? '&amp;' : '';
46426             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46427         }
46428
46429         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46430     },
46431     
46432     
46433     _getIFrameHtml : function()
46434     {
46435         var sFile = 'fckeditor.html' ;
46436         /* no idea what this is about..
46437         try
46438         {
46439             if ( (/fcksource=true/i).test( window.top.location.search ) )
46440                 sFile = 'fckeditor.original.html' ;
46441         }
46442         catch (e) { 
46443         */
46444
46445         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46446         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46447         
46448         
46449         var html = '<iframe id="' + this.getId() +
46450             '___Frame" src="' + sLink +
46451             '" width="' + this.width +
46452             '" height="' + this.height + '"' +
46453             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46454             ' frameborder="0" scrolling="no"></iframe>' ;
46455
46456         return html ;
46457     },
46458     
46459     _insertHtmlBefore : function( html, element )
46460     {
46461         if ( element.insertAdjacentHTML )       {
46462             // IE
46463             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46464         } else { // Gecko
46465             var oRange = document.createRange() ;
46466             oRange.setStartBefore( element ) ;
46467             var oFragment = oRange.createContextualFragment( html );
46468             element.parentNode.insertBefore( oFragment, element ) ;
46469         }
46470     }
46471     
46472     
46473   
46474     
46475     
46476     
46477     
46478
46479 });
46480
46481 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46482
46483 function FCKeditor_OnComplete(editorInstance){
46484     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46485     f.fckEditor = editorInstance;
46486     //console.log("loaded");
46487     f.fireEvent('editorinit', f, editorInstance);
46488
46489   
46490
46491  
46492
46493
46494
46495
46496
46497
46498
46499
46500
46501
46502
46503
46504
46505
46506
46507 //<script type="text/javascript">
46508 /**
46509  * @class Roo.form.GridField
46510  * @extends Roo.form.Field
46511  * Embed a grid (or editable grid into a form)
46512  * STATUS ALPHA
46513  * 
46514  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46515  * it needs 
46516  * xgrid.store = Roo.data.Store
46517  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46518  * xgrid.store.reader = Roo.data.JsonReader 
46519  * 
46520  * 
46521  * @constructor
46522  * Creates a new GridField
46523  * @param {Object} config Configuration options
46524  */
46525 Roo.form.GridField = function(config){
46526     Roo.form.GridField.superclass.constructor.call(this, config);
46527      
46528 };
46529
46530 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46531     /**
46532      * @cfg {Number} width  - used to restrict width of grid..
46533      */
46534     width : 100,
46535     /**
46536      * @cfg {Number} height - used to restrict height of grid..
46537      */
46538     height : 50,
46539      /**
46540      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46541          * 
46542          *}
46543      */
46544     xgrid : false, 
46545     /**
46546      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46547      * {tag: "input", type: "checkbox", autocomplete: "off"})
46548      */
46549    // defaultAutoCreate : { tag: 'div' },
46550     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46551     /**
46552      * @cfg {String} addTitle Text to include for adding a title.
46553      */
46554     addTitle : false,
46555     //
46556     onResize : function(){
46557         Roo.form.Field.superclass.onResize.apply(this, arguments);
46558     },
46559
46560     initEvents : function(){
46561         // Roo.form.Checkbox.superclass.initEvents.call(this);
46562         // has no events...
46563        
46564     },
46565
46566
46567     getResizeEl : function(){
46568         return this.wrap;
46569     },
46570
46571     getPositionEl : function(){
46572         return this.wrap;
46573     },
46574
46575     // private
46576     onRender : function(ct, position){
46577         
46578         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46579         var style = this.style;
46580         delete this.style;
46581         
46582         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46583         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46584         this.viewEl = this.wrap.createChild({ tag: 'div' });
46585         if (style) {
46586             this.viewEl.applyStyles(style);
46587         }
46588         if (this.width) {
46589             this.viewEl.setWidth(this.width);
46590         }
46591         if (this.height) {
46592             this.viewEl.setHeight(this.height);
46593         }
46594         //if(this.inputValue !== undefined){
46595         //this.setValue(this.value);
46596         
46597         
46598         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46599         
46600         
46601         this.grid.render();
46602         this.grid.getDataSource().on('remove', this.refreshValue, this);
46603         this.grid.getDataSource().on('update', this.refreshValue, this);
46604         this.grid.on('afteredit', this.refreshValue, this);
46605  
46606     },
46607      
46608     
46609     /**
46610      * Sets the value of the item. 
46611      * @param {String} either an object  or a string..
46612      */
46613     setValue : function(v){
46614         //this.value = v;
46615         v = v || []; // empty set..
46616         // this does not seem smart - it really only affects memoryproxy grids..
46617         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46618             var ds = this.grid.getDataSource();
46619             // assumes a json reader..
46620             var data = {}
46621             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46622             ds.loadData( data);
46623         }
46624         // clear selection so it does not get stale.
46625         if (this.grid.sm) { 
46626             this.grid.sm.clearSelections();
46627         }
46628         
46629         Roo.form.GridField.superclass.setValue.call(this, v);
46630         this.refreshValue();
46631         // should load data in the grid really....
46632     },
46633     
46634     // private
46635     refreshValue: function() {
46636          var val = [];
46637         this.grid.getDataSource().each(function(r) {
46638             val.push(r.data);
46639         });
46640         this.el.dom.value = Roo.encode(val);
46641     }
46642     
46643      
46644     
46645     
46646 });/*
46647  * Based on:
46648  * Ext JS Library 1.1.1
46649  * Copyright(c) 2006-2007, Ext JS, LLC.
46650  *
46651  * Originally Released Under LGPL - original licence link has changed is not relivant.
46652  *
46653  * Fork - LGPL
46654  * <script type="text/javascript">
46655  */
46656 /**
46657  * @class Roo.form.DisplayField
46658  * @extends Roo.form.Field
46659  * A generic Field to display non-editable data.
46660  * @constructor
46661  * Creates a new Display Field item.
46662  * @param {Object} config Configuration options
46663  */
46664 Roo.form.DisplayField = function(config){
46665     Roo.form.DisplayField.superclass.constructor.call(this, config);
46666     
46667 };
46668
46669 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46670     inputType:      'hidden',
46671     allowBlank:     true,
46672     readOnly:         true,
46673     
46674  
46675     /**
46676      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46677      */
46678     focusClass : undefined,
46679     /**
46680      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46681      */
46682     fieldClass: 'x-form-field',
46683     
46684      /**
46685      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46686      */
46687     valueRenderer: undefined,
46688     
46689     width: 100,
46690     /**
46691      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46692      * {tag: "input", type: "checkbox", autocomplete: "off"})
46693      */
46694      
46695  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46696
46697     onResize : function(){
46698         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46699         
46700     },
46701
46702     initEvents : function(){
46703         // Roo.form.Checkbox.superclass.initEvents.call(this);
46704         // has no events...
46705        
46706     },
46707
46708
46709     getResizeEl : function(){
46710         return this.wrap;
46711     },
46712
46713     getPositionEl : function(){
46714         return this.wrap;
46715     },
46716
46717     // private
46718     onRender : function(ct, position){
46719         
46720         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46721         //if(this.inputValue !== undefined){
46722         this.wrap = this.el.wrap();
46723         
46724         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46725         
46726         if (this.bodyStyle) {
46727             this.viewEl.applyStyles(this.bodyStyle);
46728         }
46729         //this.viewEl.setStyle('padding', '2px');
46730         
46731         this.setValue(this.value);
46732         
46733     },
46734 /*
46735     // private
46736     initValue : Roo.emptyFn,
46737
46738   */
46739
46740         // private
46741     onClick : function(){
46742         
46743     },
46744
46745     /**
46746      * Sets the checked state of the checkbox.
46747      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46748      */
46749     setValue : function(v){
46750         this.value = v;
46751         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46752         // this might be called before we have a dom element..
46753         if (!this.viewEl) {
46754             return;
46755         }
46756         this.viewEl.dom.innerHTML = html;
46757         Roo.form.DisplayField.superclass.setValue.call(this, v);
46758
46759     }
46760 });/*
46761  * 
46762  * Licence- LGPL
46763  * 
46764  */
46765
46766 /**
46767  * @class Roo.form.DayPicker
46768  * @extends Roo.form.Field
46769  * A Day picker show [M] [T] [W] ....
46770  * @constructor
46771  * Creates a new Day Picker
46772  * @param {Object} config Configuration options
46773  */
46774 Roo.form.DayPicker= function(config){
46775     Roo.form.DayPicker.superclass.constructor.call(this, config);
46776      
46777 };
46778
46779 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46780     /**
46781      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46782      */
46783     focusClass : undefined,
46784     /**
46785      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46786      */
46787     fieldClass: "x-form-field",
46788    
46789     /**
46790      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46791      * {tag: "input", type: "checkbox", autocomplete: "off"})
46792      */
46793     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46794     
46795    
46796     actionMode : 'viewEl', 
46797     //
46798     // private
46799  
46800     inputType : 'hidden',
46801     
46802      
46803     inputElement: false, // real input element?
46804     basedOn: false, // ????
46805     
46806     isFormField: true, // not sure where this is needed!!!!
46807
46808     onResize : function(){
46809         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46810         if(!this.boxLabel){
46811             this.el.alignTo(this.wrap, 'c-c');
46812         }
46813     },
46814
46815     initEvents : function(){
46816         Roo.form.Checkbox.superclass.initEvents.call(this);
46817         this.el.on("click", this.onClick,  this);
46818         this.el.on("change", this.onClick,  this);
46819     },
46820
46821
46822     getResizeEl : function(){
46823         return this.wrap;
46824     },
46825
46826     getPositionEl : function(){
46827         return this.wrap;
46828     },
46829
46830     
46831     // private
46832     onRender : function(ct, position){
46833         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46834        
46835         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46836         
46837         var r1 = '<table><tr>';
46838         var r2 = '<tr class="x-form-daypick-icons">';
46839         for (var i=0; i < 7; i++) {
46840             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46841             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46842         }
46843         
46844         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46845         viewEl.select('img').on('click', this.onClick, this);
46846         this.viewEl = viewEl;   
46847         
46848         
46849         // this will not work on Chrome!!!
46850         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46851         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46852         
46853         
46854           
46855
46856     },
46857
46858     // private
46859     initValue : Roo.emptyFn,
46860
46861     /**
46862      * Returns the checked state of the checkbox.
46863      * @return {Boolean} True if checked, else false
46864      */
46865     getValue : function(){
46866         return this.el.dom.value;
46867         
46868     },
46869
46870         // private
46871     onClick : function(e){ 
46872         //this.setChecked(!this.checked);
46873         Roo.get(e.target).toggleClass('x-menu-item-checked');
46874         this.refreshValue();
46875         //if(this.el.dom.checked != this.checked){
46876         //    this.setValue(this.el.dom.checked);
46877        // }
46878     },
46879     
46880     // private
46881     refreshValue : function()
46882     {
46883         var val = '';
46884         this.viewEl.select('img',true).each(function(e,i,n)  {
46885             val += e.is(".x-menu-item-checked") ? String(n) : '';
46886         });
46887         this.setValue(val, true);
46888     },
46889
46890     /**
46891      * Sets the checked state of the checkbox.
46892      * On is always based on a string comparison between inputValue and the param.
46893      * @param {Boolean/String} value - the value to set 
46894      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46895      */
46896     setValue : function(v,suppressEvent){
46897         if (!this.el.dom) {
46898             return;
46899         }
46900         var old = this.el.dom.value ;
46901         this.el.dom.value = v;
46902         if (suppressEvent) {
46903             return ;
46904         }
46905          
46906         // update display..
46907         this.viewEl.select('img',true).each(function(e,i,n)  {
46908             
46909             var on = e.is(".x-menu-item-checked");
46910             var newv = v.indexOf(String(n)) > -1;
46911             if (on != newv) {
46912                 e.toggleClass('x-menu-item-checked');
46913             }
46914             
46915         });
46916         
46917         
46918         this.fireEvent('change', this, v, old);
46919         
46920         
46921     },
46922    
46923     // handle setting of hidden value by some other method!!?!?
46924     setFromHidden: function()
46925     {
46926         if(!this.el){
46927             return;
46928         }
46929         //console.log("SET FROM HIDDEN");
46930         //alert('setFrom hidden');
46931         this.setValue(this.el.dom.value);
46932     },
46933     
46934     onDestroy : function()
46935     {
46936         if(this.viewEl){
46937             Roo.get(this.viewEl).remove();
46938         }
46939          
46940         Roo.form.DayPicker.superclass.onDestroy.call(this);
46941     }
46942
46943 });/*
46944  * RooJS Library 1.1.1
46945  * Copyright(c) 2008-2011  Alan Knowles
46946  *
46947  * License - LGPL
46948  */
46949  
46950
46951 /**
46952  * @class Roo.form.ComboCheck
46953  * @extends Roo.form.ComboBox
46954  * A combobox for multiple select items.
46955  *
46956  * FIXME - could do with a reset button..
46957  * 
46958  * @constructor
46959  * Create a new ComboCheck
46960  * @param {Object} config Configuration options
46961  */
46962 Roo.form.ComboCheck = function(config){
46963     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46964     // should verify some data...
46965     // like
46966     // hiddenName = required..
46967     // displayField = required
46968     // valudField == required
46969     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46970     var _t = this;
46971     Roo.each(req, function(e) {
46972         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46973             throw "Roo.form.ComboCheck : missing value for: " + e;
46974         }
46975     });
46976     
46977     
46978 };
46979
46980 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46981      
46982      
46983     editable : false,
46984      
46985     selectedClass: 'x-menu-item-checked', 
46986     
46987     // private
46988     onRender : function(ct, position){
46989         var _t = this;
46990         
46991         
46992         
46993         if(!this.tpl){
46994             var cls = 'x-combo-list';
46995
46996             
46997             this.tpl =  new Roo.Template({
46998                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46999                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47000                    '<span>{' + this.displayField + '}</span>' +
47001                     '</div>' 
47002                 
47003             });
47004         }
47005  
47006         
47007         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47008         this.view.singleSelect = false;
47009         this.view.multiSelect = true;
47010         this.view.toggleSelect = true;
47011         this.pageTb.add(new Roo.Toolbar.Fill(), {
47012             
47013             text: 'Done',
47014             handler: function()
47015             {
47016                 _t.collapse();
47017             }
47018         });
47019     },
47020     
47021     onViewOver : function(e, t){
47022         // do nothing...
47023         return;
47024         
47025     },
47026     
47027     onViewClick : function(doFocus,index){
47028         return;
47029         
47030     },
47031     select: function () {
47032         //Roo.log("SELECT CALLED");
47033     },
47034      
47035     selectByValue : function(xv, scrollIntoView){
47036         var ar = this.getValueArray();
47037         var sels = [];
47038         
47039         Roo.each(ar, function(v) {
47040             if(v === undefined || v === null){
47041                 return;
47042             }
47043             var r = this.findRecord(this.valueField, v);
47044             if(r){
47045                 sels.push(this.store.indexOf(r))
47046                 
47047             }
47048         },this);
47049         this.view.select(sels);
47050         return false;
47051     },
47052     
47053     
47054     
47055     onSelect : function(record, index){
47056        // Roo.log("onselect Called");
47057        // this is only called by the clear button now..
47058         this.view.clearSelections();
47059         this.setValue('[]');
47060         if (this.value != this.valueBefore) {
47061             this.fireEvent('change', this, this.value, this.valueBefore);
47062             this.valueBefore = this.value;
47063         }
47064     },
47065     getValueArray : function()
47066     {
47067         var ar = [] ;
47068         
47069         try {
47070             //Roo.log(this.value);
47071             if (typeof(this.value) == 'undefined') {
47072                 return [];
47073             }
47074             var ar = Roo.decode(this.value);
47075             return  ar instanceof Array ? ar : []; //?? valid?
47076             
47077         } catch(e) {
47078             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47079             return [];
47080         }
47081          
47082     },
47083     expand : function ()
47084     {
47085         
47086         Roo.form.ComboCheck.superclass.expand.call(this);
47087         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47088         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47089         
47090
47091     },
47092     
47093     collapse : function(){
47094         Roo.form.ComboCheck.superclass.collapse.call(this);
47095         var sl = this.view.getSelectedIndexes();
47096         var st = this.store;
47097         var nv = [];
47098         var tv = [];
47099         var r;
47100         Roo.each(sl, function(i) {
47101             r = st.getAt(i);
47102             nv.push(r.get(this.valueField));
47103         },this);
47104         this.setValue(Roo.encode(nv));
47105         if (this.value != this.valueBefore) {
47106
47107             this.fireEvent('change', this, this.value, this.valueBefore);
47108             this.valueBefore = this.value;
47109         }
47110         
47111     },
47112     
47113     setValue : function(v){
47114         // Roo.log(v);
47115         this.value = v;
47116         
47117         var vals = this.getValueArray();
47118         var tv = [];
47119         Roo.each(vals, function(k) {
47120             var r = this.findRecord(this.valueField, k);
47121             if(r){
47122                 tv.push(r.data[this.displayField]);
47123             }else if(this.valueNotFoundText !== undefined){
47124                 tv.push( this.valueNotFoundText );
47125             }
47126         },this);
47127        // Roo.log(tv);
47128         
47129         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47130         this.hiddenField.value = v;
47131         this.value = v;
47132     }
47133     
47134 });/*
47135  * Based on:
47136  * Ext JS Library 1.1.1
47137  * Copyright(c) 2006-2007, Ext JS, LLC.
47138  *
47139  * Originally Released Under LGPL - original licence link has changed is not relivant.
47140  *
47141  * Fork - LGPL
47142  * <script type="text/javascript">
47143  */
47144  
47145 /**
47146  * @class Roo.form.Signature
47147  * @extends Roo.form.Field
47148  * Signature field.  
47149  * @constructor
47150  * 
47151  * @param {Object} config Configuration options
47152  */
47153
47154 Roo.form.Signature = function(config){
47155     Roo.form.Signature.superclass.constructor.call(this, config);
47156     
47157     this.addEvents({// not in used??
47158          /**
47159          * @event confirm
47160          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47161              * @param {Roo.form.Signature} combo This combo box
47162              */
47163         'confirm' : true,
47164         /**
47165          * @event reset
47166          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47167              * @param {Roo.form.ComboBox} combo This combo box
47168              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47169              */
47170         'reset' : true
47171     });
47172 };
47173
47174 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47175     /**
47176      * @cfg {Object} labels Label to use when rendering a form.
47177      * defaults to 
47178      * labels : { 
47179      *      clear : "Clear",
47180      *      confirm : "Confirm"
47181      *  }
47182      */
47183     labels : { 
47184         clear : "Clear",
47185         confirm : "Confirm"
47186     },
47187     /**
47188      * @cfg {Number} width The signature panel width (defaults to 300)
47189      */
47190     width: 300,
47191     /**
47192      * @cfg {Number} height The signature panel height (defaults to 100)
47193      */
47194     height : 100,
47195     /**
47196      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47197      */
47198     allowBlank : false,
47199     
47200     //private
47201     // {Object} signPanel The signature SVG panel element (defaults to {})
47202     signPanel : {},
47203     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47204     isMouseDown : false,
47205     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47206     isConfirmed : false,
47207     // {String} signatureTmp SVG mapping string (defaults to empty string)
47208     signatureTmp : '',
47209     
47210     
47211     defaultAutoCreate : { // modified by initCompnoent..
47212         tag: "input",
47213         type:"hidden"
47214     },
47215
47216     // private
47217     onRender : function(ct, position){
47218         
47219         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47220         
47221         this.wrap = this.el.wrap({
47222             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47223         });
47224         
47225         this.createToolbar(this);
47226         this.signPanel = this.wrap.createChild({
47227                 tag: 'div',
47228                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47229             }, this.el
47230         );
47231             
47232         this.svgID = Roo.id();
47233         this.svgEl = this.signPanel.createChild({
47234               xmlns : 'http://www.w3.org/2000/svg',
47235               tag : 'svg',
47236               id : this.svgID + "-svg",
47237               width: this.width,
47238               height: this.height,
47239               viewBox: '0 0 '+this.width+' '+this.height,
47240               cn : [
47241                 {
47242                     tag: "rect",
47243                     id: this.svgID + "-svg-r",
47244                     width: this.width,
47245                     height: this.height,
47246                     fill: "#ffa"
47247                 },
47248                 {
47249                     tag: "line",
47250                     id: this.svgID + "-svg-l",
47251                     x1: "0", // start
47252                     y1: (this.height*0.8), // start set the line in 80% of height
47253                     x2: this.width, // end
47254                     y2: (this.height*0.8), // end set the line in 80% of height
47255                     'stroke': "#666",
47256                     'stroke-width': "1",
47257                     'stroke-dasharray': "3",
47258                     'shape-rendering': "crispEdges",
47259                     'pointer-events': "none"
47260                 },
47261                 {
47262                     tag: "path",
47263                     id: this.svgID + "-svg-p",
47264                     'stroke': "navy",
47265                     'stroke-width': "3",
47266                     'fill': "none",
47267                     'pointer-events': 'none'
47268                 }
47269               ]
47270         });
47271         this.createSVG();
47272         this.svgBox = this.svgEl.dom.getScreenCTM();
47273     },
47274     createSVG : function(){ 
47275         var svg = this.signPanel;
47276         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47277         var t = this;
47278
47279         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47280         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47281         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47282         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47283         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47284         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47285         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47286         
47287     },
47288     isTouchEvent : function(e){
47289         return e.type.match(/^touch/);
47290     },
47291     getCoords : function (e) {
47292         var pt    = this.svgEl.dom.createSVGPoint();
47293         pt.x = e.clientX; 
47294         pt.y = e.clientY;
47295         if (this.isTouchEvent(e)) {
47296             pt.x =  e.targetTouches[0].clientX 
47297             pt.y = e.targetTouches[0].clientY;
47298         }
47299         var a = this.svgEl.dom.getScreenCTM();
47300         var b = a.inverse();
47301         var mx = pt.matrixTransform(b);
47302         return mx.x + ',' + mx.y;
47303     },
47304     //mouse event headler 
47305     down : function (e) {
47306         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47307         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47308         
47309         this.isMouseDown = true;
47310         
47311         e.preventDefault();
47312     },
47313     move : function (e) {
47314         if (this.isMouseDown) {
47315             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47316             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47317         }
47318         
47319         e.preventDefault();
47320     },
47321     up : function (e) {
47322         this.isMouseDown = false;
47323         var sp = this.signatureTmp.split(' ');
47324         
47325         if(sp.length > 1){
47326             if(!sp[sp.length-2].match(/^L/)){
47327                 sp.pop();
47328                 sp.pop();
47329                 sp.push("");
47330                 this.signatureTmp = sp.join(" ");
47331             }
47332         }
47333         if(this.getValue() != this.signatureTmp){
47334             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47335             this.isConfirmed = false;
47336         }
47337         e.preventDefault();
47338     },
47339     
47340     /**
47341      * Protected method that will not generally be called directly. It
47342      * is called when the editor creates its toolbar. Override this method if you need to
47343      * add custom toolbar buttons.
47344      * @param {HtmlEditor} editor
47345      */
47346     createToolbar : function(editor){
47347          function btn(id, toggle, handler){
47348             var xid = fid + '-'+ id ;
47349             return {
47350                 id : xid,
47351                 cmd : id,
47352                 cls : 'x-btn-icon x-edit-'+id,
47353                 enableToggle:toggle !== false,
47354                 scope: editor, // was editor...
47355                 handler:handler||editor.relayBtnCmd,
47356                 clickEvent:'mousedown',
47357                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47358                 tabIndex:-1
47359             };
47360         }
47361         
47362         
47363         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47364         this.tb = tb;
47365         this.tb.add(
47366            {
47367                 cls : ' x-signature-btn x-signature-'+id,
47368                 scope: editor, // was editor...
47369                 handler: this.reset,
47370                 clickEvent:'mousedown',
47371                 text: this.labels.clear
47372             },
47373             {
47374                  xtype : 'Fill',
47375                  xns: Roo.Toolbar
47376             }, 
47377             {
47378                 cls : '  x-signature-btn x-signature-'+id,
47379                 scope: editor, // was editor...
47380                 handler: this.confirmHandler,
47381                 clickEvent:'mousedown',
47382                 text: this.labels.confirm
47383             }
47384         );
47385     
47386     },
47387     //public
47388     /**
47389      * when user is clicked confirm then show this image.....
47390      * 
47391      * @return {String} Image Data URI
47392      */
47393     getImageDataURI : function(){
47394         var svg = this.svgEl.dom.parentNode.innerHTML;
47395         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47396         return src; 
47397     },
47398     /**
47399      * 
47400      * @return {Boolean} this.isConfirmed
47401      */
47402     getConfirmed : function(){
47403         return this.isConfirmed;
47404     },
47405     /**
47406      * 
47407      * @return {Number} this.width
47408      */
47409     getWidth : function(){
47410         return this.width;
47411     },
47412     /**
47413      * 
47414      * @return {Number} this.height
47415      */
47416     getHeight : function(){
47417         return this.height;
47418     },
47419     // private
47420     getSignature : function(){
47421         return this.signatureTmp;
47422     },
47423     // private
47424     reset : function(){
47425         this.signatureTmp = '';
47426         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47427         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47428         this.isConfirmed = false;
47429         Roo.form.Signature.superclass.reset.call(this);
47430     },
47431     setSignature : function(s){
47432         this.signatureTmp = s;
47433         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47434         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47435         this.setValue(s);
47436         this.isConfirmed = false;
47437         Roo.form.Signature.superclass.reset.call(this);
47438     }, 
47439     test : function(){
47440 //        Roo.log(this.signPanel.dom.contentWindow.up())
47441     },
47442     //private
47443     setConfirmed : function(){
47444         
47445         
47446         
47447 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47448     },
47449     // private
47450     confirmHandler : function(){
47451         if(!this.getSignature()){
47452             return;
47453         }
47454         
47455         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47456         this.setValue(this.getSignature());
47457         this.isConfirmed = true;
47458         
47459         this.fireEvent('confirm', this);
47460     },
47461     // private
47462     // Subclasses should provide the validation implementation by overriding this
47463     validateValue : function(value){
47464         if(this.allowBlank){
47465             return true;
47466         }
47467         
47468         if(this.isConfirmed){
47469             return true;
47470         }
47471         return false;
47472     }
47473 });/*
47474  * Based on:
47475  * Ext JS Library 1.1.1
47476  * Copyright(c) 2006-2007, Ext JS, LLC.
47477  *
47478  * Originally Released Under LGPL - original licence link has changed is not relivant.
47479  *
47480  * Fork - LGPL
47481  * <script type="text/javascript">
47482  */
47483  
47484
47485 /**
47486  * @class Roo.form.ComboBox
47487  * @extends Roo.form.TriggerField
47488  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47489  * @constructor
47490  * Create a new ComboBox.
47491  * @param {Object} config Configuration options
47492  */
47493 Roo.form.Select = function(config){
47494     Roo.form.Select.superclass.constructor.call(this, config);
47495      
47496 };
47497
47498 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47499     /**
47500      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47501      */
47502     /**
47503      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47504      * rendering into an Roo.Editor, defaults to false)
47505      */
47506     /**
47507      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47508      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47509      */
47510     /**
47511      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47512      */
47513     /**
47514      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47515      * the dropdown list (defaults to undefined, with no header element)
47516      */
47517
47518      /**
47519      * @cfg {String/Roo.Template} tpl The template to use to render the output
47520      */
47521      
47522     // private
47523     defaultAutoCreate : {tag: "select"  },
47524     /**
47525      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47526      */
47527     listWidth: undefined,
47528     /**
47529      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47530      * mode = 'remote' or 'text' if mode = 'local')
47531      */
47532     displayField: undefined,
47533     /**
47534      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47535      * mode = 'remote' or 'value' if mode = 'local'). 
47536      * Note: use of a valueField requires the user make a selection
47537      * in order for a value to be mapped.
47538      */
47539     valueField: undefined,
47540     
47541     
47542     /**
47543      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47544      * field's data value (defaults to the underlying DOM element's name)
47545      */
47546     hiddenName: undefined,
47547     /**
47548      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47549      */
47550     listClass: '',
47551     /**
47552      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47553      */
47554     selectedClass: 'x-combo-selected',
47555     /**
47556      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47557      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47558      * which displays a downward arrow icon).
47559      */
47560     triggerClass : 'x-form-arrow-trigger',
47561     /**
47562      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47563      */
47564     shadow:'sides',
47565     /**
47566      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47567      * anchor positions (defaults to 'tl-bl')
47568      */
47569     listAlign: 'tl-bl?',
47570     /**
47571      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47572      */
47573     maxHeight: 300,
47574     /**
47575      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47576      * query specified by the allQuery config option (defaults to 'query')
47577      */
47578     triggerAction: 'query',
47579     /**
47580      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47581      * (defaults to 4, does not apply if editable = false)
47582      */
47583     minChars : 4,
47584     /**
47585      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47586      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47587      */
47588     typeAhead: false,
47589     /**
47590      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47591      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47592      */
47593     queryDelay: 500,
47594     /**
47595      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47596      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47597      */
47598     pageSize: 0,
47599     /**
47600      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47601      * when editable = true (defaults to false)
47602      */
47603     selectOnFocus:false,
47604     /**
47605      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47606      */
47607     queryParam: 'query',
47608     /**
47609      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47610      * when mode = 'remote' (defaults to 'Loading...')
47611      */
47612     loadingText: 'Loading...',
47613     /**
47614      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47615      */
47616     resizable: false,
47617     /**
47618      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47619      */
47620     handleHeight : 8,
47621     /**
47622      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47623      * traditional select (defaults to true)
47624      */
47625     editable: true,
47626     /**
47627      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47628      */
47629     allQuery: '',
47630     /**
47631      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47632      */
47633     mode: 'remote',
47634     /**
47635      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47636      * listWidth has a higher value)
47637      */
47638     minListWidth : 70,
47639     /**
47640      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47641      * allow the user to set arbitrary text into the field (defaults to false)
47642      */
47643     forceSelection:false,
47644     /**
47645      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47646      * if typeAhead = true (defaults to 250)
47647      */
47648     typeAheadDelay : 250,
47649     /**
47650      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47651      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47652      */
47653     valueNotFoundText : undefined,
47654     
47655     /**
47656      * @cfg {String} defaultValue The value displayed after loading the store.
47657      */
47658     defaultValue: '',
47659     
47660     /**
47661      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47662      */
47663     blockFocus : false,
47664     
47665     /**
47666      * @cfg {Boolean} disableClear Disable showing of clear button.
47667      */
47668     disableClear : false,
47669     /**
47670      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47671      */
47672     alwaysQuery : false,
47673     
47674     //private
47675     addicon : false,
47676     editicon: false,
47677     
47678     // element that contains real text value.. (when hidden is used..)
47679      
47680     // private
47681     onRender : function(ct, position){
47682         Roo.form.Field.prototype.onRender.call(this, ct, position);
47683         
47684         if(this.store){
47685             this.store.on('beforeload', this.onBeforeLoad, this);
47686             this.store.on('load', this.onLoad, this);
47687             this.store.on('loadexception', this.onLoadException, this);
47688             this.store.load({});
47689         }
47690         
47691         
47692         
47693     },
47694
47695     // private
47696     initEvents : function(){
47697         //Roo.form.ComboBox.superclass.initEvents.call(this);
47698  
47699     },
47700
47701     onDestroy : function(){
47702        
47703         if(this.store){
47704             this.store.un('beforeload', this.onBeforeLoad, this);
47705             this.store.un('load', this.onLoad, this);
47706             this.store.un('loadexception', this.onLoadException, this);
47707         }
47708         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47709     },
47710
47711     // private
47712     fireKey : function(e){
47713         if(e.isNavKeyPress() && !this.list.isVisible()){
47714             this.fireEvent("specialkey", this, e);
47715         }
47716     },
47717
47718     // private
47719     onResize: function(w, h){
47720         
47721         return; 
47722     
47723         
47724     },
47725
47726     /**
47727      * Allow or prevent the user from directly editing the field text.  If false is passed,
47728      * the user will only be able to select from the items defined in the dropdown list.  This method
47729      * is the runtime equivalent of setting the 'editable' config option at config time.
47730      * @param {Boolean} value True to allow the user to directly edit the field text
47731      */
47732     setEditable : function(value){
47733          
47734     },
47735
47736     // private
47737     onBeforeLoad : function(){
47738         
47739         Roo.log("Select before load");
47740         return;
47741     
47742         this.innerList.update(this.loadingText ?
47743                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47744         //this.restrictHeight();
47745         this.selectedIndex = -1;
47746     },
47747
47748     // private
47749     onLoad : function(){
47750
47751     
47752         var dom = this.el.dom;
47753         dom.innerHTML = '';
47754          var od = dom.ownerDocument;
47755          
47756         if (this.emptyText) {
47757             var op = od.createElement('option');
47758             op.setAttribute('value', '');
47759             op.innerHTML = String.format('{0}', this.emptyText);
47760             dom.appendChild(op);
47761         }
47762         if(this.store.getCount() > 0){
47763            
47764             var vf = this.valueField;
47765             var df = this.displayField;
47766             this.store.data.each(function(r) {
47767                 // which colmsn to use... testing - cdoe / title..
47768                 var op = od.createElement('option');
47769                 op.setAttribute('value', r.data[vf]);
47770                 op.innerHTML = String.format('{0}', r.data[df]);
47771                 dom.appendChild(op);
47772             });
47773             if (typeof(this.defaultValue != 'undefined')) {
47774                 this.setValue(this.defaultValue);
47775             }
47776             
47777              
47778         }else{
47779             //this.onEmptyResults();
47780         }
47781         //this.el.focus();
47782     },
47783     // private
47784     onLoadException : function()
47785     {
47786         dom.innerHTML = '';
47787             
47788         Roo.log("Select on load exception");
47789         return;
47790     
47791         this.collapse();
47792         Roo.log(this.store.reader.jsonData);
47793         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47794             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47795         }
47796         
47797         
47798     },
47799     // private
47800     onTypeAhead : function(){
47801          
47802     },
47803
47804     // private
47805     onSelect : function(record, index){
47806         Roo.log('on select?');
47807         return;
47808         if(this.fireEvent('beforeselect', this, record, index) !== false){
47809             this.setFromData(index > -1 ? record.data : false);
47810             this.collapse();
47811             this.fireEvent('select', this, record, index);
47812         }
47813     },
47814
47815     /**
47816      * Returns the currently selected field value or empty string if no value is set.
47817      * @return {String} value The selected value
47818      */
47819     getValue : function(){
47820         var dom = this.el.dom;
47821         this.value = dom.options[dom.selectedIndex].value;
47822         return this.value;
47823         
47824     },
47825
47826     /**
47827      * Clears any text/value currently set in the field
47828      */
47829     clearValue : function(){
47830         this.value = '';
47831         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47832         
47833     },
47834
47835     /**
47836      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47837      * will be displayed in the field.  If the value does not match the data value of an existing item,
47838      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47839      * Otherwise the field will be blank (although the value will still be set).
47840      * @param {String} value The value to match
47841      */
47842     setValue : function(v){
47843         var d = this.el.dom;
47844         for (var i =0; i < d.options.length;i++) {
47845             if (v == d.options[i].value) {
47846                 d.selectedIndex = i;
47847                 this.value = v;
47848                 return;
47849             }
47850         }
47851         this.clearValue();
47852     },
47853     /**
47854      * @property {Object} the last set data for the element
47855      */
47856     
47857     lastData : false,
47858     /**
47859      * Sets the value of the field based on a object which is related to the record format for the store.
47860      * @param {Object} value the value to set as. or false on reset?
47861      */
47862     setFromData : function(o){
47863         Roo.log('setfrom data?');
47864          
47865         
47866         
47867     },
47868     // private
47869     reset : function(){
47870         this.clearValue();
47871     },
47872     // private
47873     findRecord : function(prop, value){
47874         
47875         return false;
47876     
47877         var record;
47878         if(this.store.getCount() > 0){
47879             this.store.each(function(r){
47880                 if(r.data[prop] == value){
47881                     record = r;
47882                     return false;
47883                 }
47884                 return true;
47885             });
47886         }
47887         return record;
47888     },
47889     
47890     getName: function()
47891     {
47892         // returns hidden if it's set..
47893         if (!this.rendered) {return ''};
47894         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47895         
47896     },
47897      
47898
47899     
47900
47901     // private
47902     onEmptyResults : function(){
47903         Roo.log('empty results');
47904         //this.collapse();
47905     },
47906
47907     /**
47908      * Returns true if the dropdown list is expanded, else false.
47909      */
47910     isExpanded : function(){
47911         return false;
47912     },
47913
47914     /**
47915      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47916      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47917      * @param {String} value The data value of the item to select
47918      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47919      * selected item if it is not currently in view (defaults to true)
47920      * @return {Boolean} True if the value matched an item in the list, else false
47921      */
47922     selectByValue : function(v, scrollIntoView){
47923         Roo.log('select By Value');
47924         return false;
47925     
47926         if(v !== undefined && v !== null){
47927             var r = this.findRecord(this.valueField || this.displayField, v);
47928             if(r){
47929                 this.select(this.store.indexOf(r), scrollIntoView);
47930                 return true;
47931             }
47932         }
47933         return false;
47934     },
47935
47936     /**
47937      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47938      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47939      * @param {Number} index The zero-based index of the list item to select
47940      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47941      * selected item if it is not currently in view (defaults to true)
47942      */
47943     select : function(index, scrollIntoView){
47944         Roo.log('select ');
47945         return  ;
47946         
47947         this.selectedIndex = index;
47948         this.view.select(index);
47949         if(scrollIntoView !== false){
47950             var el = this.view.getNode(index);
47951             if(el){
47952                 this.innerList.scrollChildIntoView(el, false);
47953             }
47954         }
47955     },
47956
47957       
47958
47959     // private
47960     validateBlur : function(){
47961         
47962         return;
47963         
47964     },
47965
47966     // private
47967     initQuery : function(){
47968         this.doQuery(this.getRawValue());
47969     },
47970
47971     // private
47972     doForce : function(){
47973         if(this.el.dom.value.length > 0){
47974             this.el.dom.value =
47975                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47976              
47977         }
47978     },
47979
47980     /**
47981      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47982      * query allowing the query action to be canceled if needed.
47983      * @param {String} query The SQL query to execute
47984      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47985      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47986      * saved in the current store (defaults to false)
47987      */
47988     doQuery : function(q, forceAll){
47989         
47990         Roo.log('doQuery?');
47991         if(q === undefined || q === null){
47992             q = '';
47993         }
47994         var qe = {
47995             query: q,
47996             forceAll: forceAll,
47997             combo: this,
47998             cancel:false
47999         };
48000         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48001             return false;
48002         }
48003         q = qe.query;
48004         forceAll = qe.forceAll;
48005         if(forceAll === true || (q.length >= this.minChars)){
48006             if(this.lastQuery != q || this.alwaysQuery){
48007                 this.lastQuery = q;
48008                 if(this.mode == 'local'){
48009                     this.selectedIndex = -1;
48010                     if(forceAll){
48011                         this.store.clearFilter();
48012                     }else{
48013                         this.store.filter(this.displayField, q);
48014                     }
48015                     this.onLoad();
48016                 }else{
48017                     this.store.baseParams[this.queryParam] = q;
48018                     this.store.load({
48019                         params: this.getParams(q)
48020                     });
48021                     this.expand();
48022                 }
48023             }else{
48024                 this.selectedIndex = -1;
48025                 this.onLoad();   
48026             }
48027         }
48028     },
48029
48030     // private
48031     getParams : function(q){
48032         var p = {};
48033         //p[this.queryParam] = q;
48034         if(this.pageSize){
48035             p.start = 0;
48036             p.limit = this.pageSize;
48037         }
48038         return p;
48039     },
48040
48041     /**
48042      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48043      */
48044     collapse : function(){
48045         
48046     },
48047
48048     // private
48049     collapseIf : function(e){
48050         
48051     },
48052
48053     /**
48054      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48055      */
48056     expand : function(){
48057         
48058     } ,
48059
48060     // private
48061      
48062
48063     /** 
48064     * @cfg {Boolean} grow 
48065     * @hide 
48066     */
48067     /** 
48068     * @cfg {Number} growMin 
48069     * @hide 
48070     */
48071     /** 
48072     * @cfg {Number} growMax 
48073     * @hide 
48074     */
48075     /**
48076      * @hide
48077      * @method autoSize
48078      */
48079     
48080     setWidth : function()
48081     {
48082         
48083     },
48084     getResizeEl : function(){
48085         return this.el;
48086     }
48087 });//<script type="text/javasscript">
48088  
48089
48090 /**
48091  * @class Roo.DDView
48092  * A DnD enabled version of Roo.View.
48093  * @param {Element/String} container The Element in which to create the View.
48094  * @param {String} tpl The template string used to create the markup for each element of the View
48095  * @param {Object} config The configuration properties. These include all the config options of
48096  * {@link Roo.View} plus some specific to this class.<br>
48097  * <p>
48098  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48099  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48100  * <p>
48101  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48102 .x-view-drag-insert-above {
48103         border-top:1px dotted #3366cc;
48104 }
48105 .x-view-drag-insert-below {
48106         border-bottom:1px dotted #3366cc;
48107 }
48108 </code></pre>
48109  * 
48110  */
48111  
48112 Roo.DDView = function(container, tpl, config) {
48113     Roo.DDView.superclass.constructor.apply(this, arguments);
48114     this.getEl().setStyle("outline", "0px none");
48115     this.getEl().unselectable();
48116     if (this.dragGroup) {
48117                 this.setDraggable(this.dragGroup.split(","));
48118     }
48119     if (this.dropGroup) {
48120                 this.setDroppable(this.dropGroup.split(","));
48121     }
48122     if (this.deletable) {
48123         this.setDeletable();
48124     }
48125     this.isDirtyFlag = false;
48126         this.addEvents({
48127                 "drop" : true
48128         });
48129 };
48130
48131 Roo.extend(Roo.DDView, Roo.View, {
48132 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48133 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48134 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48135 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48136
48137         isFormField: true,
48138
48139         reset: Roo.emptyFn,
48140         
48141         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48142
48143         validate: function() {
48144                 return true;
48145         },
48146         
48147         destroy: function() {
48148                 this.purgeListeners();
48149                 this.getEl.removeAllListeners();
48150                 this.getEl().remove();
48151                 if (this.dragZone) {
48152                         if (this.dragZone.destroy) {
48153                                 this.dragZone.destroy();
48154                         }
48155                 }
48156                 if (this.dropZone) {
48157                         if (this.dropZone.destroy) {
48158                                 this.dropZone.destroy();
48159                         }
48160                 }
48161         },
48162
48163 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48164         getName: function() {
48165                 return this.name;
48166         },
48167
48168 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48169         setValue: function(v) {
48170                 if (!this.store) {
48171                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48172                 }
48173                 var data = {};
48174                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48175                 this.store.proxy = new Roo.data.MemoryProxy(data);
48176                 this.store.load();
48177         },
48178
48179 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48180         getValue: function() {
48181                 var result = '(';
48182                 this.store.each(function(rec) {
48183                         result += rec.id + ',';
48184                 });
48185                 return result.substr(0, result.length - 1) + ')';
48186         },
48187         
48188         getIds: function() {
48189                 var i = 0, result = new Array(this.store.getCount());
48190                 this.store.each(function(rec) {
48191                         result[i++] = rec.id;
48192                 });
48193                 return result;
48194         },
48195         
48196         isDirty: function() {
48197                 return this.isDirtyFlag;
48198         },
48199
48200 /**
48201  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48202  *      whole Element becomes the target, and this causes the drop gesture to append.
48203  */
48204     getTargetFromEvent : function(e) {
48205                 var target = e.getTarget();
48206                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48207                 target = target.parentNode;
48208                 }
48209                 if (!target) {
48210                         target = this.el.dom.lastChild || this.el.dom;
48211                 }
48212                 return target;
48213     },
48214
48215 /**
48216  *      Create the drag data which consists of an object which has the property "ddel" as
48217  *      the drag proxy element. 
48218  */
48219     getDragData : function(e) {
48220         var target = this.findItemFromChild(e.getTarget());
48221                 if(target) {
48222                         this.handleSelection(e);
48223                         var selNodes = this.getSelectedNodes();
48224             var dragData = {
48225                 source: this,
48226                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48227                 nodes: selNodes,
48228                 records: []
48229                         };
48230                         var selectedIndices = this.getSelectedIndexes();
48231                         for (var i = 0; i < selectedIndices.length; i++) {
48232                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48233                         }
48234                         if (selNodes.length == 1) {
48235                                 dragData.ddel = target.cloneNode(true); // the div element
48236                         } else {
48237                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48238                                 div.className = 'multi-proxy';
48239                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48240                                         div.appendChild(selNodes[i].cloneNode(true));
48241                                 }
48242                                 dragData.ddel = div;
48243                         }
48244             //console.log(dragData)
48245             //console.log(dragData.ddel.innerHTML)
48246                         return dragData;
48247                 }
48248         //console.log('nodragData')
48249                 return false;
48250     },
48251     
48252 /**     Specify to which ddGroup items in this DDView may be dragged. */
48253     setDraggable: function(ddGroup) {
48254         if (ddGroup instanceof Array) {
48255                 Roo.each(ddGroup, this.setDraggable, this);
48256                 return;
48257         }
48258         if (this.dragZone) {
48259                 this.dragZone.addToGroup(ddGroup);
48260         } else {
48261                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48262                                 containerScroll: true,
48263                                 ddGroup: ddGroup 
48264
48265                         });
48266 //                      Draggability implies selection. DragZone's mousedown selects the element.
48267                         if (!this.multiSelect) { this.singleSelect = true; }
48268
48269 //                      Wire the DragZone's handlers up to methods in *this*
48270                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48271                 }
48272     },
48273
48274 /**     Specify from which ddGroup this DDView accepts drops. */
48275     setDroppable: function(ddGroup) {
48276         if (ddGroup instanceof Array) {
48277                 Roo.each(ddGroup, this.setDroppable, this);
48278                 return;
48279         }
48280         if (this.dropZone) {
48281                 this.dropZone.addToGroup(ddGroup);
48282         } else {
48283                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48284                                 containerScroll: true,
48285                                 ddGroup: ddGroup
48286                         });
48287
48288 //                      Wire the DropZone's handlers up to methods in *this*
48289                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48290                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48291                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48292                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48293                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48294                 }
48295     },
48296
48297 /**     Decide whether to drop above or below a View node. */
48298     getDropPoint : function(e, n, dd){
48299         if (n == this.el.dom) { return "above"; }
48300                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48301                 var c = t + (b - t) / 2;
48302                 var y = Roo.lib.Event.getPageY(e);
48303                 if(y <= c) {
48304                         return "above";
48305                 }else{
48306                         return "below";
48307                 }
48308     },
48309
48310     onNodeEnter : function(n, dd, e, data){
48311                 return false;
48312     },
48313     
48314     onNodeOver : function(n, dd, e, data){
48315                 var pt = this.getDropPoint(e, n, dd);
48316                 // set the insert point style on the target node
48317                 var dragElClass = this.dropNotAllowed;
48318                 if (pt) {
48319                         var targetElClass;
48320                         if (pt == "above"){
48321                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48322                                 targetElClass = "x-view-drag-insert-above";
48323                         } else {
48324                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48325                                 targetElClass = "x-view-drag-insert-below";
48326                         }
48327                         if (this.lastInsertClass != targetElClass){
48328                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48329                                 this.lastInsertClass = targetElClass;
48330                         }
48331                 }
48332                 return dragElClass;
48333         },
48334
48335     onNodeOut : function(n, dd, e, data){
48336                 this.removeDropIndicators(n);
48337     },
48338
48339     onNodeDrop : function(n, dd, e, data){
48340         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48341                 return false;
48342         }
48343         var pt = this.getDropPoint(e, n, dd);
48344                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48345                 if (pt == "below") { insertAt++; }
48346                 for (var i = 0; i < data.records.length; i++) {
48347                         var r = data.records[i];
48348                         var dup = this.store.getById(r.id);
48349                         if (dup && (dd != this.dragZone)) {
48350                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48351                         } else {
48352                                 if (data.copy) {
48353                                         this.store.insert(insertAt++, r.copy());
48354                                 } else {
48355                                         data.source.isDirtyFlag = true;
48356                                         r.store.remove(r);
48357                                         this.store.insert(insertAt++, r);
48358                                 }
48359                                 this.isDirtyFlag = true;
48360                         }
48361                 }
48362                 this.dragZone.cachedTarget = null;
48363                 return true;
48364     },
48365
48366     removeDropIndicators : function(n){
48367                 if(n){
48368                         Roo.fly(n).removeClass([
48369                                 "x-view-drag-insert-above",
48370                                 "x-view-drag-insert-below"]);
48371                         this.lastInsertClass = "_noclass";
48372                 }
48373     },
48374
48375 /**
48376  *      Utility method. Add a delete option to the DDView's context menu.
48377  *      @param {String} imageUrl The URL of the "delete" icon image.
48378  */
48379         setDeletable: function(imageUrl) {
48380                 if (!this.singleSelect && !this.multiSelect) {
48381                         this.singleSelect = true;
48382                 }
48383                 var c = this.getContextMenu();
48384                 this.contextMenu.on("itemclick", function(item) {
48385                         switch (item.id) {
48386                                 case "delete":
48387                                         this.remove(this.getSelectedIndexes());
48388                                         break;
48389                         }
48390                 }, this);
48391                 this.contextMenu.add({
48392                         icon: imageUrl,
48393                         id: "delete",
48394                         text: 'Delete'
48395                 });
48396         },
48397         
48398 /**     Return the context menu for this DDView. */
48399         getContextMenu: function() {
48400                 if (!this.contextMenu) {
48401 //                      Create the View's context menu
48402                         this.contextMenu = new Roo.menu.Menu({
48403                                 id: this.id + "-contextmenu"
48404                         });
48405                         this.el.on("contextmenu", this.showContextMenu, this);
48406                 }
48407                 return this.contextMenu;
48408         },
48409         
48410         disableContextMenu: function() {
48411                 if (this.contextMenu) {
48412                         this.el.un("contextmenu", this.showContextMenu, this);
48413                 }
48414         },
48415
48416         showContextMenu: function(e, item) {
48417         item = this.findItemFromChild(e.getTarget());
48418                 if (item) {
48419                         e.stopEvent();
48420                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48421                         this.contextMenu.showAt(e.getXY());
48422             }
48423     },
48424
48425 /**
48426  *      Remove {@link Roo.data.Record}s at the specified indices.
48427  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48428  */
48429     remove: function(selectedIndices) {
48430                 selectedIndices = [].concat(selectedIndices);
48431                 for (var i = 0; i < selectedIndices.length; i++) {
48432                         var rec = this.store.getAt(selectedIndices[i]);
48433                         this.store.remove(rec);
48434                 }
48435     },
48436
48437 /**
48438  *      Double click fires the event, but also, if this is draggable, and there is only one other
48439  *      related DropZone, it transfers the selected node.
48440  */
48441     onDblClick : function(e){
48442         var item = this.findItemFromChild(e.getTarget());
48443         if(item){
48444             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48445                 return false;
48446             }
48447             if (this.dragGroup) {
48448                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48449                     while (targets.indexOf(this.dropZone) > -1) {
48450                             targets.remove(this.dropZone);
48451                                 }
48452                     if (targets.length == 1) {
48453                                         this.dragZone.cachedTarget = null;
48454                         var el = Roo.get(targets[0].getEl());
48455                         var box = el.getBox(true);
48456                         targets[0].onNodeDrop(el.dom, {
48457                                 target: el.dom,
48458                                 xy: [box.x, box.y + box.height - 1]
48459                         }, null, this.getDragData(e));
48460                     }
48461                 }
48462         }
48463     },
48464     
48465     handleSelection: function(e) {
48466                 this.dragZone.cachedTarget = null;
48467         var item = this.findItemFromChild(e.getTarget());
48468         if (!item) {
48469                 this.clearSelections(true);
48470                 return;
48471         }
48472                 if (item && (this.multiSelect || this.singleSelect)){
48473                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48474                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48475                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48476                                 this.unselect(item);
48477                         } else {
48478                                 this.select(item, this.multiSelect && e.ctrlKey);
48479                                 this.lastSelection = item;
48480                         }
48481                 }
48482     },
48483
48484     onItemClick : function(item, index, e){
48485                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48486                         return false;
48487                 }
48488                 return true;
48489     },
48490
48491     unselect : function(nodeInfo, suppressEvent){
48492                 var node = this.getNode(nodeInfo);
48493                 if(node && this.isSelected(node)){
48494                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48495                                 Roo.fly(node).removeClass(this.selectedClass);
48496                                 this.selections.remove(node);
48497                                 if(!suppressEvent){
48498                                         this.fireEvent("selectionchange", this, this.selections);
48499                                 }
48500                         }
48501                 }
48502     }
48503 });
48504 /*
48505  * Based on:
48506  * Ext JS Library 1.1.1
48507  * Copyright(c) 2006-2007, Ext JS, LLC.
48508  *
48509  * Originally Released Under LGPL - original licence link has changed is not relivant.
48510  *
48511  * Fork - LGPL
48512  * <script type="text/javascript">
48513  */
48514  
48515 /**
48516  * @class Roo.LayoutManager
48517  * @extends Roo.util.Observable
48518  * Base class for layout managers.
48519  */
48520 Roo.LayoutManager = function(container, config){
48521     Roo.LayoutManager.superclass.constructor.call(this);
48522     this.el = Roo.get(container);
48523     // ie scrollbar fix
48524     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48525         document.body.scroll = "no";
48526     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48527         this.el.position('relative');
48528     }
48529     this.id = this.el.id;
48530     this.el.addClass("x-layout-container");
48531     /** false to disable window resize monitoring @type Boolean */
48532     this.monitorWindowResize = true;
48533     this.regions = {};
48534     this.addEvents({
48535         /**
48536          * @event layout
48537          * Fires when a layout is performed. 
48538          * @param {Roo.LayoutManager} this
48539          */
48540         "layout" : true,
48541         /**
48542          * @event regionresized
48543          * Fires when the user resizes a region. 
48544          * @param {Roo.LayoutRegion} region The resized region
48545          * @param {Number} newSize The new size (width for east/west, height for north/south)
48546          */
48547         "regionresized" : true,
48548         /**
48549          * @event regioncollapsed
48550          * Fires when a region is collapsed. 
48551          * @param {Roo.LayoutRegion} region The collapsed region
48552          */
48553         "regioncollapsed" : true,
48554         /**
48555          * @event regionexpanded
48556          * Fires when a region is expanded.  
48557          * @param {Roo.LayoutRegion} region The expanded region
48558          */
48559         "regionexpanded" : true
48560     });
48561     this.updating = false;
48562     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48563 };
48564
48565 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48566     /**
48567      * Returns true if this layout is currently being updated
48568      * @return {Boolean}
48569      */
48570     isUpdating : function(){
48571         return this.updating; 
48572     },
48573     
48574     /**
48575      * Suspend the LayoutManager from doing auto-layouts while
48576      * making multiple add or remove calls
48577      */
48578     beginUpdate : function(){
48579         this.updating = true;    
48580     },
48581     
48582     /**
48583      * Restore auto-layouts and optionally disable the manager from performing a layout
48584      * @param {Boolean} noLayout true to disable a layout update 
48585      */
48586     endUpdate : function(noLayout){
48587         this.updating = false;
48588         if(!noLayout){
48589             this.layout();
48590         }    
48591     },
48592     
48593     layout: function(){
48594         
48595     },
48596     
48597     onRegionResized : function(region, newSize){
48598         this.fireEvent("regionresized", region, newSize);
48599         this.layout();
48600     },
48601     
48602     onRegionCollapsed : function(region){
48603         this.fireEvent("regioncollapsed", region);
48604     },
48605     
48606     onRegionExpanded : function(region){
48607         this.fireEvent("regionexpanded", region);
48608     },
48609         
48610     /**
48611      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48612      * performs box-model adjustments.
48613      * @return {Object} The size as an object {width: (the width), height: (the height)}
48614      */
48615     getViewSize : function(){
48616         var size;
48617         if(this.el.dom != document.body){
48618             size = this.el.getSize();
48619         }else{
48620             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48621         }
48622         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48623         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48624         return size;
48625     },
48626     
48627     /**
48628      * Returns the Element this layout is bound to.
48629      * @return {Roo.Element}
48630      */
48631     getEl : function(){
48632         return this.el;
48633     },
48634     
48635     /**
48636      * Returns the specified region.
48637      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48638      * @return {Roo.LayoutRegion}
48639      */
48640     getRegion : function(target){
48641         return this.regions[target.toLowerCase()];
48642     },
48643     
48644     onWindowResize : function(){
48645         if(this.monitorWindowResize){
48646             this.layout();
48647         }
48648     }
48649 });/*
48650  * Based on:
48651  * Ext JS Library 1.1.1
48652  * Copyright(c) 2006-2007, Ext JS, LLC.
48653  *
48654  * Originally Released Under LGPL - original licence link has changed is not relivant.
48655  *
48656  * Fork - LGPL
48657  * <script type="text/javascript">
48658  */
48659 /**
48660  * @class Roo.BorderLayout
48661  * @extends Roo.LayoutManager
48662  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48663  * please see: <br><br>
48664  * <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>
48665  * <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>
48666  * Example:
48667  <pre><code>
48668  var layout = new Roo.BorderLayout(document.body, {
48669     north: {
48670         initialSize: 25,
48671         titlebar: false
48672     },
48673     west: {
48674         split:true,
48675         initialSize: 200,
48676         minSize: 175,
48677         maxSize: 400,
48678         titlebar: true,
48679         collapsible: true
48680     },
48681     east: {
48682         split:true,
48683         initialSize: 202,
48684         minSize: 175,
48685         maxSize: 400,
48686         titlebar: true,
48687         collapsible: true
48688     },
48689     south: {
48690         split:true,
48691         initialSize: 100,
48692         minSize: 100,
48693         maxSize: 200,
48694         titlebar: true,
48695         collapsible: true
48696     },
48697     center: {
48698         titlebar: true,
48699         autoScroll:true,
48700         resizeTabs: true,
48701         minTabWidth: 50,
48702         preferredTabWidth: 150
48703     }
48704 });
48705
48706 // shorthand
48707 var CP = Roo.ContentPanel;
48708
48709 layout.beginUpdate();
48710 layout.add("north", new CP("north", "North"));
48711 layout.add("south", new CP("south", {title: "South", closable: true}));
48712 layout.add("west", new CP("west", {title: "West"}));
48713 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48714 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48715 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48716 layout.getRegion("center").showPanel("center1");
48717 layout.endUpdate();
48718 </code></pre>
48719
48720 <b>The container the layout is rendered into can be either the body element or any other element.
48721 If it is not the body element, the container needs to either be an absolute positioned element,
48722 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48723 the container size if it is not the body element.</b>
48724
48725 * @constructor
48726 * Create a new BorderLayout
48727 * @param {String/HTMLElement/Element} container The container this layout is bound to
48728 * @param {Object} config Configuration options
48729  */
48730 Roo.BorderLayout = function(container, config){
48731     config = config || {};
48732     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48733     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48734     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48735         var target = this.factory.validRegions[i];
48736         if(config[target]){
48737             this.addRegion(target, config[target]);
48738         }
48739     }
48740 };
48741
48742 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48743     /**
48744      * Creates and adds a new region if it doesn't already exist.
48745      * @param {String} target The target region key (north, south, east, west or center).
48746      * @param {Object} config The regions config object
48747      * @return {BorderLayoutRegion} The new region
48748      */
48749     addRegion : function(target, config){
48750         if(!this.regions[target]){
48751             var r = this.factory.create(target, this, config);
48752             this.bindRegion(target, r);
48753         }
48754         return this.regions[target];
48755     },
48756
48757     // private (kinda)
48758     bindRegion : function(name, r){
48759         this.regions[name] = r;
48760         r.on("visibilitychange", this.layout, this);
48761         r.on("paneladded", this.layout, this);
48762         r.on("panelremoved", this.layout, this);
48763         r.on("invalidated", this.layout, this);
48764         r.on("resized", this.onRegionResized, this);
48765         r.on("collapsed", this.onRegionCollapsed, this);
48766         r.on("expanded", this.onRegionExpanded, this);
48767     },
48768
48769     /**
48770      * Performs a layout update.
48771      */
48772     layout : function(){
48773         if(this.updating) return;
48774         var size = this.getViewSize();
48775         var w = size.width;
48776         var h = size.height;
48777         var centerW = w;
48778         var centerH = h;
48779         var centerY = 0;
48780         var centerX = 0;
48781         //var x = 0, y = 0;
48782
48783         var rs = this.regions;
48784         var north = rs["north"];
48785         var south = rs["south"]; 
48786         var west = rs["west"];
48787         var east = rs["east"];
48788         var center = rs["center"];
48789         //if(this.hideOnLayout){ // not supported anymore
48790             //c.el.setStyle("display", "none");
48791         //}
48792         if(north && north.isVisible()){
48793             var b = north.getBox();
48794             var m = north.getMargins();
48795             b.width = w - (m.left+m.right);
48796             b.x = m.left;
48797             b.y = m.top;
48798             centerY = b.height + b.y + m.bottom;
48799             centerH -= centerY;
48800             north.updateBox(this.safeBox(b));
48801         }
48802         if(south && south.isVisible()){
48803             var b = south.getBox();
48804             var m = south.getMargins();
48805             b.width = w - (m.left+m.right);
48806             b.x = m.left;
48807             var totalHeight = (b.height + m.top + m.bottom);
48808             b.y = h - totalHeight + m.top;
48809             centerH -= totalHeight;
48810             south.updateBox(this.safeBox(b));
48811         }
48812         if(west && west.isVisible()){
48813             var b = west.getBox();
48814             var m = west.getMargins();
48815             b.height = centerH - (m.top+m.bottom);
48816             b.x = m.left;
48817             b.y = centerY + m.top;
48818             var totalWidth = (b.width + m.left + m.right);
48819             centerX += totalWidth;
48820             centerW -= totalWidth;
48821             west.updateBox(this.safeBox(b));
48822         }
48823         if(east && east.isVisible()){
48824             var b = east.getBox();
48825             var m = east.getMargins();
48826             b.height = centerH - (m.top+m.bottom);
48827             var totalWidth = (b.width + m.left + m.right);
48828             b.x = w - totalWidth + m.left;
48829             b.y = centerY + m.top;
48830             centerW -= totalWidth;
48831             east.updateBox(this.safeBox(b));
48832         }
48833         if(center){
48834             var m = center.getMargins();
48835             var centerBox = {
48836                 x: centerX + m.left,
48837                 y: centerY + m.top,
48838                 width: centerW - (m.left+m.right),
48839                 height: centerH - (m.top+m.bottom)
48840             };
48841             //if(this.hideOnLayout){
48842                 //center.el.setStyle("display", "block");
48843             //}
48844             center.updateBox(this.safeBox(centerBox));
48845         }
48846         this.el.repaint();
48847         this.fireEvent("layout", this);
48848     },
48849
48850     // private
48851     safeBox : function(box){
48852         box.width = Math.max(0, box.width);
48853         box.height = Math.max(0, box.height);
48854         return box;
48855     },
48856
48857     /**
48858      * Adds a ContentPanel (or subclass) to this layout.
48859      * @param {String} target The target region key (north, south, east, west or center).
48860      * @param {Roo.ContentPanel} panel The panel to add
48861      * @return {Roo.ContentPanel} The added panel
48862      */
48863     add : function(target, panel){
48864          
48865         target = target.toLowerCase();
48866         return this.regions[target].add(panel);
48867     },
48868
48869     /**
48870      * Remove a ContentPanel (or subclass) to this layout.
48871      * @param {String} target The target region key (north, south, east, west or center).
48872      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48873      * @return {Roo.ContentPanel} The removed panel
48874      */
48875     remove : function(target, panel){
48876         target = target.toLowerCase();
48877         return this.regions[target].remove(panel);
48878     },
48879
48880     /**
48881      * Searches all regions for a panel with the specified id
48882      * @param {String} panelId
48883      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48884      */
48885     findPanel : function(panelId){
48886         var rs = this.regions;
48887         for(var target in rs){
48888             if(typeof rs[target] != "function"){
48889                 var p = rs[target].getPanel(panelId);
48890                 if(p){
48891                     return p;
48892                 }
48893             }
48894         }
48895         return null;
48896     },
48897
48898     /**
48899      * Searches all regions for a panel with the specified id and activates (shows) it.
48900      * @param {String/ContentPanel} panelId The panels id or the panel itself
48901      * @return {Roo.ContentPanel} The shown panel or null
48902      */
48903     showPanel : function(panelId) {
48904       var rs = this.regions;
48905       for(var target in rs){
48906          var r = rs[target];
48907          if(typeof r != "function"){
48908             if(r.hasPanel(panelId)){
48909                return r.showPanel(panelId);
48910             }
48911          }
48912       }
48913       return null;
48914    },
48915
48916    /**
48917      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48918      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48919      */
48920     restoreState : function(provider){
48921         if(!provider){
48922             provider = Roo.state.Manager;
48923         }
48924         var sm = new Roo.LayoutStateManager();
48925         sm.init(this, provider);
48926     },
48927
48928     /**
48929      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48930      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48931      * a valid ContentPanel config object.  Example:
48932      * <pre><code>
48933 // Create the main layout
48934 var layout = new Roo.BorderLayout('main-ct', {
48935     west: {
48936         split:true,
48937         minSize: 175,
48938         titlebar: true
48939     },
48940     center: {
48941         title:'Components'
48942     }
48943 }, 'main-ct');
48944
48945 // Create and add multiple ContentPanels at once via configs
48946 layout.batchAdd({
48947    west: {
48948        id: 'source-files',
48949        autoCreate:true,
48950        title:'Ext Source Files',
48951        autoScroll:true,
48952        fitToFrame:true
48953    },
48954    center : {
48955        el: cview,
48956        autoScroll:true,
48957        fitToFrame:true,
48958        toolbar: tb,
48959        resizeEl:'cbody'
48960    }
48961 });
48962 </code></pre>
48963      * @param {Object} regions An object containing ContentPanel configs by region name
48964      */
48965     batchAdd : function(regions){
48966         this.beginUpdate();
48967         for(var rname in regions){
48968             var lr = this.regions[rname];
48969             if(lr){
48970                 this.addTypedPanels(lr, regions[rname]);
48971             }
48972         }
48973         this.endUpdate();
48974     },
48975
48976     // private
48977     addTypedPanels : function(lr, ps){
48978         if(typeof ps == 'string'){
48979             lr.add(new Roo.ContentPanel(ps));
48980         }
48981         else if(ps instanceof Array){
48982             for(var i =0, len = ps.length; i < len; i++){
48983                 this.addTypedPanels(lr, ps[i]);
48984             }
48985         }
48986         else if(!ps.events){ // raw config?
48987             var el = ps.el;
48988             delete ps.el; // prevent conflict
48989             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48990         }
48991         else {  // panel object assumed!
48992             lr.add(ps);
48993         }
48994     },
48995     /**
48996      * Adds a xtype elements to the layout.
48997      * <pre><code>
48998
48999 layout.addxtype({
49000        xtype : 'ContentPanel',
49001        region: 'west',
49002        items: [ .... ]
49003    }
49004 );
49005
49006 layout.addxtype({
49007         xtype : 'NestedLayoutPanel',
49008         region: 'west',
49009         layout: {
49010            center: { },
49011            west: { }   
49012         },
49013         items : [ ... list of content panels or nested layout panels.. ]
49014    }
49015 );
49016 </code></pre>
49017      * @param {Object} cfg Xtype definition of item to add.
49018      */
49019     addxtype : function(cfg)
49020     {
49021         // basically accepts a pannel...
49022         // can accept a layout region..!?!?
49023         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49024         
49025         if (!cfg.xtype.match(/Panel$/)) {
49026             return false;
49027         }
49028         var ret = false;
49029         
49030         if (typeof(cfg.region) == 'undefined') {
49031             Roo.log("Failed to add Panel, region was not set");
49032             Roo.log(cfg);
49033             return false;
49034         }
49035         var region = cfg.region;
49036         delete cfg.region;
49037         
49038           
49039         var xitems = [];
49040         if (cfg.items) {
49041             xitems = cfg.items;
49042             delete cfg.items;
49043         }
49044         var nb = false;
49045         
49046         switch(cfg.xtype) 
49047         {
49048             case 'ContentPanel':  // ContentPanel (el, cfg)
49049             case 'ScrollPanel':  // ContentPanel (el, cfg)
49050             case 'ViewPanel': 
49051                 if(cfg.autoCreate) {
49052                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49053                 } else {
49054                     var el = this.el.createChild();
49055                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49056                 }
49057                 
49058                 this.add(region, ret);
49059                 break;
49060             
49061             
49062             case 'TreePanel': // our new panel!
49063                 cfg.el = this.el.createChild();
49064                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49065                 this.add(region, ret);
49066                 break;
49067             
49068             case 'NestedLayoutPanel': 
49069                 // create a new Layout (which is  a Border Layout...
49070                 var el = this.el.createChild();
49071                 var clayout = cfg.layout;
49072                 delete cfg.layout;
49073                 clayout.items   = clayout.items  || [];
49074                 // replace this exitems with the clayout ones..
49075                 xitems = clayout.items;
49076                  
49077                 
49078                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49079                     cfg.background = false;
49080                 }
49081                 var layout = new Roo.BorderLayout(el, clayout);
49082                 
49083                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49084                 //console.log('adding nested layout panel '  + cfg.toSource());
49085                 this.add(region, ret);
49086                 nb = {}; /// find first...
49087                 break;
49088                 
49089             case 'GridPanel': 
49090             
49091                 // needs grid and region
49092                 
49093                 //var el = this.getRegion(region).el.createChild();
49094                 var el = this.el.createChild();
49095                 // create the grid first...
49096                 
49097                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49098                 delete cfg.grid;
49099                 if (region == 'center' && this.active ) {
49100                     cfg.background = false;
49101                 }
49102                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49103                 
49104                 this.add(region, ret);
49105                 if (cfg.background) {
49106                     ret.on('activate', function(gp) {
49107                         if (!gp.grid.rendered) {
49108                             gp.grid.render();
49109                         }
49110                     });
49111                 } else {
49112                     grid.render();
49113                 }
49114                 break;
49115            
49116            
49117            
49118                 
49119                 
49120                 
49121             default:
49122                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49123                     
49124                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49125                     this.add(region, ret);
49126                 } else {
49127                 
49128                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49129                     return null;
49130                 }
49131                 
49132              // GridPanel (grid, cfg)
49133             
49134         }
49135         this.beginUpdate();
49136         // add children..
49137         var region = '';
49138         var abn = {};
49139         Roo.each(xitems, function(i)  {
49140             region = nb && i.region ? i.region : false;
49141             
49142             var add = ret.addxtype(i);
49143            
49144             if (region) {
49145                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49146                 if (!i.background) {
49147                     abn[region] = nb[region] ;
49148                 }
49149             }
49150             
49151         });
49152         this.endUpdate();
49153
49154         // make the last non-background panel active..
49155         //if (nb) { Roo.log(abn); }
49156         if (nb) {
49157             
49158             for(var r in abn) {
49159                 region = this.getRegion(r);
49160                 if (region) {
49161                     // tried using nb[r], but it does not work..
49162                      
49163                     region.showPanel(abn[r]);
49164                    
49165                 }
49166             }
49167         }
49168         return ret;
49169         
49170     }
49171 });
49172
49173 /**
49174  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49175  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49176  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49177  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49178  * <pre><code>
49179 // shorthand
49180 var CP = Roo.ContentPanel;
49181
49182 var layout = Roo.BorderLayout.create({
49183     north: {
49184         initialSize: 25,
49185         titlebar: false,
49186         panels: [new CP("north", "North")]
49187     },
49188     west: {
49189         split:true,
49190         initialSize: 200,
49191         minSize: 175,
49192         maxSize: 400,
49193         titlebar: true,
49194         collapsible: true,
49195         panels: [new CP("west", {title: "West"})]
49196     },
49197     east: {
49198         split:true,
49199         initialSize: 202,
49200         minSize: 175,
49201         maxSize: 400,
49202         titlebar: true,
49203         collapsible: true,
49204         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49205     },
49206     south: {
49207         split:true,
49208         initialSize: 100,
49209         minSize: 100,
49210         maxSize: 200,
49211         titlebar: true,
49212         collapsible: true,
49213         panels: [new CP("south", {title: "South", closable: true})]
49214     },
49215     center: {
49216         titlebar: true,
49217         autoScroll:true,
49218         resizeTabs: true,
49219         minTabWidth: 50,
49220         preferredTabWidth: 150,
49221         panels: [
49222             new CP("center1", {title: "Close Me", closable: true}),
49223             new CP("center2", {title: "Center Panel", closable: false})
49224         ]
49225     }
49226 }, document.body);
49227
49228 layout.getRegion("center").showPanel("center1");
49229 </code></pre>
49230  * @param config
49231  * @param targetEl
49232  */
49233 Roo.BorderLayout.create = function(config, targetEl){
49234     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49235     layout.beginUpdate();
49236     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49237     for(var j = 0, jlen = regions.length; j < jlen; j++){
49238         var lr = regions[j];
49239         if(layout.regions[lr] && config[lr].panels){
49240             var r = layout.regions[lr];
49241             var ps = config[lr].panels;
49242             layout.addTypedPanels(r, ps);
49243         }
49244     }
49245     layout.endUpdate();
49246     return layout;
49247 };
49248
49249 // private
49250 Roo.BorderLayout.RegionFactory = {
49251     // private
49252     validRegions : ["north","south","east","west","center"],
49253
49254     // private
49255     create : function(target, mgr, config){
49256         target = target.toLowerCase();
49257         if(config.lightweight || config.basic){
49258             return new Roo.BasicLayoutRegion(mgr, config, target);
49259         }
49260         switch(target){
49261             case "north":
49262                 return new Roo.NorthLayoutRegion(mgr, config);
49263             case "south":
49264                 return new Roo.SouthLayoutRegion(mgr, config);
49265             case "east":
49266                 return new Roo.EastLayoutRegion(mgr, config);
49267             case "west":
49268                 return new Roo.WestLayoutRegion(mgr, config);
49269             case "center":
49270                 return new Roo.CenterLayoutRegion(mgr, config);
49271         }
49272         throw 'Layout region "'+target+'" not supported.';
49273     }
49274 };/*
49275  * Based on:
49276  * Ext JS Library 1.1.1
49277  * Copyright(c) 2006-2007, Ext JS, LLC.
49278  *
49279  * Originally Released Under LGPL - original licence link has changed is not relivant.
49280  *
49281  * Fork - LGPL
49282  * <script type="text/javascript">
49283  */
49284  
49285 /**
49286  * @class Roo.BasicLayoutRegion
49287  * @extends Roo.util.Observable
49288  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49289  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49290  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49291  */
49292 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49293     this.mgr = mgr;
49294     this.position  = pos;
49295     this.events = {
49296         /**
49297          * @scope Roo.BasicLayoutRegion
49298          */
49299         
49300         /**
49301          * @event beforeremove
49302          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49303          * @param {Roo.LayoutRegion} this
49304          * @param {Roo.ContentPanel} panel The panel
49305          * @param {Object} e The cancel event object
49306          */
49307         "beforeremove" : true,
49308         /**
49309          * @event invalidated
49310          * Fires when the layout for this region is changed.
49311          * @param {Roo.LayoutRegion} this
49312          */
49313         "invalidated" : true,
49314         /**
49315          * @event visibilitychange
49316          * Fires when this region is shown or hidden 
49317          * @param {Roo.LayoutRegion} this
49318          * @param {Boolean} visibility true or false
49319          */
49320         "visibilitychange" : true,
49321         /**
49322          * @event paneladded
49323          * Fires when a panel is added. 
49324          * @param {Roo.LayoutRegion} this
49325          * @param {Roo.ContentPanel} panel The panel
49326          */
49327         "paneladded" : true,
49328         /**
49329          * @event panelremoved
49330          * Fires when a panel is removed. 
49331          * @param {Roo.LayoutRegion} this
49332          * @param {Roo.ContentPanel} panel The panel
49333          */
49334         "panelremoved" : true,
49335         /**
49336          * @event collapsed
49337          * Fires when this region is collapsed.
49338          * @param {Roo.LayoutRegion} this
49339          */
49340         "collapsed" : true,
49341         /**
49342          * @event expanded
49343          * Fires when this region is expanded.
49344          * @param {Roo.LayoutRegion} this
49345          */
49346         "expanded" : true,
49347         /**
49348          * @event slideshow
49349          * Fires when this region is slid into view.
49350          * @param {Roo.LayoutRegion} this
49351          */
49352         "slideshow" : true,
49353         /**
49354          * @event slidehide
49355          * Fires when this region slides out of view. 
49356          * @param {Roo.LayoutRegion} this
49357          */
49358         "slidehide" : true,
49359         /**
49360          * @event panelactivated
49361          * Fires when a panel is activated. 
49362          * @param {Roo.LayoutRegion} this
49363          * @param {Roo.ContentPanel} panel The activated panel
49364          */
49365         "panelactivated" : true,
49366         /**
49367          * @event resized
49368          * Fires when the user resizes this region. 
49369          * @param {Roo.LayoutRegion} this
49370          * @param {Number} newSize The new size (width for east/west, height for north/south)
49371          */
49372         "resized" : true
49373     };
49374     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49375     this.panels = new Roo.util.MixedCollection();
49376     this.panels.getKey = this.getPanelId.createDelegate(this);
49377     this.box = null;
49378     this.activePanel = null;
49379     // ensure listeners are added...
49380     
49381     if (config.listeners || config.events) {
49382         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49383             listeners : config.listeners || {},
49384             events : config.events || {}
49385         });
49386     }
49387     
49388     if(skipConfig !== true){
49389         this.applyConfig(config);
49390     }
49391 };
49392
49393 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49394     getPanelId : function(p){
49395         return p.getId();
49396     },
49397     
49398     applyConfig : function(config){
49399         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49400         this.config = config;
49401         
49402     },
49403     
49404     /**
49405      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49406      * the width, for horizontal (north, south) the height.
49407      * @param {Number} newSize The new width or height
49408      */
49409     resizeTo : function(newSize){
49410         var el = this.el ? this.el :
49411                  (this.activePanel ? this.activePanel.getEl() : null);
49412         if(el){
49413             switch(this.position){
49414                 case "east":
49415                 case "west":
49416                     el.setWidth(newSize);
49417                     this.fireEvent("resized", this, newSize);
49418                 break;
49419                 case "north":
49420                 case "south":
49421                     el.setHeight(newSize);
49422                     this.fireEvent("resized", this, newSize);
49423                 break;                
49424             }
49425         }
49426     },
49427     
49428     getBox : function(){
49429         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49430     },
49431     
49432     getMargins : function(){
49433         return this.margins;
49434     },
49435     
49436     updateBox : function(box){
49437         this.box = box;
49438         var el = this.activePanel.getEl();
49439         el.dom.style.left = box.x + "px";
49440         el.dom.style.top = box.y + "px";
49441         this.activePanel.setSize(box.width, box.height);
49442     },
49443     
49444     /**
49445      * Returns the container element for this region.
49446      * @return {Roo.Element}
49447      */
49448     getEl : function(){
49449         return this.activePanel;
49450     },
49451     
49452     /**
49453      * Returns true if this region is currently visible.
49454      * @return {Boolean}
49455      */
49456     isVisible : function(){
49457         return this.activePanel ? true : false;
49458     },
49459     
49460     setActivePanel : function(panel){
49461         panel = this.getPanel(panel);
49462         if(this.activePanel && this.activePanel != panel){
49463             this.activePanel.setActiveState(false);
49464             this.activePanel.getEl().setLeftTop(-10000,-10000);
49465         }
49466         this.activePanel = panel;
49467         panel.setActiveState(true);
49468         if(this.box){
49469             panel.setSize(this.box.width, this.box.height);
49470         }
49471         this.fireEvent("panelactivated", this, panel);
49472         this.fireEvent("invalidated");
49473     },
49474     
49475     /**
49476      * Show the specified panel.
49477      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49478      * @return {Roo.ContentPanel} The shown panel or null
49479      */
49480     showPanel : function(panel){
49481         if(panel = this.getPanel(panel)){
49482             this.setActivePanel(panel);
49483         }
49484         return panel;
49485     },
49486     
49487     /**
49488      * Get the active panel for this region.
49489      * @return {Roo.ContentPanel} The active panel or null
49490      */
49491     getActivePanel : function(){
49492         return this.activePanel;
49493     },
49494     
49495     /**
49496      * Add the passed ContentPanel(s)
49497      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49498      * @return {Roo.ContentPanel} The panel added (if only one was added)
49499      */
49500     add : function(panel){
49501         if(arguments.length > 1){
49502             for(var i = 0, len = arguments.length; i < len; i++) {
49503                 this.add(arguments[i]);
49504             }
49505             return null;
49506         }
49507         if(this.hasPanel(panel)){
49508             this.showPanel(panel);
49509             return panel;
49510         }
49511         var el = panel.getEl();
49512         if(el.dom.parentNode != this.mgr.el.dom){
49513             this.mgr.el.dom.appendChild(el.dom);
49514         }
49515         if(panel.setRegion){
49516             panel.setRegion(this);
49517         }
49518         this.panels.add(panel);
49519         el.setStyle("position", "absolute");
49520         if(!panel.background){
49521             this.setActivePanel(panel);
49522             if(this.config.initialSize && this.panels.getCount()==1){
49523                 this.resizeTo(this.config.initialSize);
49524             }
49525         }
49526         this.fireEvent("paneladded", this, panel);
49527         return panel;
49528     },
49529     
49530     /**
49531      * Returns true if the panel is in this region.
49532      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49533      * @return {Boolean}
49534      */
49535     hasPanel : function(panel){
49536         if(typeof panel == "object"){ // must be panel obj
49537             panel = panel.getId();
49538         }
49539         return this.getPanel(panel) ? true : false;
49540     },
49541     
49542     /**
49543      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49544      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49545      * @param {Boolean} preservePanel Overrides the config preservePanel option
49546      * @return {Roo.ContentPanel} The panel that was removed
49547      */
49548     remove : function(panel, preservePanel){
49549         panel = this.getPanel(panel);
49550         if(!panel){
49551             return null;
49552         }
49553         var e = {};
49554         this.fireEvent("beforeremove", this, panel, e);
49555         if(e.cancel === true){
49556             return null;
49557         }
49558         var panelId = panel.getId();
49559         this.panels.removeKey(panelId);
49560         return panel;
49561     },
49562     
49563     /**
49564      * Returns the panel specified or null if it's not in this region.
49565      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49566      * @return {Roo.ContentPanel}
49567      */
49568     getPanel : function(id){
49569         if(typeof id == "object"){ // must be panel obj
49570             return id;
49571         }
49572         return this.panels.get(id);
49573     },
49574     
49575     /**
49576      * Returns this regions position (north/south/east/west/center).
49577      * @return {String} 
49578      */
49579     getPosition: function(){
49580         return this.position;    
49581     }
49582 });/*
49583  * Based on:
49584  * Ext JS Library 1.1.1
49585  * Copyright(c) 2006-2007, Ext JS, LLC.
49586  *
49587  * Originally Released Under LGPL - original licence link has changed is not relivant.
49588  *
49589  * Fork - LGPL
49590  * <script type="text/javascript">
49591  */
49592  
49593 /**
49594  * @class Roo.LayoutRegion
49595  * @extends Roo.BasicLayoutRegion
49596  * This class represents a region in a layout manager.
49597  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49598  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49599  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49600  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49601  * @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})
49602  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49603  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49604  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49605  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49606  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49607  * @cfg {String}    title           The title for the region (overrides panel titles)
49608  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49609  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49610  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49611  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49612  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49613  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49614  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49615  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49616  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49617  * @cfg {Boolean}   showPin         True to show a pin button
49618  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49619  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49620  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49621  * @cfg {Number}    width           For East/West panels
49622  * @cfg {Number}    height          For North/South panels
49623  * @cfg {Boolean}   split           To show the splitter
49624  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49625  */
49626 Roo.LayoutRegion = function(mgr, config, pos){
49627     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49628     var dh = Roo.DomHelper;
49629     /** This region's container element 
49630     * @type Roo.Element */
49631     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49632     /** This region's title element 
49633     * @type Roo.Element */
49634
49635     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49636         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49637         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49638     ]}, true);
49639     this.titleEl.enableDisplayMode();
49640     /** This region's title text element 
49641     * @type HTMLElement */
49642     this.titleTextEl = this.titleEl.dom.firstChild;
49643     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49644     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49645     this.closeBtn.enableDisplayMode();
49646     this.closeBtn.on("click", this.closeClicked, this);
49647     this.closeBtn.hide();
49648
49649     this.createBody(config);
49650     this.visible = true;
49651     this.collapsed = false;
49652
49653     if(config.hideWhenEmpty){
49654         this.hide();
49655         this.on("paneladded", this.validateVisibility, this);
49656         this.on("panelremoved", this.validateVisibility, this);
49657     }
49658     this.applyConfig(config);
49659 };
49660
49661 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49662
49663     createBody : function(){
49664         /** This region's body element 
49665         * @type Roo.Element */
49666         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49667     },
49668
49669     applyConfig : function(c){
49670         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49671             var dh = Roo.DomHelper;
49672             if(c.titlebar !== false){
49673                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49674                 this.collapseBtn.on("click", this.collapse, this);
49675                 this.collapseBtn.enableDisplayMode();
49676
49677                 if(c.showPin === true || this.showPin){
49678                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49679                     this.stickBtn.enableDisplayMode();
49680                     this.stickBtn.on("click", this.expand, this);
49681                     this.stickBtn.hide();
49682                 }
49683             }
49684             /** This region's collapsed element
49685             * @type Roo.Element */
49686             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49687                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49688             ]}, true);
49689             if(c.floatable !== false){
49690                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49691                this.collapsedEl.on("click", this.collapseClick, this);
49692             }
49693
49694             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49695                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49696                    id: "message", unselectable: "on", style:{"float":"left"}});
49697                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49698              }
49699             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49700             this.expandBtn.on("click", this.expand, this);
49701         }
49702         if(this.collapseBtn){
49703             this.collapseBtn.setVisible(c.collapsible == true);
49704         }
49705         this.cmargins = c.cmargins || this.cmargins ||
49706                          (this.position == "west" || this.position == "east" ?
49707                              {top: 0, left: 2, right:2, bottom: 0} :
49708                              {top: 2, left: 0, right:0, bottom: 2});
49709         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49710         this.bottomTabs = c.tabPosition != "top";
49711         this.autoScroll = c.autoScroll || false;
49712         if(this.autoScroll){
49713             this.bodyEl.setStyle("overflow", "auto");
49714         }else{
49715             this.bodyEl.setStyle("overflow", "hidden");
49716         }
49717         //if(c.titlebar !== false){
49718             if((!c.titlebar && !c.title) || c.titlebar === false){
49719                 this.titleEl.hide();
49720             }else{
49721                 this.titleEl.show();
49722                 if(c.title){
49723                     this.titleTextEl.innerHTML = c.title;
49724                 }
49725             }
49726         //}
49727         this.duration = c.duration || .30;
49728         this.slideDuration = c.slideDuration || .45;
49729         this.config = c;
49730         if(c.collapsed){
49731             this.collapse(true);
49732         }
49733         if(c.hidden){
49734             this.hide();
49735         }
49736     },
49737     /**
49738      * Returns true if this region is currently visible.
49739      * @return {Boolean}
49740      */
49741     isVisible : function(){
49742         return this.visible;
49743     },
49744
49745     /**
49746      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49747      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49748      */
49749     setCollapsedTitle : function(title){
49750         title = title || "&#160;";
49751         if(this.collapsedTitleTextEl){
49752             this.collapsedTitleTextEl.innerHTML = title;
49753         }
49754     },
49755
49756     getBox : function(){
49757         var b;
49758         if(!this.collapsed){
49759             b = this.el.getBox(false, true);
49760         }else{
49761             b = this.collapsedEl.getBox(false, true);
49762         }
49763         return b;
49764     },
49765
49766     getMargins : function(){
49767         return this.collapsed ? this.cmargins : this.margins;
49768     },
49769
49770     highlight : function(){
49771         this.el.addClass("x-layout-panel-dragover");
49772     },
49773
49774     unhighlight : function(){
49775         this.el.removeClass("x-layout-panel-dragover");
49776     },
49777
49778     updateBox : function(box){
49779         this.box = box;
49780         if(!this.collapsed){
49781             this.el.dom.style.left = box.x + "px";
49782             this.el.dom.style.top = box.y + "px";
49783             this.updateBody(box.width, box.height);
49784         }else{
49785             this.collapsedEl.dom.style.left = box.x + "px";
49786             this.collapsedEl.dom.style.top = box.y + "px";
49787             this.collapsedEl.setSize(box.width, box.height);
49788         }
49789         if(this.tabs){
49790             this.tabs.autoSizeTabs();
49791         }
49792     },
49793
49794     updateBody : function(w, h){
49795         if(w !== null){
49796             this.el.setWidth(w);
49797             w -= this.el.getBorderWidth("rl");
49798             if(this.config.adjustments){
49799                 w += this.config.adjustments[0];
49800             }
49801         }
49802         if(h !== null){
49803             this.el.setHeight(h);
49804             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49805             h -= this.el.getBorderWidth("tb");
49806             if(this.config.adjustments){
49807                 h += this.config.adjustments[1];
49808             }
49809             this.bodyEl.setHeight(h);
49810             if(this.tabs){
49811                 h = this.tabs.syncHeight(h);
49812             }
49813         }
49814         if(this.panelSize){
49815             w = w !== null ? w : this.panelSize.width;
49816             h = h !== null ? h : this.panelSize.height;
49817         }
49818         if(this.activePanel){
49819             var el = this.activePanel.getEl();
49820             w = w !== null ? w : el.getWidth();
49821             h = h !== null ? h : el.getHeight();
49822             this.panelSize = {width: w, height: h};
49823             this.activePanel.setSize(w, h);
49824         }
49825         if(Roo.isIE && this.tabs){
49826             this.tabs.el.repaint();
49827         }
49828     },
49829
49830     /**
49831      * Returns the container element for this region.
49832      * @return {Roo.Element}
49833      */
49834     getEl : function(){
49835         return this.el;
49836     },
49837
49838     /**
49839      * Hides this region.
49840      */
49841     hide : function(){
49842         if(!this.collapsed){
49843             this.el.dom.style.left = "-2000px";
49844             this.el.hide();
49845         }else{
49846             this.collapsedEl.dom.style.left = "-2000px";
49847             this.collapsedEl.hide();
49848         }
49849         this.visible = false;
49850         this.fireEvent("visibilitychange", this, false);
49851     },
49852
49853     /**
49854      * Shows this region if it was previously hidden.
49855      */
49856     show : function(){
49857         if(!this.collapsed){
49858             this.el.show();
49859         }else{
49860             this.collapsedEl.show();
49861         }
49862         this.visible = true;
49863         this.fireEvent("visibilitychange", this, true);
49864     },
49865
49866     closeClicked : function(){
49867         if(this.activePanel){
49868             this.remove(this.activePanel);
49869         }
49870     },
49871
49872     collapseClick : function(e){
49873         if(this.isSlid){
49874            e.stopPropagation();
49875            this.slideIn();
49876         }else{
49877            e.stopPropagation();
49878            this.slideOut();
49879         }
49880     },
49881
49882     /**
49883      * Collapses this region.
49884      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49885      */
49886     collapse : function(skipAnim){
49887         if(this.collapsed) return;
49888         this.collapsed = true;
49889         if(this.split){
49890             this.split.el.hide();
49891         }
49892         if(this.config.animate && skipAnim !== true){
49893             this.fireEvent("invalidated", this);
49894             this.animateCollapse();
49895         }else{
49896             this.el.setLocation(-20000,-20000);
49897             this.el.hide();
49898             this.collapsedEl.show();
49899             this.fireEvent("collapsed", this);
49900             this.fireEvent("invalidated", this);
49901         }
49902     },
49903
49904     animateCollapse : function(){
49905         // overridden
49906     },
49907
49908     /**
49909      * Expands this region if it was previously collapsed.
49910      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49911      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49912      */
49913     expand : function(e, skipAnim){
49914         if(e) e.stopPropagation();
49915         if(!this.collapsed || this.el.hasActiveFx()) return;
49916         if(this.isSlid){
49917             this.afterSlideIn();
49918             skipAnim = true;
49919         }
49920         this.collapsed = false;
49921         if(this.config.animate && skipAnim !== true){
49922             this.animateExpand();
49923         }else{
49924             this.el.show();
49925             if(this.split){
49926                 this.split.el.show();
49927             }
49928             this.collapsedEl.setLocation(-2000,-2000);
49929             this.collapsedEl.hide();
49930             this.fireEvent("invalidated", this);
49931             this.fireEvent("expanded", this);
49932         }
49933     },
49934
49935     animateExpand : function(){
49936         // overridden
49937     },
49938
49939     initTabs : function()
49940     {
49941         this.bodyEl.setStyle("overflow", "hidden");
49942         var ts = new Roo.TabPanel(
49943                 this.bodyEl.dom,
49944                 {
49945                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49946                     disableTooltips: this.config.disableTabTips,
49947                     toolbar : this.config.toolbar
49948                 }
49949         );
49950         if(this.config.hideTabs){
49951             ts.stripWrap.setDisplayed(false);
49952         }
49953         this.tabs = ts;
49954         ts.resizeTabs = this.config.resizeTabs === true;
49955         ts.minTabWidth = this.config.minTabWidth || 40;
49956         ts.maxTabWidth = this.config.maxTabWidth || 250;
49957         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49958         ts.monitorResize = false;
49959         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49960         ts.bodyEl.addClass('x-layout-tabs-body');
49961         this.panels.each(this.initPanelAsTab, this);
49962     },
49963
49964     initPanelAsTab : function(panel){
49965         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49966                     this.config.closeOnTab && panel.isClosable());
49967         if(panel.tabTip !== undefined){
49968             ti.setTooltip(panel.tabTip);
49969         }
49970         ti.on("activate", function(){
49971               this.setActivePanel(panel);
49972         }, this);
49973         if(this.config.closeOnTab){
49974             ti.on("beforeclose", function(t, e){
49975                 e.cancel = true;
49976                 this.remove(panel);
49977             }, this);
49978         }
49979         return ti;
49980     },
49981
49982     updatePanelTitle : function(panel, title){
49983         if(this.activePanel == panel){
49984             this.updateTitle(title);
49985         }
49986         if(this.tabs){
49987             var ti = this.tabs.getTab(panel.getEl().id);
49988             ti.setText(title);
49989             if(panel.tabTip !== undefined){
49990                 ti.setTooltip(panel.tabTip);
49991             }
49992         }
49993     },
49994
49995     updateTitle : function(title){
49996         if(this.titleTextEl && !this.config.title){
49997             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49998         }
49999     },
50000
50001     setActivePanel : function(panel){
50002         panel = this.getPanel(panel);
50003         if(this.activePanel && this.activePanel != panel){
50004             this.activePanel.setActiveState(false);
50005         }
50006         this.activePanel = panel;
50007         panel.setActiveState(true);
50008         if(this.panelSize){
50009             panel.setSize(this.panelSize.width, this.panelSize.height);
50010         }
50011         if(this.closeBtn){
50012             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50013         }
50014         this.updateTitle(panel.getTitle());
50015         if(this.tabs){
50016             this.fireEvent("invalidated", this);
50017         }
50018         this.fireEvent("panelactivated", this, panel);
50019     },
50020
50021     /**
50022      * Shows the specified panel.
50023      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50024      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50025      */
50026     showPanel : function(panel){
50027         if(panel = this.getPanel(panel)){
50028             if(this.tabs){
50029                 var tab = this.tabs.getTab(panel.getEl().id);
50030                 if(tab.isHidden()){
50031                     this.tabs.unhideTab(tab.id);
50032                 }
50033                 tab.activate();
50034             }else{
50035                 this.setActivePanel(panel);
50036             }
50037         }
50038         return panel;
50039     },
50040
50041     /**
50042      * Get the active panel for this region.
50043      * @return {Roo.ContentPanel} The active panel or null
50044      */
50045     getActivePanel : function(){
50046         return this.activePanel;
50047     },
50048
50049     validateVisibility : function(){
50050         if(this.panels.getCount() < 1){
50051             this.updateTitle("&#160;");
50052             this.closeBtn.hide();
50053             this.hide();
50054         }else{
50055             if(!this.isVisible()){
50056                 this.show();
50057             }
50058         }
50059     },
50060
50061     /**
50062      * Adds the passed ContentPanel(s) to this region.
50063      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50064      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50065      */
50066     add : function(panel){
50067         if(arguments.length > 1){
50068             for(var i = 0, len = arguments.length; i < len; i++) {
50069                 this.add(arguments[i]);
50070             }
50071             return null;
50072         }
50073         if(this.hasPanel(panel)){
50074             this.showPanel(panel);
50075             return panel;
50076         }
50077         panel.setRegion(this);
50078         this.panels.add(panel);
50079         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50080             this.bodyEl.dom.appendChild(panel.getEl().dom);
50081             if(panel.background !== true){
50082                 this.setActivePanel(panel);
50083             }
50084             this.fireEvent("paneladded", this, panel);
50085             return panel;
50086         }
50087         if(!this.tabs){
50088             this.initTabs();
50089         }else{
50090             this.initPanelAsTab(panel);
50091         }
50092         if(panel.background !== true){
50093             this.tabs.activate(panel.getEl().id);
50094         }
50095         this.fireEvent("paneladded", this, panel);
50096         return panel;
50097     },
50098
50099     /**
50100      * Hides the tab for the specified panel.
50101      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50102      */
50103     hidePanel : function(panel){
50104         if(this.tabs && (panel = this.getPanel(panel))){
50105             this.tabs.hideTab(panel.getEl().id);
50106         }
50107     },
50108
50109     /**
50110      * Unhides the tab for a previously hidden panel.
50111      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50112      */
50113     unhidePanel : function(panel){
50114         if(this.tabs && (panel = this.getPanel(panel))){
50115             this.tabs.unhideTab(panel.getEl().id);
50116         }
50117     },
50118
50119     clearPanels : function(){
50120         while(this.panels.getCount() > 0){
50121              this.remove(this.panels.first());
50122         }
50123     },
50124
50125     /**
50126      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50127      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50128      * @param {Boolean} preservePanel Overrides the config preservePanel option
50129      * @return {Roo.ContentPanel} The panel that was removed
50130      */
50131     remove : function(panel, preservePanel){
50132         panel = this.getPanel(panel);
50133         if(!panel){
50134             return null;
50135         }
50136         var e = {};
50137         this.fireEvent("beforeremove", this, panel, e);
50138         if(e.cancel === true){
50139             return null;
50140         }
50141         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50142         var panelId = panel.getId();
50143         this.panels.removeKey(panelId);
50144         if(preservePanel){
50145             document.body.appendChild(panel.getEl().dom);
50146         }
50147         if(this.tabs){
50148             this.tabs.removeTab(panel.getEl().id);
50149         }else if (!preservePanel){
50150             this.bodyEl.dom.removeChild(panel.getEl().dom);
50151         }
50152         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50153             var p = this.panels.first();
50154             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50155             tempEl.appendChild(p.getEl().dom);
50156             this.bodyEl.update("");
50157             this.bodyEl.dom.appendChild(p.getEl().dom);
50158             tempEl = null;
50159             this.updateTitle(p.getTitle());
50160             this.tabs = null;
50161             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50162             this.setActivePanel(p);
50163         }
50164         panel.setRegion(null);
50165         if(this.activePanel == panel){
50166             this.activePanel = null;
50167         }
50168         if(this.config.autoDestroy !== false && preservePanel !== true){
50169             try{panel.destroy();}catch(e){}
50170         }
50171         this.fireEvent("panelremoved", this, panel);
50172         return panel;
50173     },
50174
50175     /**
50176      * Returns the TabPanel component used by this region
50177      * @return {Roo.TabPanel}
50178      */
50179     getTabs : function(){
50180         return this.tabs;
50181     },
50182
50183     createTool : function(parentEl, className){
50184         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50185             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50186         btn.addClassOnOver("x-layout-tools-button-over");
50187         return btn;
50188     }
50189 });/*
50190  * Based on:
50191  * Ext JS Library 1.1.1
50192  * Copyright(c) 2006-2007, Ext JS, LLC.
50193  *
50194  * Originally Released Under LGPL - original licence link has changed is not relivant.
50195  *
50196  * Fork - LGPL
50197  * <script type="text/javascript">
50198  */
50199  
50200
50201
50202 /**
50203  * @class Roo.SplitLayoutRegion
50204  * @extends Roo.LayoutRegion
50205  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50206  */
50207 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50208     this.cursor = cursor;
50209     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50210 };
50211
50212 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50213     splitTip : "Drag to resize.",
50214     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50215     useSplitTips : false,
50216
50217     applyConfig : function(config){
50218         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50219         if(config.split){
50220             if(!this.split){
50221                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50222                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50223                 /** The SplitBar for this region 
50224                 * @type Roo.SplitBar */
50225                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50226                 this.split.on("moved", this.onSplitMove, this);
50227                 this.split.useShim = config.useShim === true;
50228                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50229                 if(this.useSplitTips){
50230                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50231                 }
50232                 if(config.collapsible){
50233                     this.split.el.on("dblclick", this.collapse,  this);
50234                 }
50235             }
50236             if(typeof config.minSize != "undefined"){
50237                 this.split.minSize = config.minSize;
50238             }
50239             if(typeof config.maxSize != "undefined"){
50240                 this.split.maxSize = config.maxSize;
50241             }
50242             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50243                 this.hideSplitter();
50244             }
50245         }
50246     },
50247
50248     getHMaxSize : function(){
50249          var cmax = this.config.maxSize || 10000;
50250          var center = this.mgr.getRegion("center");
50251          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50252     },
50253
50254     getVMaxSize : function(){
50255          var cmax = this.config.maxSize || 10000;
50256          var center = this.mgr.getRegion("center");
50257          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50258     },
50259
50260     onSplitMove : function(split, newSize){
50261         this.fireEvent("resized", this, newSize);
50262     },
50263     
50264     /** 
50265      * Returns the {@link Roo.SplitBar} for this region.
50266      * @return {Roo.SplitBar}
50267      */
50268     getSplitBar : function(){
50269         return this.split;
50270     },
50271     
50272     hide : function(){
50273         this.hideSplitter();
50274         Roo.SplitLayoutRegion.superclass.hide.call(this);
50275     },
50276
50277     hideSplitter : function(){
50278         if(this.split){
50279             this.split.el.setLocation(-2000,-2000);
50280             this.split.el.hide();
50281         }
50282     },
50283
50284     show : function(){
50285         if(this.split){
50286             this.split.el.show();
50287         }
50288         Roo.SplitLayoutRegion.superclass.show.call(this);
50289     },
50290     
50291     beforeSlide: function(){
50292         if(Roo.isGecko){// firefox overflow auto bug workaround
50293             this.bodyEl.clip();
50294             if(this.tabs) this.tabs.bodyEl.clip();
50295             if(this.activePanel){
50296                 this.activePanel.getEl().clip();
50297                 
50298                 if(this.activePanel.beforeSlide){
50299                     this.activePanel.beforeSlide();
50300                 }
50301             }
50302         }
50303     },
50304     
50305     afterSlide : function(){
50306         if(Roo.isGecko){// firefox overflow auto bug workaround
50307             this.bodyEl.unclip();
50308             if(this.tabs) this.tabs.bodyEl.unclip();
50309             if(this.activePanel){
50310                 this.activePanel.getEl().unclip();
50311                 if(this.activePanel.afterSlide){
50312                     this.activePanel.afterSlide();
50313                 }
50314             }
50315         }
50316     },
50317
50318     initAutoHide : function(){
50319         if(this.autoHide !== false){
50320             if(!this.autoHideHd){
50321                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50322                 this.autoHideHd = {
50323                     "mouseout": function(e){
50324                         if(!e.within(this.el, true)){
50325                             st.delay(500);
50326                         }
50327                     },
50328                     "mouseover" : function(e){
50329                         st.cancel();
50330                     },
50331                     scope : this
50332                 };
50333             }
50334             this.el.on(this.autoHideHd);
50335         }
50336     },
50337
50338     clearAutoHide : function(){
50339         if(this.autoHide !== false){
50340             this.el.un("mouseout", this.autoHideHd.mouseout);
50341             this.el.un("mouseover", this.autoHideHd.mouseover);
50342         }
50343     },
50344
50345     clearMonitor : function(){
50346         Roo.get(document).un("click", this.slideInIf, this);
50347     },
50348
50349     // these names are backwards but not changed for compat
50350     slideOut : function(){
50351         if(this.isSlid || this.el.hasActiveFx()){
50352             return;
50353         }
50354         this.isSlid = true;
50355         if(this.collapseBtn){
50356             this.collapseBtn.hide();
50357         }
50358         this.closeBtnState = this.closeBtn.getStyle('display');
50359         this.closeBtn.hide();
50360         if(this.stickBtn){
50361             this.stickBtn.show();
50362         }
50363         this.el.show();
50364         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50365         this.beforeSlide();
50366         this.el.setStyle("z-index", 10001);
50367         this.el.slideIn(this.getSlideAnchor(), {
50368             callback: function(){
50369                 this.afterSlide();
50370                 this.initAutoHide();
50371                 Roo.get(document).on("click", this.slideInIf, this);
50372                 this.fireEvent("slideshow", this);
50373             },
50374             scope: this,
50375             block: true
50376         });
50377     },
50378
50379     afterSlideIn : function(){
50380         this.clearAutoHide();
50381         this.isSlid = false;
50382         this.clearMonitor();
50383         this.el.setStyle("z-index", "");
50384         if(this.collapseBtn){
50385             this.collapseBtn.show();
50386         }
50387         this.closeBtn.setStyle('display', this.closeBtnState);
50388         if(this.stickBtn){
50389             this.stickBtn.hide();
50390         }
50391         this.fireEvent("slidehide", this);
50392     },
50393
50394     slideIn : function(cb){
50395         if(!this.isSlid || this.el.hasActiveFx()){
50396             Roo.callback(cb);
50397             return;
50398         }
50399         this.isSlid = false;
50400         this.beforeSlide();
50401         this.el.slideOut(this.getSlideAnchor(), {
50402             callback: function(){
50403                 this.el.setLeftTop(-10000, -10000);
50404                 this.afterSlide();
50405                 this.afterSlideIn();
50406                 Roo.callback(cb);
50407             },
50408             scope: this,
50409             block: true
50410         });
50411     },
50412     
50413     slideInIf : function(e){
50414         if(!e.within(this.el)){
50415             this.slideIn();
50416         }
50417     },
50418
50419     animateCollapse : function(){
50420         this.beforeSlide();
50421         this.el.setStyle("z-index", 20000);
50422         var anchor = this.getSlideAnchor();
50423         this.el.slideOut(anchor, {
50424             callback : function(){
50425                 this.el.setStyle("z-index", "");
50426                 this.collapsedEl.slideIn(anchor, {duration:.3});
50427                 this.afterSlide();
50428                 this.el.setLocation(-10000,-10000);
50429                 this.el.hide();
50430                 this.fireEvent("collapsed", this);
50431             },
50432             scope: this,
50433             block: true
50434         });
50435     },
50436
50437     animateExpand : function(){
50438         this.beforeSlide();
50439         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50440         this.el.setStyle("z-index", 20000);
50441         this.collapsedEl.hide({
50442             duration:.1
50443         });
50444         this.el.slideIn(this.getSlideAnchor(), {
50445             callback : function(){
50446                 this.el.setStyle("z-index", "");
50447                 this.afterSlide();
50448                 if(this.split){
50449                     this.split.el.show();
50450                 }
50451                 this.fireEvent("invalidated", this);
50452                 this.fireEvent("expanded", this);
50453             },
50454             scope: this,
50455             block: true
50456         });
50457     },
50458
50459     anchors : {
50460         "west" : "left",
50461         "east" : "right",
50462         "north" : "top",
50463         "south" : "bottom"
50464     },
50465
50466     sanchors : {
50467         "west" : "l",
50468         "east" : "r",
50469         "north" : "t",
50470         "south" : "b"
50471     },
50472
50473     canchors : {
50474         "west" : "tl-tr",
50475         "east" : "tr-tl",
50476         "north" : "tl-bl",
50477         "south" : "bl-tl"
50478     },
50479
50480     getAnchor : function(){
50481         return this.anchors[this.position];
50482     },
50483
50484     getCollapseAnchor : function(){
50485         return this.canchors[this.position];
50486     },
50487
50488     getSlideAnchor : function(){
50489         return this.sanchors[this.position];
50490     },
50491
50492     getAlignAdj : function(){
50493         var cm = this.cmargins;
50494         switch(this.position){
50495             case "west":
50496                 return [0, 0];
50497             break;
50498             case "east":
50499                 return [0, 0];
50500             break;
50501             case "north":
50502                 return [0, 0];
50503             break;
50504             case "south":
50505                 return [0, 0];
50506             break;
50507         }
50508     },
50509
50510     getExpandAdj : function(){
50511         var c = this.collapsedEl, cm = this.cmargins;
50512         switch(this.position){
50513             case "west":
50514                 return [-(cm.right+c.getWidth()+cm.left), 0];
50515             break;
50516             case "east":
50517                 return [cm.right+c.getWidth()+cm.left, 0];
50518             break;
50519             case "north":
50520                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50521             break;
50522             case "south":
50523                 return [0, cm.top+cm.bottom+c.getHeight()];
50524             break;
50525         }
50526     }
50527 });/*
50528  * Based on:
50529  * Ext JS Library 1.1.1
50530  * Copyright(c) 2006-2007, Ext JS, LLC.
50531  *
50532  * Originally Released Under LGPL - original licence link has changed is not relivant.
50533  *
50534  * Fork - LGPL
50535  * <script type="text/javascript">
50536  */
50537 /*
50538  * These classes are private internal classes
50539  */
50540 Roo.CenterLayoutRegion = function(mgr, config){
50541     Roo.LayoutRegion.call(this, mgr, config, "center");
50542     this.visible = true;
50543     this.minWidth = config.minWidth || 20;
50544     this.minHeight = config.minHeight || 20;
50545 };
50546
50547 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50548     hide : function(){
50549         // center panel can't be hidden
50550     },
50551     
50552     show : function(){
50553         // center panel can't be hidden
50554     },
50555     
50556     getMinWidth: function(){
50557         return this.minWidth;
50558     },
50559     
50560     getMinHeight: function(){
50561         return this.minHeight;
50562     }
50563 });
50564
50565
50566 Roo.NorthLayoutRegion = function(mgr, config){
50567     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50568     if(this.split){
50569         this.split.placement = Roo.SplitBar.TOP;
50570         this.split.orientation = Roo.SplitBar.VERTICAL;
50571         this.split.el.addClass("x-layout-split-v");
50572     }
50573     var size = config.initialSize || config.height;
50574     if(typeof size != "undefined"){
50575         this.el.setHeight(size);
50576     }
50577 };
50578 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50579     orientation: Roo.SplitBar.VERTICAL,
50580     getBox : function(){
50581         if(this.collapsed){
50582             return this.collapsedEl.getBox();
50583         }
50584         var box = this.el.getBox();
50585         if(this.split){
50586             box.height += this.split.el.getHeight();
50587         }
50588         return box;
50589     },
50590     
50591     updateBox : function(box){
50592         if(this.split && !this.collapsed){
50593             box.height -= this.split.el.getHeight();
50594             this.split.el.setLeft(box.x);
50595             this.split.el.setTop(box.y+box.height);
50596             this.split.el.setWidth(box.width);
50597         }
50598         if(this.collapsed){
50599             this.updateBody(box.width, null);
50600         }
50601         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50602     }
50603 });
50604
50605 Roo.SouthLayoutRegion = function(mgr, config){
50606     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50607     if(this.split){
50608         this.split.placement = Roo.SplitBar.BOTTOM;
50609         this.split.orientation = Roo.SplitBar.VERTICAL;
50610         this.split.el.addClass("x-layout-split-v");
50611     }
50612     var size = config.initialSize || config.height;
50613     if(typeof size != "undefined"){
50614         this.el.setHeight(size);
50615     }
50616 };
50617 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50618     orientation: Roo.SplitBar.VERTICAL,
50619     getBox : function(){
50620         if(this.collapsed){
50621             return this.collapsedEl.getBox();
50622         }
50623         var box = this.el.getBox();
50624         if(this.split){
50625             var sh = this.split.el.getHeight();
50626             box.height += sh;
50627             box.y -= sh;
50628         }
50629         return box;
50630     },
50631     
50632     updateBox : function(box){
50633         if(this.split && !this.collapsed){
50634             var sh = this.split.el.getHeight();
50635             box.height -= sh;
50636             box.y += sh;
50637             this.split.el.setLeft(box.x);
50638             this.split.el.setTop(box.y-sh);
50639             this.split.el.setWidth(box.width);
50640         }
50641         if(this.collapsed){
50642             this.updateBody(box.width, null);
50643         }
50644         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50645     }
50646 });
50647
50648 Roo.EastLayoutRegion = function(mgr, config){
50649     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50650     if(this.split){
50651         this.split.placement = Roo.SplitBar.RIGHT;
50652         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50653         this.split.el.addClass("x-layout-split-h");
50654     }
50655     var size = config.initialSize || config.width;
50656     if(typeof size != "undefined"){
50657         this.el.setWidth(size);
50658     }
50659 };
50660 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50661     orientation: Roo.SplitBar.HORIZONTAL,
50662     getBox : function(){
50663         if(this.collapsed){
50664             return this.collapsedEl.getBox();
50665         }
50666         var box = this.el.getBox();
50667         if(this.split){
50668             var sw = this.split.el.getWidth();
50669             box.width += sw;
50670             box.x -= sw;
50671         }
50672         return box;
50673     },
50674
50675     updateBox : function(box){
50676         if(this.split && !this.collapsed){
50677             var sw = this.split.el.getWidth();
50678             box.width -= sw;
50679             this.split.el.setLeft(box.x);
50680             this.split.el.setTop(box.y);
50681             this.split.el.setHeight(box.height);
50682             box.x += sw;
50683         }
50684         if(this.collapsed){
50685             this.updateBody(null, box.height);
50686         }
50687         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50688     }
50689 });
50690
50691 Roo.WestLayoutRegion = function(mgr, config){
50692     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50693     if(this.split){
50694         this.split.placement = Roo.SplitBar.LEFT;
50695         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50696         this.split.el.addClass("x-layout-split-h");
50697     }
50698     var size = config.initialSize || config.width;
50699     if(typeof size != "undefined"){
50700         this.el.setWidth(size);
50701     }
50702 };
50703 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50704     orientation: Roo.SplitBar.HORIZONTAL,
50705     getBox : function(){
50706         if(this.collapsed){
50707             return this.collapsedEl.getBox();
50708         }
50709         var box = this.el.getBox();
50710         if(this.split){
50711             box.width += this.split.el.getWidth();
50712         }
50713         return box;
50714     },
50715     
50716     updateBox : function(box){
50717         if(this.split && !this.collapsed){
50718             var sw = this.split.el.getWidth();
50719             box.width -= sw;
50720             this.split.el.setLeft(box.x+box.width);
50721             this.split.el.setTop(box.y);
50722             this.split.el.setHeight(box.height);
50723         }
50724         if(this.collapsed){
50725             this.updateBody(null, box.height);
50726         }
50727         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50728     }
50729 });
50730 /*
50731  * Based on:
50732  * Ext JS Library 1.1.1
50733  * Copyright(c) 2006-2007, Ext JS, LLC.
50734  *
50735  * Originally Released Under LGPL - original licence link has changed is not relivant.
50736  *
50737  * Fork - LGPL
50738  * <script type="text/javascript">
50739  */
50740  
50741  
50742 /*
50743  * Private internal class for reading and applying state
50744  */
50745 Roo.LayoutStateManager = function(layout){
50746      // default empty state
50747      this.state = {
50748         north: {},
50749         south: {},
50750         east: {},
50751         west: {}       
50752     };
50753 };
50754
50755 Roo.LayoutStateManager.prototype = {
50756     init : function(layout, provider){
50757         this.provider = provider;
50758         var state = provider.get(layout.id+"-layout-state");
50759         if(state){
50760             var wasUpdating = layout.isUpdating();
50761             if(!wasUpdating){
50762                 layout.beginUpdate();
50763             }
50764             for(var key in state){
50765                 if(typeof state[key] != "function"){
50766                     var rstate = state[key];
50767                     var r = layout.getRegion(key);
50768                     if(r && rstate){
50769                         if(rstate.size){
50770                             r.resizeTo(rstate.size);
50771                         }
50772                         if(rstate.collapsed == true){
50773                             r.collapse(true);
50774                         }else{
50775                             r.expand(null, true);
50776                         }
50777                     }
50778                 }
50779             }
50780             if(!wasUpdating){
50781                 layout.endUpdate();
50782             }
50783             this.state = state; 
50784         }
50785         this.layout = layout;
50786         layout.on("regionresized", this.onRegionResized, this);
50787         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50788         layout.on("regionexpanded", this.onRegionExpanded, this);
50789     },
50790     
50791     storeState : function(){
50792         this.provider.set(this.layout.id+"-layout-state", this.state);
50793     },
50794     
50795     onRegionResized : function(region, newSize){
50796         this.state[region.getPosition()].size = newSize;
50797         this.storeState();
50798     },
50799     
50800     onRegionCollapsed : function(region){
50801         this.state[region.getPosition()].collapsed = true;
50802         this.storeState();
50803     },
50804     
50805     onRegionExpanded : function(region){
50806         this.state[region.getPosition()].collapsed = false;
50807         this.storeState();
50808     }
50809 };/*
50810  * Based on:
50811  * Ext JS Library 1.1.1
50812  * Copyright(c) 2006-2007, Ext JS, LLC.
50813  *
50814  * Originally Released Under LGPL - original licence link has changed is not relivant.
50815  *
50816  * Fork - LGPL
50817  * <script type="text/javascript">
50818  */
50819 /**
50820  * @class Roo.ContentPanel
50821  * @extends Roo.util.Observable
50822  * A basic ContentPanel element.
50823  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50824  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50825  * @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
50826  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50827  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50828  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50829  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50830  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50831  * @cfg {String} title          The title for this panel
50832  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50833  * @cfg {String} url            Calls {@link #setUrl} with this value
50834  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50835  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50836  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50837  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50838
50839  * @constructor
50840  * Create a new ContentPanel.
50841  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50842  * @param {String/Object} config A string to set only the title or a config object
50843  * @param {String} content (optional) Set the HTML content for this panel
50844  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50845  */
50846 Roo.ContentPanel = function(el, config, content){
50847     
50848      
50849     /*
50850     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50851         config = el;
50852         el = Roo.id();
50853     }
50854     if (config && config.parentLayout) { 
50855         el = config.parentLayout.el.createChild(); 
50856     }
50857     */
50858     if(el.autoCreate){ // xtype is available if this is called from factory
50859         config = el;
50860         el = Roo.id();
50861     }
50862     this.el = Roo.get(el);
50863     if(!this.el && config && config.autoCreate){
50864         if(typeof config.autoCreate == "object"){
50865             if(!config.autoCreate.id){
50866                 config.autoCreate.id = config.id||el;
50867             }
50868             this.el = Roo.DomHelper.append(document.body,
50869                         config.autoCreate, true);
50870         }else{
50871             this.el = Roo.DomHelper.append(document.body,
50872                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50873         }
50874     }
50875     this.closable = false;
50876     this.loaded = false;
50877     this.active = false;
50878     if(typeof config == "string"){
50879         this.title = config;
50880     }else{
50881         Roo.apply(this, config);
50882     }
50883     
50884     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50885         this.wrapEl = this.el.wrap();
50886         this.toolbar.container = this.el.insertSibling(false, 'before');
50887         this.toolbar = new Roo.Toolbar(this.toolbar);
50888     }
50889     
50890     // xtype created footer. - not sure if will work as we normally have to render first..
50891     if (this.footer && !this.footer.el && this.footer.xtype) {
50892         if (!this.wrapEl) {
50893             this.wrapEl = this.el.wrap();
50894         }
50895     
50896         this.footer.container = this.wrapEl.createChild();
50897          
50898         this.footer = Roo.factory(this.footer, Roo);
50899         
50900     }
50901     
50902     if(this.resizeEl){
50903         this.resizeEl = Roo.get(this.resizeEl, true);
50904     }else{
50905         this.resizeEl = this.el;
50906     }
50907     // handle view.xtype
50908     
50909  
50910     
50911     
50912     this.addEvents({
50913         /**
50914          * @event activate
50915          * Fires when this panel is activated. 
50916          * @param {Roo.ContentPanel} this
50917          */
50918         "activate" : true,
50919         /**
50920          * @event deactivate
50921          * Fires when this panel is activated. 
50922          * @param {Roo.ContentPanel} this
50923          */
50924         "deactivate" : true,
50925
50926         /**
50927          * @event resize
50928          * Fires when this panel is resized if fitToFrame is true.
50929          * @param {Roo.ContentPanel} this
50930          * @param {Number} width The width after any component adjustments
50931          * @param {Number} height The height after any component adjustments
50932          */
50933         "resize" : true,
50934         
50935          /**
50936          * @event render
50937          * Fires when this tab is created
50938          * @param {Roo.ContentPanel} this
50939          */
50940         "render" : true
50941         
50942         
50943         
50944     });
50945     
50946
50947     
50948     
50949     if(this.autoScroll){
50950         this.resizeEl.setStyle("overflow", "auto");
50951     } else {
50952         // fix randome scrolling
50953         this.el.on('scroll', function() {
50954             Roo.log('fix random scolling');
50955             this.scrollTo('top',0); 
50956         });
50957     }
50958     content = content || this.content;
50959     if(content){
50960         this.setContent(content);
50961     }
50962     if(config && config.url){
50963         this.setUrl(this.url, this.params, this.loadOnce);
50964     }
50965     
50966     
50967     
50968     Roo.ContentPanel.superclass.constructor.call(this);
50969     
50970     if (this.view && typeof(this.view.xtype) != 'undefined') {
50971         this.view.el = this.el.appendChild(document.createElement("div"));
50972         this.view = Roo.factory(this.view); 
50973         this.view.render  &&  this.view.render(false, '');  
50974     }
50975     
50976     
50977     this.fireEvent('render', this);
50978 };
50979
50980 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50981     tabTip:'',
50982     setRegion : function(region){
50983         this.region = region;
50984         if(region){
50985            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50986         }else{
50987            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50988         } 
50989     },
50990     
50991     /**
50992      * Returns the toolbar for this Panel if one was configured. 
50993      * @return {Roo.Toolbar} 
50994      */
50995     getToolbar : function(){
50996         return this.toolbar;
50997     },
50998     
50999     setActiveState : function(active){
51000         this.active = active;
51001         if(!active){
51002             this.fireEvent("deactivate", this);
51003         }else{
51004             this.fireEvent("activate", this);
51005         }
51006     },
51007     /**
51008      * Updates this panel's element
51009      * @param {String} content The new content
51010      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51011     */
51012     setContent : function(content, loadScripts){
51013         this.el.update(content, loadScripts);
51014     },
51015
51016     ignoreResize : function(w, h){
51017         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51018             return true;
51019         }else{
51020             this.lastSize = {width: w, height: h};
51021             return false;
51022         }
51023     },
51024     /**
51025      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51026      * @return {Roo.UpdateManager} The UpdateManager
51027      */
51028     getUpdateManager : function(){
51029         return this.el.getUpdateManager();
51030     },
51031      /**
51032      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51033      * @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:
51034 <pre><code>
51035 panel.load({
51036     url: "your-url.php",
51037     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51038     callback: yourFunction,
51039     scope: yourObject, //(optional scope)
51040     discardUrl: false,
51041     nocache: false,
51042     text: "Loading...",
51043     timeout: 30,
51044     scripts: false
51045 });
51046 </code></pre>
51047      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51048      * 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.
51049      * @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}
51050      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51051      * @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.
51052      * @return {Roo.ContentPanel} this
51053      */
51054     load : function(){
51055         var um = this.el.getUpdateManager();
51056         um.update.apply(um, arguments);
51057         return this;
51058     },
51059
51060
51061     /**
51062      * 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.
51063      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51064      * @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)
51065      * @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)
51066      * @return {Roo.UpdateManager} The UpdateManager
51067      */
51068     setUrl : function(url, params, loadOnce){
51069         if(this.refreshDelegate){
51070             this.removeListener("activate", this.refreshDelegate);
51071         }
51072         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51073         this.on("activate", this.refreshDelegate);
51074         return this.el.getUpdateManager();
51075     },
51076     
51077     _handleRefresh : function(url, params, loadOnce){
51078         if(!loadOnce || !this.loaded){
51079             var updater = this.el.getUpdateManager();
51080             updater.update(url, params, this._setLoaded.createDelegate(this));
51081         }
51082     },
51083     
51084     _setLoaded : function(){
51085         this.loaded = true;
51086     }, 
51087     
51088     /**
51089      * Returns this panel's id
51090      * @return {String} 
51091      */
51092     getId : function(){
51093         return this.el.id;
51094     },
51095     
51096     /** 
51097      * Returns this panel's element - used by regiosn to add.
51098      * @return {Roo.Element} 
51099      */
51100     getEl : function(){
51101         return this.wrapEl || this.el;
51102     },
51103     
51104     adjustForComponents : function(width, height)
51105     {
51106         //Roo.log('adjustForComponents ');
51107         if(this.resizeEl != this.el){
51108             width -= this.el.getFrameWidth('lr');
51109             height -= this.el.getFrameWidth('tb');
51110         }
51111         if(this.toolbar){
51112             var te = this.toolbar.getEl();
51113             height -= te.getHeight();
51114             te.setWidth(width);
51115         }
51116         if(this.footer){
51117             var te = this.footer.getEl();
51118             Roo.log("footer:" + te.getHeight());
51119             
51120             height -= te.getHeight();
51121             te.setWidth(width);
51122         }
51123         
51124         
51125         if(this.adjustments){
51126             width += this.adjustments[0];
51127             height += this.adjustments[1];
51128         }
51129         return {"width": width, "height": height};
51130     },
51131     
51132     setSize : function(width, height){
51133         if(this.fitToFrame && !this.ignoreResize(width, height)){
51134             if(this.fitContainer && this.resizeEl != this.el){
51135                 this.el.setSize(width, height);
51136             }
51137             var size = this.adjustForComponents(width, height);
51138             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51139             this.fireEvent('resize', this, size.width, size.height);
51140         }
51141     },
51142     
51143     /**
51144      * Returns this panel's title
51145      * @return {String} 
51146      */
51147     getTitle : function(){
51148         return this.title;
51149     },
51150     
51151     /**
51152      * Set this panel's title
51153      * @param {String} title
51154      */
51155     setTitle : function(title){
51156         this.title = title;
51157         if(this.region){
51158             this.region.updatePanelTitle(this, title);
51159         }
51160     },
51161     
51162     /**
51163      * Returns true is this panel was configured to be closable
51164      * @return {Boolean} 
51165      */
51166     isClosable : function(){
51167         return this.closable;
51168     },
51169     
51170     beforeSlide : function(){
51171         this.el.clip();
51172         this.resizeEl.clip();
51173     },
51174     
51175     afterSlide : function(){
51176         this.el.unclip();
51177         this.resizeEl.unclip();
51178     },
51179     
51180     /**
51181      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51182      *   Will fail silently if the {@link #setUrl} method has not been called.
51183      *   This does not activate the panel, just updates its content.
51184      */
51185     refresh : function(){
51186         if(this.refreshDelegate){
51187            this.loaded = false;
51188            this.refreshDelegate();
51189         }
51190     },
51191     
51192     /**
51193      * Destroys this panel
51194      */
51195     destroy : function(){
51196         this.el.removeAllListeners();
51197         var tempEl = document.createElement("span");
51198         tempEl.appendChild(this.el.dom);
51199         tempEl.innerHTML = "";
51200         this.el.remove();
51201         this.el = null;
51202     },
51203     
51204     /**
51205      * form - if the content panel contains a form - this is a reference to it.
51206      * @type {Roo.form.Form}
51207      */
51208     form : false,
51209     /**
51210      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51211      *    This contains a reference to it.
51212      * @type {Roo.View}
51213      */
51214     view : false,
51215     
51216       /**
51217      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51218      * <pre><code>
51219
51220 layout.addxtype({
51221        xtype : 'Form',
51222        items: [ .... ]
51223    }
51224 );
51225
51226 </code></pre>
51227      * @param {Object} cfg Xtype definition of item to add.
51228      */
51229     
51230     addxtype : function(cfg) {
51231         // add form..
51232         if (cfg.xtype.match(/^Form$/)) {
51233             
51234             var el;
51235             //if (this.footer) {
51236             //    el = this.footer.container.insertSibling(false, 'before');
51237             //} else {
51238                 el = this.el.createChild();
51239             //}
51240
51241             this.form = new  Roo.form.Form(cfg);
51242             
51243             
51244             if ( this.form.allItems.length) this.form.render(el.dom);
51245             return this.form;
51246         }
51247         // should only have one of theses..
51248         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51249             // views.. should not be just added - used named prop 'view''
51250             
51251             cfg.el = this.el.appendChild(document.createElement("div"));
51252             // factory?
51253             
51254             var ret = new Roo.factory(cfg);
51255              
51256              ret.render && ret.render(false, ''); // render blank..
51257             this.view = ret;
51258             return ret;
51259         }
51260         return false;
51261     }
51262 });
51263
51264 /**
51265  * @class Roo.GridPanel
51266  * @extends Roo.ContentPanel
51267  * @constructor
51268  * Create a new GridPanel.
51269  * @param {Roo.grid.Grid} grid The grid for this panel
51270  * @param {String/Object} config A string to set only the panel's title, or a config object
51271  */
51272 Roo.GridPanel = function(grid, config){
51273     
51274   
51275     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51276         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51277         
51278     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51279     
51280     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51281     
51282     if(this.toolbar){
51283         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51284     }
51285     // xtype created footer. - not sure if will work as we normally have to render first..
51286     if (this.footer && !this.footer.el && this.footer.xtype) {
51287         
51288         this.footer.container = this.grid.getView().getFooterPanel(true);
51289         this.footer.dataSource = this.grid.dataSource;
51290         this.footer = Roo.factory(this.footer, Roo);
51291         
51292     }
51293     
51294     grid.monitorWindowResize = false; // turn off autosizing
51295     grid.autoHeight = false;
51296     grid.autoWidth = false;
51297     this.grid = grid;
51298     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51299 };
51300
51301 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51302     getId : function(){
51303         return this.grid.id;
51304     },
51305     
51306     /**
51307      * Returns the grid for this panel
51308      * @return {Roo.grid.Grid} 
51309      */
51310     getGrid : function(){
51311         return this.grid;    
51312     },
51313     
51314     setSize : function(width, height){
51315         if(!this.ignoreResize(width, height)){
51316             var grid = this.grid;
51317             var size = this.adjustForComponents(width, height);
51318             grid.getGridEl().setSize(size.width, size.height);
51319             grid.autoSize();
51320         }
51321     },
51322     
51323     beforeSlide : function(){
51324         this.grid.getView().scroller.clip();
51325     },
51326     
51327     afterSlide : function(){
51328         this.grid.getView().scroller.unclip();
51329     },
51330     
51331     destroy : function(){
51332         this.grid.destroy();
51333         delete this.grid;
51334         Roo.GridPanel.superclass.destroy.call(this); 
51335     }
51336 });
51337
51338
51339 /**
51340  * @class Roo.NestedLayoutPanel
51341  * @extends Roo.ContentPanel
51342  * @constructor
51343  * Create a new NestedLayoutPanel.
51344  * 
51345  * 
51346  * @param {Roo.BorderLayout} layout The layout for this panel
51347  * @param {String/Object} config A string to set only the title or a config object
51348  */
51349 Roo.NestedLayoutPanel = function(layout, config)
51350 {
51351     // construct with only one argument..
51352     /* FIXME - implement nicer consturctors
51353     if (layout.layout) {
51354         config = layout;
51355         layout = config.layout;
51356         delete config.layout;
51357     }
51358     if (layout.xtype && !layout.getEl) {
51359         // then layout needs constructing..
51360         layout = Roo.factory(layout, Roo);
51361     }
51362     */
51363     
51364     
51365     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51366     
51367     layout.monitorWindowResize = false; // turn off autosizing
51368     this.layout = layout;
51369     this.layout.getEl().addClass("x-layout-nested-layout");
51370     
51371     
51372     
51373     
51374 };
51375
51376 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51377
51378     setSize : function(width, height){
51379         if(!this.ignoreResize(width, height)){
51380             var size = this.adjustForComponents(width, height);
51381             var el = this.layout.getEl();
51382             el.setSize(size.width, size.height);
51383             var touch = el.dom.offsetWidth;
51384             this.layout.layout();
51385             // ie requires a double layout on the first pass
51386             if(Roo.isIE && !this.initialized){
51387                 this.initialized = true;
51388                 this.layout.layout();
51389             }
51390         }
51391     },
51392     
51393     // activate all subpanels if not currently active..
51394     
51395     setActiveState : function(active){
51396         this.active = active;
51397         if(!active){
51398             this.fireEvent("deactivate", this);
51399             return;
51400         }
51401         
51402         this.fireEvent("activate", this);
51403         // not sure if this should happen before or after..
51404         if (!this.layout) {
51405             return; // should not happen..
51406         }
51407         var reg = false;
51408         for (var r in this.layout.regions) {
51409             reg = this.layout.getRegion(r);
51410             if (reg.getActivePanel()) {
51411                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51412                 reg.setActivePanel(reg.getActivePanel());
51413                 continue;
51414             }
51415             if (!reg.panels.length) {
51416                 continue;
51417             }
51418             reg.showPanel(reg.getPanel(0));
51419         }
51420         
51421         
51422         
51423         
51424     },
51425     
51426     /**
51427      * Returns the nested BorderLayout for this panel
51428      * @return {Roo.BorderLayout} 
51429      */
51430     getLayout : function(){
51431         return this.layout;
51432     },
51433     
51434      /**
51435      * Adds a xtype elements to the layout of the nested panel
51436      * <pre><code>
51437
51438 panel.addxtype({
51439        xtype : 'ContentPanel',
51440        region: 'west',
51441        items: [ .... ]
51442    }
51443 );
51444
51445 panel.addxtype({
51446         xtype : 'NestedLayoutPanel',
51447         region: 'west',
51448         layout: {
51449            center: { },
51450            west: { }   
51451         },
51452         items : [ ... list of content panels or nested layout panels.. ]
51453    }
51454 );
51455 </code></pre>
51456      * @param {Object} cfg Xtype definition of item to add.
51457      */
51458     addxtype : function(cfg) {
51459         return this.layout.addxtype(cfg);
51460     
51461     }
51462 });
51463
51464 Roo.ScrollPanel = function(el, config, content){
51465     config = config || {};
51466     config.fitToFrame = true;
51467     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51468     
51469     this.el.dom.style.overflow = "hidden";
51470     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51471     this.el.removeClass("x-layout-inactive-content");
51472     this.el.on("mousewheel", this.onWheel, this);
51473
51474     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51475     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51476     up.unselectable(); down.unselectable();
51477     up.on("click", this.scrollUp, this);
51478     down.on("click", this.scrollDown, this);
51479     up.addClassOnOver("x-scroller-btn-over");
51480     down.addClassOnOver("x-scroller-btn-over");
51481     up.addClassOnClick("x-scroller-btn-click");
51482     down.addClassOnClick("x-scroller-btn-click");
51483     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51484
51485     this.resizeEl = this.el;
51486     this.el = wrap; this.up = up; this.down = down;
51487 };
51488
51489 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51490     increment : 100,
51491     wheelIncrement : 5,
51492     scrollUp : function(){
51493         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51494     },
51495
51496     scrollDown : function(){
51497         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51498     },
51499
51500     afterScroll : function(){
51501         var el = this.resizeEl;
51502         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51503         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51504         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51505     },
51506
51507     setSize : function(){
51508         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51509         this.afterScroll();
51510     },
51511
51512     onWheel : function(e){
51513         var d = e.getWheelDelta();
51514         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51515         this.afterScroll();
51516         e.stopEvent();
51517     },
51518
51519     setContent : function(content, loadScripts){
51520         this.resizeEl.update(content, loadScripts);
51521     }
51522
51523 });
51524
51525
51526
51527
51528
51529
51530
51531
51532
51533 /**
51534  * @class Roo.TreePanel
51535  * @extends Roo.ContentPanel
51536  * @constructor
51537  * Create a new TreePanel. - defaults to fit/scoll contents.
51538  * @param {String/Object} config A string to set only the panel's title, or a config object
51539  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51540  */
51541 Roo.TreePanel = function(config){
51542     var el = config.el;
51543     var tree = config.tree;
51544     delete config.tree; 
51545     delete config.el; // hopefull!
51546     
51547     // wrapper for IE7 strict & safari scroll issue
51548     
51549     var treeEl = el.createChild();
51550     config.resizeEl = treeEl;
51551     
51552     
51553     
51554     Roo.TreePanel.superclass.constructor.call(this, el, config);
51555  
51556  
51557     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51558     //console.log(tree);
51559     this.on('activate', function()
51560     {
51561         if (this.tree.rendered) {
51562             return;
51563         }
51564         //console.log('render tree');
51565         this.tree.render();
51566     });
51567     // this should not be needed.. - it's actually the 'el' that resizes?
51568     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51569     
51570     //this.on('resize',  function (cp, w, h) {
51571     //        this.tree.innerCt.setWidth(w);
51572     //        this.tree.innerCt.setHeight(h);
51573     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51574     //});
51575
51576         
51577     
51578 };
51579
51580 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51581     fitToFrame : true,
51582     autoScroll : true
51583 });
51584
51585
51586
51587
51588
51589
51590
51591
51592
51593
51594
51595 /*
51596  * Based on:
51597  * Ext JS Library 1.1.1
51598  * Copyright(c) 2006-2007, Ext JS, LLC.
51599  *
51600  * Originally Released Under LGPL - original licence link has changed is not relivant.
51601  *
51602  * Fork - LGPL
51603  * <script type="text/javascript">
51604  */
51605  
51606
51607 /**
51608  * @class Roo.ReaderLayout
51609  * @extends Roo.BorderLayout
51610  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51611  * center region containing two nested regions (a top one for a list view and one for item preview below),
51612  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51613  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51614  * expedites the setup of the overall layout and regions for this common application style.
51615  * Example:
51616  <pre><code>
51617 var reader = new Roo.ReaderLayout();
51618 var CP = Roo.ContentPanel;  // shortcut for adding
51619
51620 reader.beginUpdate();
51621 reader.add("north", new CP("north", "North"));
51622 reader.add("west", new CP("west", {title: "West"}));
51623 reader.add("east", new CP("east", {title: "East"}));
51624
51625 reader.regions.listView.add(new CP("listView", "List"));
51626 reader.regions.preview.add(new CP("preview", "Preview"));
51627 reader.endUpdate();
51628 </code></pre>
51629 * @constructor
51630 * Create a new ReaderLayout
51631 * @param {Object} config Configuration options
51632 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51633 * document.body if omitted)
51634 */
51635 Roo.ReaderLayout = function(config, renderTo){
51636     var c = config || {size:{}};
51637     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51638         north: c.north !== false ? Roo.apply({
51639             split:false,
51640             initialSize: 32,
51641             titlebar: false
51642         }, c.north) : false,
51643         west: c.west !== false ? Roo.apply({
51644             split:true,
51645             initialSize: 200,
51646             minSize: 175,
51647             maxSize: 400,
51648             titlebar: true,
51649             collapsible: true,
51650             animate: true,
51651             margins:{left:5,right:0,bottom:5,top:5},
51652             cmargins:{left:5,right:5,bottom:5,top:5}
51653         }, c.west) : false,
51654         east: c.east !== false ? Roo.apply({
51655             split:true,
51656             initialSize: 200,
51657             minSize: 175,
51658             maxSize: 400,
51659             titlebar: true,
51660             collapsible: true,
51661             animate: true,
51662             margins:{left:0,right:5,bottom:5,top:5},
51663             cmargins:{left:5,right:5,bottom:5,top:5}
51664         }, c.east) : false,
51665         center: Roo.apply({
51666             tabPosition: 'top',
51667             autoScroll:false,
51668             closeOnTab: true,
51669             titlebar:false,
51670             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51671         }, c.center)
51672     });
51673
51674     this.el.addClass('x-reader');
51675
51676     this.beginUpdate();
51677
51678     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51679         south: c.preview !== false ? Roo.apply({
51680             split:true,
51681             initialSize: 200,
51682             minSize: 100,
51683             autoScroll:true,
51684             collapsible:true,
51685             titlebar: true,
51686             cmargins:{top:5,left:0, right:0, bottom:0}
51687         }, c.preview) : false,
51688         center: Roo.apply({
51689             autoScroll:false,
51690             titlebar:false,
51691             minHeight:200
51692         }, c.listView)
51693     });
51694     this.add('center', new Roo.NestedLayoutPanel(inner,
51695             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51696
51697     this.endUpdate();
51698
51699     this.regions.preview = inner.getRegion('south');
51700     this.regions.listView = inner.getRegion('center');
51701 };
51702
51703 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51704  * Based on:
51705  * Ext JS Library 1.1.1
51706  * Copyright(c) 2006-2007, Ext JS, LLC.
51707  *
51708  * Originally Released Under LGPL - original licence link has changed is not relivant.
51709  *
51710  * Fork - LGPL
51711  * <script type="text/javascript">
51712  */
51713  
51714 /**
51715  * @class Roo.grid.Grid
51716  * @extends Roo.util.Observable
51717  * This class represents the primary interface of a component based grid control.
51718  * <br><br>Usage:<pre><code>
51719  var grid = new Roo.grid.Grid("my-container-id", {
51720      ds: myDataStore,
51721      cm: myColModel,
51722      selModel: mySelectionModel,
51723      autoSizeColumns: true,
51724      monitorWindowResize: false,
51725      trackMouseOver: true
51726  });
51727  // set any options
51728  grid.render();
51729  * </code></pre>
51730  * <b>Common Problems:</b><br/>
51731  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51732  * element will correct this<br/>
51733  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51734  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51735  * are unpredictable.<br/>
51736  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51737  * grid to calculate dimensions/offsets.<br/>
51738   * @constructor
51739  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51740  * The container MUST have some type of size defined for the grid to fill. The container will be
51741  * automatically set to position relative if it isn't already.
51742  * @param {Object} config A config object that sets properties on this grid.
51743  */
51744 Roo.grid.Grid = function(container, config){
51745         // initialize the container
51746         this.container = Roo.get(container);
51747         this.container.update("");
51748         this.container.setStyle("overflow", "hidden");
51749     this.container.addClass('x-grid-container');
51750
51751     this.id = this.container.id;
51752
51753     Roo.apply(this, config);
51754     // check and correct shorthanded configs
51755     if(this.ds){
51756         this.dataSource = this.ds;
51757         delete this.ds;
51758     }
51759     if(this.cm){
51760         this.colModel = this.cm;
51761         delete this.cm;
51762     }
51763     if(this.sm){
51764         this.selModel = this.sm;
51765         delete this.sm;
51766     }
51767
51768     if (this.selModel) {
51769         this.selModel = Roo.factory(this.selModel, Roo.grid);
51770         this.sm = this.selModel;
51771         this.sm.xmodule = this.xmodule || false;
51772     }
51773     if (typeof(this.colModel.config) == 'undefined') {
51774         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51775         this.cm = this.colModel;
51776         this.cm.xmodule = this.xmodule || false;
51777     }
51778     if (this.dataSource) {
51779         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51780         this.ds = this.dataSource;
51781         this.ds.xmodule = this.xmodule || false;
51782          
51783     }
51784     
51785     
51786     
51787     if(this.width){
51788         this.container.setWidth(this.width);
51789     }
51790
51791     if(this.height){
51792         this.container.setHeight(this.height);
51793     }
51794     /** @private */
51795         this.addEvents({
51796         // raw events
51797         /**
51798          * @event click
51799          * The raw click event for the entire grid.
51800          * @param {Roo.EventObject} e
51801          */
51802         "click" : true,
51803         /**
51804          * @event dblclick
51805          * The raw dblclick event for the entire grid.
51806          * @param {Roo.EventObject} e
51807          */
51808         "dblclick" : true,
51809         /**
51810          * @event contextmenu
51811          * The raw contextmenu event for the entire grid.
51812          * @param {Roo.EventObject} e
51813          */
51814         "contextmenu" : true,
51815         /**
51816          * @event mousedown
51817          * The raw mousedown event for the entire grid.
51818          * @param {Roo.EventObject} e
51819          */
51820         "mousedown" : true,
51821         /**
51822          * @event mouseup
51823          * The raw mouseup event for the entire grid.
51824          * @param {Roo.EventObject} e
51825          */
51826         "mouseup" : true,
51827         /**
51828          * @event mouseover
51829          * The raw mouseover event for the entire grid.
51830          * @param {Roo.EventObject} e
51831          */
51832         "mouseover" : true,
51833         /**
51834          * @event mouseout
51835          * The raw mouseout event for the entire grid.
51836          * @param {Roo.EventObject} e
51837          */
51838         "mouseout" : true,
51839         /**
51840          * @event keypress
51841          * The raw keypress event for the entire grid.
51842          * @param {Roo.EventObject} e
51843          */
51844         "keypress" : true,
51845         /**
51846          * @event keydown
51847          * The raw keydown event for the entire grid.
51848          * @param {Roo.EventObject} e
51849          */
51850         "keydown" : true,
51851
51852         // custom events
51853
51854         /**
51855          * @event cellclick
51856          * Fires when a cell is clicked
51857          * @param {Grid} this
51858          * @param {Number} rowIndex
51859          * @param {Number} columnIndex
51860          * @param {Roo.EventObject} e
51861          */
51862         "cellclick" : true,
51863         /**
51864          * @event celldblclick
51865          * Fires when a cell is double clicked
51866          * @param {Grid} this
51867          * @param {Number} rowIndex
51868          * @param {Number} columnIndex
51869          * @param {Roo.EventObject} e
51870          */
51871         "celldblclick" : true,
51872         /**
51873          * @event rowclick
51874          * Fires when a row is clicked
51875          * @param {Grid} this
51876          * @param {Number} rowIndex
51877          * @param {Roo.EventObject} e
51878          */
51879         "rowclick" : true,
51880         /**
51881          * @event rowdblclick
51882          * Fires when a row is double clicked
51883          * @param {Grid} this
51884          * @param {Number} rowIndex
51885          * @param {Roo.EventObject} e
51886          */
51887         "rowdblclick" : true,
51888         /**
51889          * @event headerclick
51890          * Fires when a header is clicked
51891          * @param {Grid} this
51892          * @param {Number} columnIndex
51893          * @param {Roo.EventObject} e
51894          */
51895         "headerclick" : true,
51896         /**
51897          * @event headerdblclick
51898          * Fires when a header cell is double clicked
51899          * @param {Grid} this
51900          * @param {Number} columnIndex
51901          * @param {Roo.EventObject} e
51902          */
51903         "headerdblclick" : true,
51904         /**
51905          * @event rowcontextmenu
51906          * Fires when a row is right clicked
51907          * @param {Grid} this
51908          * @param {Number} rowIndex
51909          * @param {Roo.EventObject} e
51910          */
51911         "rowcontextmenu" : true,
51912         /**
51913          * @event cellcontextmenu
51914          * Fires when a cell is right clicked
51915          * @param {Grid} this
51916          * @param {Number} rowIndex
51917          * @param {Number} cellIndex
51918          * @param {Roo.EventObject} e
51919          */
51920          "cellcontextmenu" : true,
51921         /**
51922          * @event headercontextmenu
51923          * Fires when a header is right clicked
51924          * @param {Grid} this
51925          * @param {Number} columnIndex
51926          * @param {Roo.EventObject} e
51927          */
51928         "headercontextmenu" : true,
51929         /**
51930          * @event bodyscroll
51931          * Fires when the body element is scrolled
51932          * @param {Number} scrollLeft
51933          * @param {Number} scrollTop
51934          */
51935         "bodyscroll" : true,
51936         /**
51937          * @event columnresize
51938          * Fires when the user resizes a column
51939          * @param {Number} columnIndex
51940          * @param {Number} newSize
51941          */
51942         "columnresize" : true,
51943         /**
51944          * @event columnmove
51945          * Fires when the user moves a column
51946          * @param {Number} oldIndex
51947          * @param {Number} newIndex
51948          */
51949         "columnmove" : true,
51950         /**
51951          * @event startdrag
51952          * Fires when row(s) start being dragged
51953          * @param {Grid} this
51954          * @param {Roo.GridDD} dd The drag drop object
51955          * @param {event} e The raw browser event
51956          */
51957         "startdrag" : true,
51958         /**
51959          * @event enddrag
51960          * Fires when a drag operation is complete
51961          * @param {Grid} this
51962          * @param {Roo.GridDD} dd The drag drop object
51963          * @param {event} e The raw browser event
51964          */
51965         "enddrag" : true,
51966         /**
51967          * @event dragdrop
51968          * Fires when dragged row(s) are dropped on a valid DD target
51969          * @param {Grid} this
51970          * @param {Roo.GridDD} dd The drag drop object
51971          * @param {String} targetId The target drag drop object
51972          * @param {event} e The raw browser event
51973          */
51974         "dragdrop" : true,
51975         /**
51976          * @event dragover
51977          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51978          * @param {Grid} this
51979          * @param {Roo.GridDD} dd The drag drop object
51980          * @param {String} targetId The target drag drop object
51981          * @param {event} e The raw browser event
51982          */
51983         "dragover" : true,
51984         /**
51985          * @event dragenter
51986          *  Fires when the dragged row(s) first cross another DD target while being dragged
51987          * @param {Grid} this
51988          * @param {Roo.GridDD} dd The drag drop object
51989          * @param {String} targetId The target drag drop object
51990          * @param {event} e The raw browser event
51991          */
51992         "dragenter" : true,
51993         /**
51994          * @event dragout
51995          * Fires when the dragged row(s) leave another DD target while being dragged
51996          * @param {Grid} this
51997          * @param {Roo.GridDD} dd The drag drop object
51998          * @param {String} targetId The target drag drop object
51999          * @param {event} e The raw browser event
52000          */
52001         "dragout" : true,
52002         /**
52003          * @event rowclass
52004          * Fires when a row is rendered, so you can change add a style to it.
52005          * @param {GridView} gridview   The grid view
52006          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52007          */
52008         'rowclass' : true,
52009
52010         /**
52011          * @event render
52012          * Fires when the grid is rendered
52013          * @param {Grid} grid
52014          */
52015         'render' : true
52016     });
52017
52018     Roo.grid.Grid.superclass.constructor.call(this);
52019 };
52020 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52021     
52022     /**
52023      * @cfg {String} ddGroup - drag drop group.
52024      */
52025
52026     /**
52027      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52028      */
52029     minColumnWidth : 25,
52030
52031     /**
52032      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52033      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52034      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52035      */
52036     autoSizeColumns : false,
52037
52038     /**
52039      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52040      */
52041     autoSizeHeaders : true,
52042
52043     /**
52044      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52045      */
52046     monitorWindowResize : true,
52047
52048     /**
52049      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52050      * rows measured to get a columns size. Default is 0 (all rows).
52051      */
52052     maxRowsToMeasure : 0,
52053
52054     /**
52055      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52056      */
52057     trackMouseOver : true,
52058
52059     /**
52060     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52061     */
52062     
52063     /**
52064     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52065     */
52066     enableDragDrop : false,
52067     
52068     /**
52069     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52070     */
52071     enableColumnMove : true,
52072     
52073     /**
52074     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52075     */
52076     enableColumnHide : true,
52077     
52078     /**
52079     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52080     */
52081     enableRowHeightSync : false,
52082     
52083     /**
52084     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52085     */
52086     stripeRows : true,
52087     
52088     /**
52089     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52090     */
52091     autoHeight : false,
52092
52093     /**
52094      * @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.
52095      */
52096     autoExpandColumn : false,
52097
52098     /**
52099     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52100     * Default is 50.
52101     */
52102     autoExpandMin : 50,
52103
52104     /**
52105     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52106     */
52107     autoExpandMax : 1000,
52108
52109     /**
52110     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52111     */
52112     view : null,
52113
52114     /**
52115     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52116     */
52117     loadMask : false,
52118     /**
52119     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52120     */
52121     dropTarget: false,
52122     
52123    
52124     
52125     // private
52126     rendered : false,
52127
52128     /**
52129     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52130     * of a fixed width. Default is false.
52131     */
52132     /**
52133     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52134     */
52135     /**
52136      * Called once after all setup has been completed and the grid is ready to be rendered.
52137      * @return {Roo.grid.Grid} this
52138      */
52139     render : function()
52140     {
52141         var c = this.container;
52142         // try to detect autoHeight/width mode
52143         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52144             this.autoHeight = true;
52145         }
52146         var view = this.getView();
52147         view.init(this);
52148
52149         c.on("click", this.onClick, this);
52150         c.on("dblclick", this.onDblClick, this);
52151         c.on("contextmenu", this.onContextMenu, this);
52152         c.on("keydown", this.onKeyDown, this);
52153         if (Roo.isTouch) {
52154             c.on("touchstart", this.onTouchStart, this);
52155         }
52156
52157         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52158
52159         this.getSelectionModel().init(this);
52160
52161         view.render();
52162
52163         if(this.loadMask){
52164             this.loadMask = new Roo.LoadMask(this.container,
52165                     Roo.apply({store:this.dataSource}, this.loadMask));
52166         }
52167         
52168         
52169         if (this.toolbar && this.toolbar.xtype) {
52170             this.toolbar.container = this.getView().getHeaderPanel(true);
52171             this.toolbar = new Roo.Toolbar(this.toolbar);
52172         }
52173         if (this.footer && this.footer.xtype) {
52174             this.footer.dataSource = this.getDataSource();
52175             this.footer.container = this.getView().getFooterPanel(true);
52176             this.footer = Roo.factory(this.footer, Roo);
52177         }
52178         if (this.dropTarget && this.dropTarget.xtype) {
52179             delete this.dropTarget.xtype;
52180             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52181         }
52182         
52183         
52184         this.rendered = true;
52185         this.fireEvent('render', this);
52186         return this;
52187     },
52188
52189         /**
52190          * Reconfigures the grid to use a different Store and Column Model.
52191          * The View will be bound to the new objects and refreshed.
52192          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52193          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52194          */
52195     reconfigure : function(dataSource, colModel){
52196         if(this.loadMask){
52197             this.loadMask.destroy();
52198             this.loadMask = new Roo.LoadMask(this.container,
52199                     Roo.apply({store:dataSource}, this.loadMask));
52200         }
52201         this.view.bind(dataSource, colModel);
52202         this.dataSource = dataSource;
52203         this.colModel = colModel;
52204         this.view.refresh(true);
52205     },
52206
52207     // private
52208     onKeyDown : function(e){
52209         this.fireEvent("keydown", e);
52210     },
52211
52212     /**
52213      * Destroy this grid.
52214      * @param {Boolean} removeEl True to remove the element
52215      */
52216     destroy : function(removeEl, keepListeners){
52217         if(this.loadMask){
52218             this.loadMask.destroy();
52219         }
52220         var c = this.container;
52221         c.removeAllListeners();
52222         this.view.destroy();
52223         this.colModel.purgeListeners();
52224         if(!keepListeners){
52225             this.purgeListeners();
52226         }
52227         c.update("");
52228         if(removeEl === true){
52229             c.remove();
52230         }
52231     },
52232
52233     // private
52234     processEvent : function(name, e){
52235         // does this fire select???
52236         Roo.log('grid:processEvent '  + name);
52237         
52238         if (name != 'touchstart' ) {
52239             this.fireEvent(name, e);    
52240         }
52241         
52242         var t = e.getTarget();
52243         var v = this.view;
52244         var header = v.findHeaderIndex(t);
52245         if(header !== false){
52246             var ename = name == 'touchstart' ? 'click' : name;
52247              
52248             this.fireEvent("header" + ename, this, header, e);
52249         }else{
52250             var row = v.findRowIndex(t);
52251             var cell = v.findCellIndex(t);
52252             if (name == 'touchstart') {
52253                 // first touch is always a click.
52254                 // hopefull this happens after selection is updated.?
52255                 name = false;
52256                 
52257                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52258                     var cs = this.selModel.getSelectedCell();
52259                     if (row == cs[0] && cell == cs[1]){
52260                         name = 'dblclick';
52261                     }
52262                 }
52263                 if (typeof(this.selModel.getSelections) != 'undefined') {
52264                     var cs = this.selModel.getSelections();
52265                     var ds = this.dataSource;
52266                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52267                         name = 'dblclick';
52268                     }
52269                 }
52270                 if (!name) {
52271                     return;
52272                 }
52273             }
52274             
52275             
52276             if(row !== false){
52277                 this.fireEvent("row" + name, this, row, e);
52278                 if(cell !== false){
52279                     this.fireEvent("cell" + name, this, row, cell, e);
52280                 }
52281             }
52282         }
52283     },
52284
52285     // private
52286     onClick : function(e){
52287         this.processEvent("click", e);
52288     },
52289    // private
52290     onTouchStart : function(e){
52291         this.processEvent("touchstart", e);
52292     },
52293
52294     // private
52295     onContextMenu : function(e, t){
52296         this.processEvent("contextmenu", e);
52297     },
52298
52299     // private
52300     onDblClick : function(e){
52301         this.processEvent("dblclick", e);
52302     },
52303
52304     // private
52305     walkCells : function(row, col, step, fn, scope){
52306         var cm = this.colModel, clen = cm.getColumnCount();
52307         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52308         if(step < 0){
52309             if(col < 0){
52310                 row--;
52311                 first = false;
52312             }
52313             while(row >= 0){
52314                 if(!first){
52315                     col = clen-1;
52316                 }
52317                 first = false;
52318                 while(col >= 0){
52319                     if(fn.call(scope || this, row, col, cm) === true){
52320                         return [row, col];
52321                     }
52322                     col--;
52323                 }
52324                 row--;
52325             }
52326         } else {
52327             if(col >= clen){
52328                 row++;
52329                 first = false;
52330             }
52331             while(row < rlen){
52332                 if(!first){
52333                     col = 0;
52334                 }
52335                 first = false;
52336                 while(col < clen){
52337                     if(fn.call(scope || this, row, col, cm) === true){
52338                         return [row, col];
52339                     }
52340                     col++;
52341                 }
52342                 row++;
52343             }
52344         }
52345         return null;
52346     },
52347
52348     // private
52349     getSelections : function(){
52350         return this.selModel.getSelections();
52351     },
52352
52353     /**
52354      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52355      * but if manual update is required this method will initiate it.
52356      */
52357     autoSize : function(){
52358         if(this.rendered){
52359             this.view.layout();
52360             if(this.view.adjustForScroll){
52361                 this.view.adjustForScroll();
52362             }
52363         }
52364     },
52365
52366     /**
52367      * Returns the grid's underlying element.
52368      * @return {Element} The element
52369      */
52370     getGridEl : function(){
52371         return this.container;
52372     },
52373
52374     // private for compatibility, overridden by editor grid
52375     stopEditing : function(){},
52376
52377     /**
52378      * Returns the grid's SelectionModel.
52379      * @return {SelectionModel}
52380      */
52381     getSelectionModel : function(){
52382         if(!this.selModel){
52383             this.selModel = new Roo.grid.RowSelectionModel();
52384         }
52385         return this.selModel;
52386     },
52387
52388     /**
52389      * Returns the grid's DataSource.
52390      * @return {DataSource}
52391      */
52392     getDataSource : function(){
52393         return this.dataSource;
52394     },
52395
52396     /**
52397      * Returns the grid's ColumnModel.
52398      * @return {ColumnModel}
52399      */
52400     getColumnModel : function(){
52401         return this.colModel;
52402     },
52403
52404     /**
52405      * Returns the grid's GridView object.
52406      * @return {GridView}
52407      */
52408     getView : function(){
52409         if(!this.view){
52410             this.view = new Roo.grid.GridView(this.viewConfig);
52411         }
52412         return this.view;
52413     },
52414     /**
52415      * Called to get grid's drag proxy text, by default returns this.ddText.
52416      * @return {String}
52417      */
52418     getDragDropText : function(){
52419         var count = this.selModel.getCount();
52420         return String.format(this.ddText, count, count == 1 ? '' : 's');
52421     }
52422 });
52423 /**
52424  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52425  * %0 is replaced with the number of selected rows.
52426  * @type String
52427  */
52428 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52429  * Based on:
52430  * Ext JS Library 1.1.1
52431  * Copyright(c) 2006-2007, Ext JS, LLC.
52432  *
52433  * Originally Released Under LGPL - original licence link has changed is not relivant.
52434  *
52435  * Fork - LGPL
52436  * <script type="text/javascript">
52437  */
52438  
52439 Roo.grid.AbstractGridView = function(){
52440         this.grid = null;
52441         
52442         this.events = {
52443             "beforerowremoved" : true,
52444             "beforerowsinserted" : true,
52445             "beforerefresh" : true,
52446             "rowremoved" : true,
52447             "rowsinserted" : true,
52448             "rowupdated" : true,
52449             "refresh" : true
52450         };
52451     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52452 };
52453
52454 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52455     rowClass : "x-grid-row",
52456     cellClass : "x-grid-cell",
52457     tdClass : "x-grid-td",
52458     hdClass : "x-grid-hd",
52459     splitClass : "x-grid-hd-split",
52460     
52461     init: function(grid){
52462         this.grid = grid;
52463                 var cid = this.grid.getGridEl().id;
52464         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52465         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52466         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52467         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52468         },
52469         
52470     getColumnRenderers : function(){
52471         var renderers = [];
52472         var cm = this.grid.colModel;
52473         var colCount = cm.getColumnCount();
52474         for(var i = 0; i < colCount; i++){
52475             renderers[i] = cm.getRenderer(i);
52476         }
52477         return renderers;
52478     },
52479     
52480     getColumnIds : function(){
52481         var ids = [];
52482         var cm = this.grid.colModel;
52483         var colCount = cm.getColumnCount();
52484         for(var i = 0; i < colCount; i++){
52485             ids[i] = cm.getColumnId(i);
52486         }
52487         return ids;
52488     },
52489     
52490     getDataIndexes : function(){
52491         if(!this.indexMap){
52492             this.indexMap = this.buildIndexMap();
52493         }
52494         return this.indexMap.colToData;
52495     },
52496     
52497     getColumnIndexByDataIndex : function(dataIndex){
52498         if(!this.indexMap){
52499             this.indexMap = this.buildIndexMap();
52500         }
52501         return this.indexMap.dataToCol[dataIndex];
52502     },
52503     
52504     /**
52505      * Set a css style for a column dynamically. 
52506      * @param {Number} colIndex The index of the column
52507      * @param {String} name The css property name
52508      * @param {String} value The css value
52509      */
52510     setCSSStyle : function(colIndex, name, value){
52511         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52512         Roo.util.CSS.updateRule(selector, name, value);
52513     },
52514     
52515     generateRules : function(cm){
52516         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52517         Roo.util.CSS.removeStyleSheet(rulesId);
52518         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52519             var cid = cm.getColumnId(i);
52520             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52521                          this.tdSelector, cid, " {\n}\n",
52522                          this.hdSelector, cid, " {\n}\n",
52523                          this.splitSelector, cid, " {\n}\n");
52524         }
52525         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52526     }
52527 });/*
52528  * Based on:
52529  * Ext JS Library 1.1.1
52530  * Copyright(c) 2006-2007, Ext JS, LLC.
52531  *
52532  * Originally Released Under LGPL - original licence link has changed is not relivant.
52533  *
52534  * Fork - LGPL
52535  * <script type="text/javascript">
52536  */
52537
52538 // private
52539 // This is a support class used internally by the Grid components
52540 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52541     this.grid = grid;
52542     this.view = grid.getView();
52543     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52544     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52545     if(hd2){
52546         this.setHandleElId(Roo.id(hd));
52547         this.setOuterHandleElId(Roo.id(hd2));
52548     }
52549     this.scroll = false;
52550 };
52551 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52552     maxDragWidth: 120,
52553     getDragData : function(e){
52554         var t = Roo.lib.Event.getTarget(e);
52555         var h = this.view.findHeaderCell(t);
52556         if(h){
52557             return {ddel: h.firstChild, header:h};
52558         }
52559         return false;
52560     },
52561
52562     onInitDrag : function(e){
52563         this.view.headersDisabled = true;
52564         var clone = this.dragData.ddel.cloneNode(true);
52565         clone.id = Roo.id();
52566         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52567         this.proxy.update(clone);
52568         return true;
52569     },
52570
52571     afterValidDrop : function(){
52572         var v = this.view;
52573         setTimeout(function(){
52574             v.headersDisabled = false;
52575         }, 50);
52576     },
52577
52578     afterInvalidDrop : function(){
52579         var v = this.view;
52580         setTimeout(function(){
52581             v.headersDisabled = false;
52582         }, 50);
52583     }
52584 });
52585 /*
52586  * Based on:
52587  * Ext JS Library 1.1.1
52588  * Copyright(c) 2006-2007, Ext JS, LLC.
52589  *
52590  * Originally Released Under LGPL - original licence link has changed is not relivant.
52591  *
52592  * Fork - LGPL
52593  * <script type="text/javascript">
52594  */
52595 // private
52596 // This is a support class used internally by the Grid components
52597 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52598     this.grid = grid;
52599     this.view = grid.getView();
52600     // split the proxies so they don't interfere with mouse events
52601     this.proxyTop = Roo.DomHelper.append(document.body, {
52602         cls:"col-move-top", html:"&#160;"
52603     }, true);
52604     this.proxyBottom = Roo.DomHelper.append(document.body, {
52605         cls:"col-move-bottom", html:"&#160;"
52606     }, true);
52607     this.proxyTop.hide = this.proxyBottom.hide = function(){
52608         this.setLeftTop(-100,-100);
52609         this.setStyle("visibility", "hidden");
52610     };
52611     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52612     // temporarily disabled
52613     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52614     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52615 };
52616 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52617     proxyOffsets : [-4, -9],
52618     fly: Roo.Element.fly,
52619
52620     getTargetFromEvent : function(e){
52621         var t = Roo.lib.Event.getTarget(e);
52622         var cindex = this.view.findCellIndex(t);
52623         if(cindex !== false){
52624             return this.view.getHeaderCell(cindex);
52625         }
52626         return null;
52627     },
52628
52629     nextVisible : function(h){
52630         var v = this.view, cm = this.grid.colModel;
52631         h = h.nextSibling;
52632         while(h){
52633             if(!cm.isHidden(v.getCellIndex(h))){
52634                 return h;
52635             }
52636             h = h.nextSibling;
52637         }
52638         return null;
52639     },
52640
52641     prevVisible : function(h){
52642         var v = this.view, cm = this.grid.colModel;
52643         h = h.prevSibling;
52644         while(h){
52645             if(!cm.isHidden(v.getCellIndex(h))){
52646                 return h;
52647             }
52648             h = h.prevSibling;
52649         }
52650         return null;
52651     },
52652
52653     positionIndicator : function(h, n, e){
52654         var x = Roo.lib.Event.getPageX(e);
52655         var r = Roo.lib.Dom.getRegion(n.firstChild);
52656         var px, pt, py = r.top + this.proxyOffsets[1];
52657         if((r.right - x) <= (r.right-r.left)/2){
52658             px = r.right+this.view.borderWidth;
52659             pt = "after";
52660         }else{
52661             px = r.left;
52662             pt = "before";
52663         }
52664         var oldIndex = this.view.getCellIndex(h);
52665         var newIndex = this.view.getCellIndex(n);
52666
52667         if(this.grid.colModel.isFixed(newIndex)){
52668             return false;
52669         }
52670
52671         var locked = this.grid.colModel.isLocked(newIndex);
52672
52673         if(pt == "after"){
52674             newIndex++;
52675         }
52676         if(oldIndex < newIndex){
52677             newIndex--;
52678         }
52679         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52680             return false;
52681         }
52682         px +=  this.proxyOffsets[0];
52683         this.proxyTop.setLeftTop(px, py);
52684         this.proxyTop.show();
52685         if(!this.bottomOffset){
52686             this.bottomOffset = this.view.mainHd.getHeight();
52687         }
52688         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52689         this.proxyBottom.show();
52690         return pt;
52691     },
52692
52693     onNodeEnter : function(n, dd, e, data){
52694         if(data.header != n){
52695             this.positionIndicator(data.header, n, e);
52696         }
52697     },
52698
52699     onNodeOver : function(n, dd, e, data){
52700         var result = false;
52701         if(data.header != n){
52702             result = this.positionIndicator(data.header, n, e);
52703         }
52704         if(!result){
52705             this.proxyTop.hide();
52706             this.proxyBottom.hide();
52707         }
52708         return result ? this.dropAllowed : this.dropNotAllowed;
52709     },
52710
52711     onNodeOut : function(n, dd, e, data){
52712         this.proxyTop.hide();
52713         this.proxyBottom.hide();
52714     },
52715
52716     onNodeDrop : function(n, dd, e, data){
52717         var h = data.header;
52718         if(h != n){
52719             var cm = this.grid.colModel;
52720             var x = Roo.lib.Event.getPageX(e);
52721             var r = Roo.lib.Dom.getRegion(n.firstChild);
52722             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52723             var oldIndex = this.view.getCellIndex(h);
52724             var newIndex = this.view.getCellIndex(n);
52725             var locked = cm.isLocked(newIndex);
52726             if(pt == "after"){
52727                 newIndex++;
52728             }
52729             if(oldIndex < newIndex){
52730                 newIndex--;
52731             }
52732             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52733                 return false;
52734             }
52735             cm.setLocked(oldIndex, locked, true);
52736             cm.moveColumn(oldIndex, newIndex);
52737             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52738             return true;
52739         }
52740         return false;
52741     }
52742 });
52743 /*
52744  * Based on:
52745  * Ext JS Library 1.1.1
52746  * Copyright(c) 2006-2007, Ext JS, LLC.
52747  *
52748  * Originally Released Under LGPL - original licence link has changed is not relivant.
52749  *
52750  * Fork - LGPL
52751  * <script type="text/javascript">
52752  */
52753   
52754 /**
52755  * @class Roo.grid.GridView
52756  * @extends Roo.util.Observable
52757  *
52758  * @constructor
52759  * @param {Object} config
52760  */
52761 Roo.grid.GridView = function(config){
52762     Roo.grid.GridView.superclass.constructor.call(this);
52763     this.el = null;
52764
52765     Roo.apply(this, config);
52766 };
52767
52768 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52769
52770     unselectable :  'unselectable="on"',
52771     unselectableCls :  'x-unselectable',
52772     
52773     
52774     rowClass : "x-grid-row",
52775
52776     cellClass : "x-grid-col",
52777
52778     tdClass : "x-grid-td",
52779
52780     hdClass : "x-grid-hd",
52781
52782     splitClass : "x-grid-split",
52783
52784     sortClasses : ["sort-asc", "sort-desc"],
52785
52786     enableMoveAnim : false,
52787
52788     hlColor: "C3DAF9",
52789
52790     dh : Roo.DomHelper,
52791
52792     fly : Roo.Element.fly,
52793
52794     css : Roo.util.CSS,
52795
52796     borderWidth: 1,
52797
52798     splitOffset: 3,
52799
52800     scrollIncrement : 22,
52801
52802     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52803
52804     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52805
52806     bind : function(ds, cm){
52807         if(this.ds){
52808             this.ds.un("load", this.onLoad, this);
52809             this.ds.un("datachanged", this.onDataChange, this);
52810             this.ds.un("add", this.onAdd, this);
52811             this.ds.un("remove", this.onRemove, this);
52812             this.ds.un("update", this.onUpdate, this);
52813             this.ds.un("clear", this.onClear, this);
52814         }
52815         if(ds){
52816             ds.on("load", this.onLoad, this);
52817             ds.on("datachanged", this.onDataChange, this);
52818             ds.on("add", this.onAdd, this);
52819             ds.on("remove", this.onRemove, this);
52820             ds.on("update", this.onUpdate, this);
52821             ds.on("clear", this.onClear, this);
52822         }
52823         this.ds = ds;
52824
52825         if(this.cm){
52826             this.cm.un("widthchange", this.onColWidthChange, this);
52827             this.cm.un("headerchange", this.onHeaderChange, this);
52828             this.cm.un("hiddenchange", this.onHiddenChange, this);
52829             this.cm.un("columnmoved", this.onColumnMove, this);
52830             this.cm.un("columnlockchange", this.onColumnLock, this);
52831         }
52832         if(cm){
52833             this.generateRules(cm);
52834             cm.on("widthchange", this.onColWidthChange, this);
52835             cm.on("headerchange", this.onHeaderChange, this);
52836             cm.on("hiddenchange", this.onHiddenChange, this);
52837             cm.on("columnmoved", this.onColumnMove, this);
52838             cm.on("columnlockchange", this.onColumnLock, this);
52839         }
52840         this.cm = cm;
52841     },
52842
52843     init: function(grid){
52844         Roo.grid.GridView.superclass.init.call(this, grid);
52845
52846         this.bind(grid.dataSource, grid.colModel);
52847
52848         grid.on("headerclick", this.handleHeaderClick, this);
52849
52850         if(grid.trackMouseOver){
52851             grid.on("mouseover", this.onRowOver, this);
52852             grid.on("mouseout", this.onRowOut, this);
52853         }
52854         grid.cancelTextSelection = function(){};
52855         this.gridId = grid.id;
52856
52857         var tpls = this.templates || {};
52858
52859         if(!tpls.master){
52860             tpls.master = new Roo.Template(
52861                '<div class="x-grid" hidefocus="true">',
52862                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52863                   '<div class="x-grid-topbar"></div>',
52864                   '<div class="x-grid-scroller"><div></div></div>',
52865                   '<div class="x-grid-locked">',
52866                       '<div class="x-grid-header">{lockedHeader}</div>',
52867                       '<div class="x-grid-body">{lockedBody}</div>',
52868                   "</div>",
52869                   '<div class="x-grid-viewport">',
52870                       '<div class="x-grid-header">{header}</div>',
52871                       '<div class="x-grid-body">{body}</div>',
52872                   "</div>",
52873                   '<div class="x-grid-bottombar"></div>',
52874                  
52875                   '<div class="x-grid-resize-proxy">&#160;</div>',
52876                "</div>"
52877             );
52878             tpls.master.disableformats = true;
52879         }
52880
52881         if(!tpls.header){
52882             tpls.header = new Roo.Template(
52883                '<table border="0" cellspacing="0" cellpadding="0">',
52884                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52885                "</table>{splits}"
52886             );
52887             tpls.header.disableformats = true;
52888         }
52889         tpls.header.compile();
52890
52891         if(!tpls.hcell){
52892             tpls.hcell = new Roo.Template(
52893                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52894                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52895                 "</div></td>"
52896              );
52897              tpls.hcell.disableFormats = true;
52898         }
52899         tpls.hcell.compile();
52900
52901         if(!tpls.hsplit){
52902             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52903                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52904             tpls.hsplit.disableFormats = true;
52905         }
52906         tpls.hsplit.compile();
52907
52908         if(!tpls.body){
52909             tpls.body = new Roo.Template(
52910                '<table border="0" cellspacing="0" cellpadding="0">',
52911                "<tbody>{rows}</tbody>",
52912                "</table>"
52913             );
52914             tpls.body.disableFormats = true;
52915         }
52916         tpls.body.compile();
52917
52918         if(!tpls.row){
52919             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52920             tpls.row.disableFormats = true;
52921         }
52922         tpls.row.compile();
52923
52924         if(!tpls.cell){
52925             tpls.cell = new Roo.Template(
52926                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52927                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52928                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52929                 "</td>"
52930             );
52931             tpls.cell.disableFormats = true;
52932         }
52933         tpls.cell.compile();
52934
52935         this.templates = tpls;
52936     },
52937
52938     // remap these for backwards compat
52939     onColWidthChange : function(){
52940         this.updateColumns.apply(this, arguments);
52941     },
52942     onHeaderChange : function(){
52943         this.updateHeaders.apply(this, arguments);
52944     }, 
52945     onHiddenChange : function(){
52946         this.handleHiddenChange.apply(this, arguments);
52947     },
52948     onColumnMove : function(){
52949         this.handleColumnMove.apply(this, arguments);
52950     },
52951     onColumnLock : function(){
52952         this.handleLockChange.apply(this, arguments);
52953     },
52954
52955     onDataChange : function(){
52956         this.refresh();
52957         this.updateHeaderSortState();
52958     },
52959
52960     onClear : function(){
52961         this.refresh();
52962     },
52963
52964     onUpdate : function(ds, record){
52965         this.refreshRow(record);
52966     },
52967
52968     refreshRow : function(record){
52969         var ds = this.ds, index;
52970         if(typeof record == 'number'){
52971             index = record;
52972             record = ds.getAt(index);
52973         }else{
52974             index = ds.indexOf(record);
52975         }
52976         this.insertRows(ds, index, index, true);
52977         this.onRemove(ds, record, index+1, true);
52978         this.syncRowHeights(index, index);
52979         this.layout();
52980         this.fireEvent("rowupdated", this, index, record);
52981     },
52982
52983     onAdd : function(ds, records, index){
52984         this.insertRows(ds, index, index + (records.length-1));
52985     },
52986
52987     onRemove : function(ds, record, index, isUpdate){
52988         if(isUpdate !== true){
52989             this.fireEvent("beforerowremoved", this, index, record);
52990         }
52991         var bt = this.getBodyTable(), lt = this.getLockedTable();
52992         if(bt.rows[index]){
52993             bt.firstChild.removeChild(bt.rows[index]);
52994         }
52995         if(lt.rows[index]){
52996             lt.firstChild.removeChild(lt.rows[index]);
52997         }
52998         if(isUpdate !== true){
52999             this.stripeRows(index);
53000             this.syncRowHeights(index, index);
53001             this.layout();
53002             this.fireEvent("rowremoved", this, index, record);
53003         }
53004     },
53005
53006     onLoad : function(){
53007         this.scrollToTop();
53008     },
53009
53010     /**
53011      * Scrolls the grid to the top
53012      */
53013     scrollToTop : function(){
53014         if(this.scroller){
53015             this.scroller.dom.scrollTop = 0;
53016             this.syncScroll();
53017         }
53018     },
53019
53020     /**
53021      * Gets a panel in the header of the grid that can be used for toolbars etc.
53022      * After modifying the contents of this panel a call to grid.autoSize() may be
53023      * required to register any changes in size.
53024      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53025      * @return Roo.Element
53026      */
53027     getHeaderPanel : function(doShow){
53028         if(doShow){
53029             this.headerPanel.show();
53030         }
53031         return this.headerPanel;
53032     },
53033
53034     /**
53035      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53036      * After modifying the contents of this panel a call to grid.autoSize() may be
53037      * required to register any changes in size.
53038      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53039      * @return Roo.Element
53040      */
53041     getFooterPanel : function(doShow){
53042         if(doShow){
53043             this.footerPanel.show();
53044         }
53045         return this.footerPanel;
53046     },
53047
53048     initElements : function(){
53049         var E = Roo.Element;
53050         var el = this.grid.getGridEl().dom.firstChild;
53051         var cs = el.childNodes;
53052
53053         this.el = new E(el);
53054         
53055          this.focusEl = new E(el.firstChild);
53056         this.focusEl.swallowEvent("click", true);
53057         
53058         this.headerPanel = new E(cs[1]);
53059         this.headerPanel.enableDisplayMode("block");
53060
53061         this.scroller = new E(cs[2]);
53062         this.scrollSizer = new E(this.scroller.dom.firstChild);
53063
53064         this.lockedWrap = new E(cs[3]);
53065         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53066         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53067
53068         this.mainWrap = new E(cs[4]);
53069         this.mainHd = new E(this.mainWrap.dom.firstChild);
53070         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53071
53072         this.footerPanel = new E(cs[5]);
53073         this.footerPanel.enableDisplayMode("block");
53074
53075         this.resizeProxy = new E(cs[6]);
53076
53077         this.headerSelector = String.format(
53078            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53079            this.lockedHd.id, this.mainHd.id
53080         );
53081
53082         this.splitterSelector = String.format(
53083            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53084            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53085         );
53086     },
53087     idToCssName : function(s)
53088     {
53089         return s.replace(/[^a-z0-9]+/ig, '-');
53090     },
53091
53092     getHeaderCell : function(index){
53093         return Roo.DomQuery.select(this.headerSelector)[index];
53094     },
53095
53096     getHeaderCellMeasure : function(index){
53097         return this.getHeaderCell(index).firstChild;
53098     },
53099
53100     getHeaderCellText : function(index){
53101         return this.getHeaderCell(index).firstChild.firstChild;
53102     },
53103
53104     getLockedTable : function(){
53105         return this.lockedBody.dom.firstChild;
53106     },
53107
53108     getBodyTable : function(){
53109         return this.mainBody.dom.firstChild;
53110     },
53111
53112     getLockedRow : function(index){
53113         return this.getLockedTable().rows[index];
53114     },
53115
53116     getRow : function(index){
53117         return this.getBodyTable().rows[index];
53118     },
53119
53120     getRowComposite : function(index){
53121         if(!this.rowEl){
53122             this.rowEl = new Roo.CompositeElementLite();
53123         }
53124         var els = [], lrow, mrow;
53125         if(lrow = this.getLockedRow(index)){
53126             els.push(lrow);
53127         }
53128         if(mrow = this.getRow(index)){
53129             els.push(mrow);
53130         }
53131         this.rowEl.elements = els;
53132         return this.rowEl;
53133     },
53134     /**
53135      * Gets the 'td' of the cell
53136      * 
53137      * @param {Integer} rowIndex row to select
53138      * @param {Integer} colIndex column to select
53139      * 
53140      * @return {Object} 
53141      */
53142     getCell : function(rowIndex, colIndex){
53143         var locked = this.cm.getLockedCount();
53144         var source;
53145         if(colIndex < locked){
53146             source = this.lockedBody.dom.firstChild;
53147         }else{
53148             source = this.mainBody.dom.firstChild;
53149             colIndex -= locked;
53150         }
53151         return source.rows[rowIndex].childNodes[colIndex];
53152     },
53153
53154     getCellText : function(rowIndex, colIndex){
53155         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53156     },
53157
53158     getCellBox : function(cell){
53159         var b = this.fly(cell).getBox();
53160         if(Roo.isOpera){ // opera fails to report the Y
53161             b.y = cell.offsetTop + this.mainBody.getY();
53162         }
53163         return b;
53164     },
53165
53166     getCellIndex : function(cell){
53167         var id = String(cell.className).match(this.cellRE);
53168         if(id){
53169             return parseInt(id[1], 10);
53170         }
53171         return 0;
53172     },
53173
53174     findHeaderIndex : function(n){
53175         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53176         return r ? this.getCellIndex(r) : false;
53177     },
53178
53179     findHeaderCell : function(n){
53180         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53181         return r ? r : false;
53182     },
53183
53184     findRowIndex : function(n){
53185         if(!n){
53186             return false;
53187         }
53188         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53189         return r ? r.rowIndex : false;
53190     },
53191
53192     findCellIndex : function(node){
53193         var stop = this.el.dom;
53194         while(node && node != stop){
53195             if(this.findRE.test(node.className)){
53196                 return this.getCellIndex(node);
53197             }
53198             node = node.parentNode;
53199         }
53200         return false;
53201     },
53202
53203     getColumnId : function(index){
53204         return this.cm.getColumnId(index);
53205     },
53206
53207     getSplitters : function()
53208     {
53209         if(this.splitterSelector){
53210            return Roo.DomQuery.select(this.splitterSelector);
53211         }else{
53212             return null;
53213       }
53214     },
53215
53216     getSplitter : function(index){
53217         return this.getSplitters()[index];
53218     },
53219
53220     onRowOver : function(e, t){
53221         var row;
53222         if((row = this.findRowIndex(t)) !== false){
53223             this.getRowComposite(row).addClass("x-grid-row-over");
53224         }
53225     },
53226
53227     onRowOut : function(e, t){
53228         var row;
53229         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53230             this.getRowComposite(row).removeClass("x-grid-row-over");
53231         }
53232     },
53233
53234     renderHeaders : function(){
53235         var cm = this.cm;
53236         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53237         var cb = [], lb = [], sb = [], lsb = [], p = {};
53238         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53239             p.cellId = "x-grid-hd-0-" + i;
53240             p.splitId = "x-grid-csplit-0-" + i;
53241             p.id = cm.getColumnId(i);
53242             p.title = cm.getColumnTooltip(i) || "";
53243             p.value = cm.getColumnHeader(i) || "";
53244             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53245             if(!cm.isLocked(i)){
53246                 cb[cb.length] = ct.apply(p);
53247                 sb[sb.length] = st.apply(p);
53248             }else{
53249                 lb[lb.length] = ct.apply(p);
53250                 lsb[lsb.length] = st.apply(p);
53251             }
53252         }
53253         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53254                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53255     },
53256
53257     updateHeaders : function(){
53258         var html = this.renderHeaders();
53259         this.lockedHd.update(html[0]);
53260         this.mainHd.update(html[1]);
53261     },
53262
53263     /**
53264      * Focuses the specified row.
53265      * @param {Number} row The row index
53266      */
53267     focusRow : function(row)
53268     {
53269         //Roo.log('GridView.focusRow');
53270         var x = this.scroller.dom.scrollLeft;
53271         this.focusCell(row, 0, false);
53272         this.scroller.dom.scrollLeft = x;
53273     },
53274
53275     /**
53276      * Focuses the specified cell.
53277      * @param {Number} row The row index
53278      * @param {Number} col The column index
53279      * @param {Boolean} hscroll false to disable horizontal scrolling
53280      */
53281     focusCell : function(row, col, hscroll)
53282     {
53283         //Roo.log('GridView.focusCell');
53284         var el = this.ensureVisible(row, col, hscroll);
53285         this.focusEl.alignTo(el, "tl-tl");
53286         if(Roo.isGecko){
53287             this.focusEl.focus();
53288         }else{
53289             this.focusEl.focus.defer(1, this.focusEl);
53290         }
53291     },
53292
53293     /**
53294      * Scrolls the specified cell into view
53295      * @param {Number} row The row index
53296      * @param {Number} col The column index
53297      * @param {Boolean} hscroll false to disable horizontal scrolling
53298      */
53299     ensureVisible : function(row, col, hscroll)
53300     {
53301         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53302         //return null; //disable for testing.
53303         if(typeof row != "number"){
53304             row = row.rowIndex;
53305         }
53306         if(row < 0 && row >= this.ds.getCount()){
53307             return  null;
53308         }
53309         col = (col !== undefined ? col : 0);
53310         var cm = this.grid.colModel;
53311         while(cm.isHidden(col)){
53312             col++;
53313         }
53314
53315         var el = this.getCell(row, col);
53316         if(!el){
53317             return null;
53318         }
53319         var c = this.scroller.dom;
53320
53321         var ctop = parseInt(el.offsetTop, 10);
53322         var cleft = parseInt(el.offsetLeft, 10);
53323         var cbot = ctop + el.offsetHeight;
53324         var cright = cleft + el.offsetWidth;
53325         
53326         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53327         var stop = parseInt(c.scrollTop, 10);
53328         var sleft = parseInt(c.scrollLeft, 10);
53329         var sbot = stop + ch;
53330         var sright = sleft + c.clientWidth;
53331         /*
53332         Roo.log('GridView.ensureVisible:' +
53333                 ' ctop:' + ctop +
53334                 ' c.clientHeight:' + c.clientHeight +
53335                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53336                 ' stop:' + stop +
53337                 ' cbot:' + cbot +
53338                 ' sbot:' + sbot +
53339                 ' ch:' + ch  
53340                 );
53341         */
53342         if(ctop < stop){
53343              c.scrollTop = ctop;
53344             //Roo.log("set scrolltop to ctop DISABLE?");
53345         }else if(cbot > sbot){
53346             //Roo.log("set scrolltop to cbot-ch");
53347             c.scrollTop = cbot-ch;
53348         }
53349         
53350         if(hscroll !== false){
53351             if(cleft < sleft){
53352                 c.scrollLeft = cleft;
53353             }else if(cright > sright){
53354                 c.scrollLeft = cright-c.clientWidth;
53355             }
53356         }
53357          
53358         return el;
53359     },
53360
53361     updateColumns : function(){
53362         this.grid.stopEditing();
53363         var cm = this.grid.colModel, colIds = this.getColumnIds();
53364         //var totalWidth = cm.getTotalWidth();
53365         var pos = 0;
53366         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53367             //if(cm.isHidden(i)) continue;
53368             var w = cm.getColumnWidth(i);
53369             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53370             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53371         }
53372         this.updateSplitters();
53373     },
53374
53375     generateRules : function(cm){
53376         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53377         Roo.util.CSS.removeStyleSheet(rulesId);
53378         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53379             var cid = cm.getColumnId(i);
53380             var align = '';
53381             if(cm.config[i].align){
53382                 align = 'text-align:'+cm.config[i].align+';';
53383             }
53384             var hidden = '';
53385             if(cm.isHidden(i)){
53386                 hidden = 'display:none;';
53387             }
53388             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53389             ruleBuf.push(
53390                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53391                     this.hdSelector, cid, " {\n", align, width, "}\n",
53392                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53393                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53394         }
53395         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53396     },
53397
53398     updateSplitters : function(){
53399         var cm = this.cm, s = this.getSplitters();
53400         if(s){ // splitters not created yet
53401             var pos = 0, locked = true;
53402             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53403                 if(cm.isHidden(i)) continue;
53404                 var w = cm.getColumnWidth(i); // make sure it's a number
53405                 if(!cm.isLocked(i) && locked){
53406                     pos = 0;
53407                     locked = false;
53408                 }
53409                 pos += w;
53410                 s[i].style.left = (pos-this.splitOffset) + "px";
53411             }
53412         }
53413     },
53414
53415     handleHiddenChange : function(colModel, colIndex, hidden){
53416         if(hidden){
53417             this.hideColumn(colIndex);
53418         }else{
53419             this.unhideColumn(colIndex);
53420         }
53421     },
53422
53423     hideColumn : function(colIndex){
53424         var cid = this.getColumnId(colIndex);
53425         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53426         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53427         if(Roo.isSafari){
53428             this.updateHeaders();
53429         }
53430         this.updateSplitters();
53431         this.layout();
53432     },
53433
53434     unhideColumn : function(colIndex){
53435         var cid = this.getColumnId(colIndex);
53436         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53437         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53438
53439         if(Roo.isSafari){
53440             this.updateHeaders();
53441         }
53442         this.updateSplitters();
53443         this.layout();
53444     },
53445
53446     insertRows : function(dm, firstRow, lastRow, isUpdate){
53447         if(firstRow == 0 && lastRow == dm.getCount()-1){
53448             this.refresh();
53449         }else{
53450             if(!isUpdate){
53451                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53452             }
53453             var s = this.getScrollState();
53454             var markup = this.renderRows(firstRow, lastRow);
53455             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53456             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53457             this.restoreScroll(s);
53458             if(!isUpdate){
53459                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53460                 this.syncRowHeights(firstRow, lastRow);
53461                 this.stripeRows(firstRow);
53462                 this.layout();
53463             }
53464         }
53465     },
53466
53467     bufferRows : function(markup, target, index){
53468         var before = null, trows = target.rows, tbody = target.tBodies[0];
53469         if(index < trows.length){
53470             before = trows[index];
53471         }
53472         var b = document.createElement("div");
53473         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53474         var rows = b.firstChild.rows;
53475         for(var i = 0, len = rows.length; i < len; i++){
53476             if(before){
53477                 tbody.insertBefore(rows[0], before);
53478             }else{
53479                 tbody.appendChild(rows[0]);
53480             }
53481         }
53482         b.innerHTML = "";
53483         b = null;
53484     },
53485
53486     deleteRows : function(dm, firstRow, lastRow){
53487         if(dm.getRowCount()<1){
53488             this.fireEvent("beforerefresh", this);
53489             this.mainBody.update("");
53490             this.lockedBody.update("");
53491             this.fireEvent("refresh", this);
53492         }else{
53493             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53494             var bt = this.getBodyTable();
53495             var tbody = bt.firstChild;
53496             var rows = bt.rows;
53497             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53498                 tbody.removeChild(rows[firstRow]);
53499             }
53500             this.stripeRows(firstRow);
53501             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53502         }
53503     },
53504
53505     updateRows : function(dataSource, firstRow, lastRow){
53506         var s = this.getScrollState();
53507         this.refresh();
53508         this.restoreScroll(s);
53509     },
53510
53511     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53512         if(!noRefresh){
53513            this.refresh();
53514         }
53515         this.updateHeaderSortState();
53516     },
53517
53518     getScrollState : function(){
53519         
53520         var sb = this.scroller.dom;
53521         return {left: sb.scrollLeft, top: sb.scrollTop};
53522     },
53523
53524     stripeRows : function(startRow){
53525         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53526             return;
53527         }
53528         startRow = startRow || 0;
53529         var rows = this.getBodyTable().rows;
53530         var lrows = this.getLockedTable().rows;
53531         var cls = ' x-grid-row-alt ';
53532         for(var i = startRow, len = rows.length; i < len; i++){
53533             var row = rows[i], lrow = lrows[i];
53534             var isAlt = ((i+1) % 2 == 0);
53535             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53536             if(isAlt == hasAlt){
53537                 continue;
53538             }
53539             if(isAlt){
53540                 row.className += " x-grid-row-alt";
53541             }else{
53542                 row.className = row.className.replace("x-grid-row-alt", "");
53543             }
53544             if(lrow){
53545                 lrow.className = row.className;
53546             }
53547         }
53548     },
53549
53550     restoreScroll : function(state){
53551         //Roo.log('GridView.restoreScroll');
53552         var sb = this.scroller.dom;
53553         sb.scrollLeft = state.left;
53554         sb.scrollTop = state.top;
53555         this.syncScroll();
53556     },
53557
53558     syncScroll : function(){
53559         //Roo.log('GridView.syncScroll');
53560         var sb = this.scroller.dom;
53561         var sh = this.mainHd.dom;
53562         var bs = this.mainBody.dom;
53563         var lv = this.lockedBody.dom;
53564         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53565         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53566     },
53567
53568     handleScroll : function(e){
53569         this.syncScroll();
53570         var sb = this.scroller.dom;
53571         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53572         e.stopEvent();
53573     },
53574
53575     handleWheel : function(e){
53576         var d = e.getWheelDelta();
53577         this.scroller.dom.scrollTop -= d*22;
53578         // set this here to prevent jumpy scrolling on large tables
53579         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53580         e.stopEvent();
53581     },
53582
53583     renderRows : function(startRow, endRow){
53584         // pull in all the crap needed to render rows
53585         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53586         var colCount = cm.getColumnCount();
53587
53588         if(ds.getCount() < 1){
53589             return ["", ""];
53590         }
53591
53592         // build a map for all the columns
53593         var cs = [];
53594         for(var i = 0; i < colCount; i++){
53595             var name = cm.getDataIndex(i);
53596             cs[i] = {
53597                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53598                 renderer : cm.getRenderer(i),
53599                 id : cm.getColumnId(i),
53600                 locked : cm.isLocked(i)
53601             };
53602         }
53603
53604         startRow = startRow || 0;
53605         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53606
53607         // records to render
53608         var rs = ds.getRange(startRow, endRow);
53609
53610         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53611     },
53612
53613     // As much as I hate to duplicate code, this was branched because FireFox really hates
53614     // [].join("") on strings. The performance difference was substantial enough to
53615     // branch this function
53616     doRender : Roo.isGecko ?
53617             function(cs, rs, ds, startRow, colCount, stripe){
53618                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53619                 // buffers
53620                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53621                 
53622                 var hasListener = this.grid.hasListener('rowclass');
53623                 var rowcfg = {};
53624                 for(var j = 0, len = rs.length; j < len; j++){
53625                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53626                     for(var i = 0; i < colCount; i++){
53627                         c = cs[i];
53628                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53629                         p.id = c.id;
53630                         p.css = p.attr = "";
53631                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53632                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53633                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53634                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53635                         }
53636                         var markup = ct.apply(p);
53637                         if(!c.locked){
53638                             cb+= markup;
53639                         }else{
53640                             lcb+= markup;
53641                         }
53642                     }
53643                     var alt = [];
53644                     if(stripe && ((rowIndex+1) % 2 == 0)){
53645                         alt.push("x-grid-row-alt")
53646                     }
53647                     if(r.dirty){
53648                         alt.push(  " x-grid-dirty-row");
53649                     }
53650                     rp.cells = lcb;
53651                     if(this.getRowClass){
53652                         alt.push(this.getRowClass(r, rowIndex));
53653                     }
53654                     if (hasListener) {
53655                         rowcfg = {
53656                              
53657                             record: r,
53658                             rowIndex : rowIndex,
53659                             rowClass : ''
53660                         }
53661                         this.grid.fireEvent('rowclass', this, rowcfg);
53662                         alt.push(rowcfg.rowClass);
53663                     }
53664                     rp.alt = alt.join(" ");
53665                     lbuf+= rt.apply(rp);
53666                     rp.cells = cb;
53667                     buf+=  rt.apply(rp);
53668                 }
53669                 return [lbuf, buf];
53670             } :
53671             function(cs, rs, ds, startRow, colCount, stripe){
53672                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53673                 // buffers
53674                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53675                 var hasListener = this.grid.hasListener('rowclass');
53676  
53677                 var rowcfg = {};
53678                 for(var j = 0, len = rs.length; j < len; j++){
53679                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53680                     for(var i = 0; i < colCount; i++){
53681                         c = cs[i];
53682                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53683                         p.id = c.id;
53684                         p.css = p.attr = "";
53685                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53686                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53687                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53688                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53689                         }
53690                         
53691                         var markup = ct.apply(p);
53692                         if(!c.locked){
53693                             cb[cb.length] = markup;
53694                         }else{
53695                             lcb[lcb.length] = markup;
53696                         }
53697                     }
53698                     var alt = [];
53699                     if(stripe && ((rowIndex+1) % 2 == 0)){
53700                         alt.push( "x-grid-row-alt");
53701                     }
53702                     if(r.dirty){
53703                         alt.push(" x-grid-dirty-row");
53704                     }
53705                     rp.cells = lcb;
53706                     if(this.getRowClass){
53707                         alt.push( this.getRowClass(r, rowIndex));
53708                     }
53709                     if (hasListener) {
53710                         rowcfg = {
53711                              
53712                             record: r,
53713                             rowIndex : rowIndex,
53714                             rowClass : ''
53715                         }
53716                         this.grid.fireEvent('rowclass', this, rowcfg);
53717                         alt.push(rowcfg.rowClass);
53718                     }
53719                     rp.alt = alt.join(" ");
53720                     rp.cells = lcb.join("");
53721                     lbuf[lbuf.length] = rt.apply(rp);
53722                     rp.cells = cb.join("");
53723                     buf[buf.length] =  rt.apply(rp);
53724                 }
53725                 return [lbuf.join(""), buf.join("")];
53726             },
53727
53728     renderBody : function(){
53729         var markup = this.renderRows();
53730         var bt = this.templates.body;
53731         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53732     },
53733
53734     /**
53735      * Refreshes the grid
53736      * @param {Boolean} headersToo
53737      */
53738     refresh : function(headersToo){
53739         this.fireEvent("beforerefresh", this);
53740         this.grid.stopEditing();
53741         var result = this.renderBody();
53742         this.lockedBody.update(result[0]);
53743         this.mainBody.update(result[1]);
53744         if(headersToo === true){
53745             this.updateHeaders();
53746             this.updateColumns();
53747             this.updateSplitters();
53748             this.updateHeaderSortState();
53749         }
53750         this.syncRowHeights();
53751         this.layout();
53752         this.fireEvent("refresh", this);
53753     },
53754
53755     handleColumnMove : function(cm, oldIndex, newIndex){
53756         this.indexMap = null;
53757         var s = this.getScrollState();
53758         this.refresh(true);
53759         this.restoreScroll(s);
53760         this.afterMove(newIndex);
53761     },
53762
53763     afterMove : function(colIndex){
53764         if(this.enableMoveAnim && Roo.enableFx){
53765             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53766         }
53767         // if multisort - fix sortOrder, and reload..
53768         if (this.grid.dataSource.multiSort) {
53769             // the we can call sort again..
53770             var dm = this.grid.dataSource;
53771             var cm = this.grid.colModel;
53772             var so = [];
53773             for(var i = 0; i < cm.config.length; i++ ) {
53774                 
53775                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53776                     continue; // dont' bother, it's not in sort list or being set.
53777                 }
53778                 
53779                 so.push(cm.config[i].dataIndex);
53780             };
53781             dm.sortOrder = so;
53782             dm.load(dm.lastOptions);
53783             
53784             
53785         }
53786         
53787     },
53788
53789     updateCell : function(dm, rowIndex, dataIndex){
53790         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53791         if(typeof colIndex == "undefined"){ // not present in grid
53792             return;
53793         }
53794         var cm = this.grid.colModel;
53795         var cell = this.getCell(rowIndex, colIndex);
53796         var cellText = this.getCellText(rowIndex, colIndex);
53797
53798         var p = {
53799             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53800             id : cm.getColumnId(colIndex),
53801             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53802         };
53803         var renderer = cm.getRenderer(colIndex);
53804         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53805         if(typeof val == "undefined" || val === "") val = "&#160;";
53806         cellText.innerHTML = val;
53807         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53808         this.syncRowHeights(rowIndex, rowIndex);
53809     },
53810
53811     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53812         var maxWidth = 0;
53813         if(this.grid.autoSizeHeaders){
53814             var h = this.getHeaderCellMeasure(colIndex);
53815             maxWidth = Math.max(maxWidth, h.scrollWidth);
53816         }
53817         var tb, index;
53818         if(this.cm.isLocked(colIndex)){
53819             tb = this.getLockedTable();
53820             index = colIndex;
53821         }else{
53822             tb = this.getBodyTable();
53823             index = colIndex - this.cm.getLockedCount();
53824         }
53825         if(tb && tb.rows){
53826             var rows = tb.rows;
53827             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53828             for(var i = 0; i < stopIndex; i++){
53829                 var cell = rows[i].childNodes[index].firstChild;
53830                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53831             }
53832         }
53833         return maxWidth + /*margin for error in IE*/ 5;
53834     },
53835     /**
53836      * Autofit a column to its content.
53837      * @param {Number} colIndex
53838      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53839      */
53840      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53841          if(this.cm.isHidden(colIndex)){
53842              return; // can't calc a hidden column
53843          }
53844         if(forceMinSize){
53845             var cid = this.cm.getColumnId(colIndex);
53846             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53847            if(this.grid.autoSizeHeaders){
53848                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53849            }
53850         }
53851         var newWidth = this.calcColumnWidth(colIndex);
53852         this.cm.setColumnWidth(colIndex,
53853             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53854         if(!suppressEvent){
53855             this.grid.fireEvent("columnresize", colIndex, newWidth);
53856         }
53857     },
53858
53859     /**
53860      * Autofits all columns to their content and then expands to fit any extra space in the grid
53861      */
53862      autoSizeColumns : function(){
53863         var cm = this.grid.colModel;
53864         var colCount = cm.getColumnCount();
53865         for(var i = 0; i < colCount; i++){
53866             this.autoSizeColumn(i, true, true);
53867         }
53868         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53869             this.fitColumns();
53870         }else{
53871             this.updateColumns();
53872             this.layout();
53873         }
53874     },
53875
53876     /**
53877      * Autofits all columns to the grid's width proportionate with their current size
53878      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53879      */
53880     fitColumns : function(reserveScrollSpace){
53881         var cm = this.grid.colModel;
53882         var colCount = cm.getColumnCount();
53883         var cols = [];
53884         var width = 0;
53885         var i, w;
53886         for (i = 0; i < colCount; i++){
53887             if(!cm.isHidden(i) && !cm.isFixed(i)){
53888                 w = cm.getColumnWidth(i);
53889                 cols.push(i);
53890                 cols.push(w);
53891                 width += w;
53892             }
53893         }
53894         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53895         if(reserveScrollSpace){
53896             avail -= 17;
53897         }
53898         var frac = (avail - cm.getTotalWidth())/width;
53899         while (cols.length){
53900             w = cols.pop();
53901             i = cols.pop();
53902             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53903         }
53904         this.updateColumns();
53905         this.layout();
53906     },
53907
53908     onRowSelect : function(rowIndex){
53909         var row = this.getRowComposite(rowIndex);
53910         row.addClass("x-grid-row-selected");
53911     },
53912
53913     onRowDeselect : function(rowIndex){
53914         var row = this.getRowComposite(rowIndex);
53915         row.removeClass("x-grid-row-selected");
53916     },
53917
53918     onCellSelect : function(row, col){
53919         var cell = this.getCell(row, col);
53920         if(cell){
53921             Roo.fly(cell).addClass("x-grid-cell-selected");
53922         }
53923     },
53924
53925     onCellDeselect : function(row, col){
53926         var cell = this.getCell(row, col);
53927         if(cell){
53928             Roo.fly(cell).removeClass("x-grid-cell-selected");
53929         }
53930     },
53931
53932     updateHeaderSortState : function(){
53933         
53934         // sort state can be single { field: xxx, direction : yyy}
53935         // or   { xxx=>ASC , yyy : DESC ..... }
53936         
53937         var mstate = {};
53938         if (!this.ds.multiSort) { 
53939             var state = this.ds.getSortState();
53940             if(!state){
53941                 return;
53942             }
53943             mstate[state.field] = state.direction;
53944             // FIXME... - this is not used here.. but might be elsewhere..
53945             this.sortState = state;
53946             
53947         } else {
53948             mstate = this.ds.sortToggle;
53949         }
53950         //remove existing sort classes..
53951         
53952         var sc = this.sortClasses;
53953         var hds = this.el.select(this.headerSelector).removeClass(sc);
53954         
53955         for(var f in mstate) {
53956         
53957             var sortColumn = this.cm.findColumnIndex(f);
53958             
53959             if(sortColumn != -1){
53960                 var sortDir = mstate[f];        
53961                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53962             }
53963         }
53964         
53965          
53966         
53967     },
53968
53969
53970     handleHeaderClick : function(g, index,e){
53971         
53972         Roo.log("header click");
53973         
53974         if (Roo.isTouch) {
53975             // touch events on header are handled by context
53976             this.handleHdCtx(g,index,e);
53977             return;
53978         }
53979         
53980         
53981         if(this.headersDisabled){
53982             return;
53983         }
53984         var dm = g.dataSource, cm = g.colModel;
53985         if(!cm.isSortable(index)){
53986             return;
53987         }
53988         g.stopEditing();
53989         
53990         if (dm.multiSort) {
53991             // update the sortOrder
53992             var so = [];
53993             for(var i = 0; i < cm.config.length; i++ ) {
53994                 
53995                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53996                     continue; // dont' bother, it's not in sort list or being set.
53997                 }
53998                 
53999                 so.push(cm.config[i].dataIndex);
54000             };
54001             dm.sortOrder = so;
54002         }
54003         
54004         
54005         dm.sort(cm.getDataIndex(index));
54006     },
54007
54008
54009     destroy : function(){
54010         if(this.colMenu){
54011             this.colMenu.removeAll();
54012             Roo.menu.MenuMgr.unregister(this.colMenu);
54013             this.colMenu.getEl().remove();
54014             delete this.colMenu;
54015         }
54016         if(this.hmenu){
54017             this.hmenu.removeAll();
54018             Roo.menu.MenuMgr.unregister(this.hmenu);
54019             this.hmenu.getEl().remove();
54020             delete this.hmenu;
54021         }
54022         if(this.grid.enableColumnMove){
54023             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54024             if(dds){
54025                 for(var dd in dds){
54026                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54027                         var elid = dds[dd].dragElId;
54028                         dds[dd].unreg();
54029                         Roo.get(elid).remove();
54030                     } else if(dds[dd].config.isTarget){
54031                         dds[dd].proxyTop.remove();
54032                         dds[dd].proxyBottom.remove();
54033                         dds[dd].unreg();
54034                     }
54035                     if(Roo.dd.DDM.locationCache[dd]){
54036                         delete Roo.dd.DDM.locationCache[dd];
54037                     }
54038                 }
54039                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54040             }
54041         }
54042         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54043         this.bind(null, null);
54044         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54045     },
54046
54047     handleLockChange : function(){
54048         this.refresh(true);
54049     },
54050
54051     onDenyColumnLock : function(){
54052
54053     },
54054
54055     onDenyColumnHide : function(){
54056
54057     },
54058
54059     handleHdMenuClick : function(item){
54060         var index = this.hdCtxIndex;
54061         var cm = this.cm, ds = this.ds;
54062         switch(item.id){
54063             case "asc":
54064                 ds.sort(cm.getDataIndex(index), "ASC");
54065                 break;
54066             case "desc":
54067                 ds.sort(cm.getDataIndex(index), "DESC");
54068                 break;
54069             case "lock":
54070                 var lc = cm.getLockedCount();
54071                 if(cm.getColumnCount(true) <= lc+1){
54072                     this.onDenyColumnLock();
54073                     return;
54074                 }
54075                 if(lc != index){
54076                     cm.setLocked(index, true, true);
54077                     cm.moveColumn(index, lc);
54078                     this.grid.fireEvent("columnmove", index, lc);
54079                 }else{
54080                     cm.setLocked(index, true);
54081                 }
54082             break;
54083             case "unlock":
54084                 var lc = cm.getLockedCount();
54085                 if((lc-1) != index){
54086                     cm.setLocked(index, false, true);
54087                     cm.moveColumn(index, lc-1);
54088                     this.grid.fireEvent("columnmove", index, lc-1);
54089                 }else{
54090                     cm.setLocked(index, false);
54091                 }
54092             break;
54093             case 'wider': // used to expand cols on touch..
54094             case 'narrow':
54095                 var cw = cm.getColumnWidth(index);
54096                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54097                 cw = Math.max(0, cw);
54098                 cw = Math.min(cw,4000);
54099                 cm.setColumnWidth(index, cw);
54100                 break;
54101                 
54102             default:
54103                 index = cm.getIndexById(item.id.substr(4));
54104                 if(index != -1){
54105                     if(item.checked && cm.getColumnCount(true) <= 1){
54106                         this.onDenyColumnHide();
54107                         return false;
54108                     }
54109                     cm.setHidden(index, item.checked);
54110                 }
54111         }
54112         return true;
54113     },
54114
54115     beforeColMenuShow : function(){
54116         var cm = this.cm,  colCount = cm.getColumnCount();
54117         this.colMenu.removeAll();
54118         for(var i = 0; i < colCount; i++){
54119             this.colMenu.add(new Roo.menu.CheckItem({
54120                 id: "col-"+cm.getColumnId(i),
54121                 text: cm.getColumnHeader(i),
54122                 checked: !cm.isHidden(i),
54123                 hideOnClick:false
54124             }));
54125         }
54126     },
54127
54128     handleHdCtx : function(g, index, e){
54129         e.stopEvent();
54130         var hd = this.getHeaderCell(index);
54131         this.hdCtxIndex = index;
54132         var ms = this.hmenu.items, cm = this.cm;
54133         ms.get("asc").setDisabled(!cm.isSortable(index));
54134         ms.get("desc").setDisabled(!cm.isSortable(index));
54135         if(this.grid.enableColLock !== false){
54136             ms.get("lock").setDisabled(cm.isLocked(index));
54137             ms.get("unlock").setDisabled(!cm.isLocked(index));
54138         }
54139         this.hmenu.show(hd, "tl-bl");
54140     },
54141
54142     handleHdOver : function(e){
54143         var hd = this.findHeaderCell(e.getTarget());
54144         if(hd && !this.headersDisabled){
54145             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54146                this.fly(hd).addClass("x-grid-hd-over");
54147             }
54148         }
54149     },
54150
54151     handleHdOut : function(e){
54152         var hd = this.findHeaderCell(e.getTarget());
54153         if(hd){
54154             this.fly(hd).removeClass("x-grid-hd-over");
54155         }
54156     },
54157
54158     handleSplitDblClick : function(e, t){
54159         var i = this.getCellIndex(t);
54160         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54161             this.autoSizeColumn(i, true);
54162             this.layout();
54163         }
54164     },
54165
54166     render : function(){
54167
54168         var cm = this.cm;
54169         var colCount = cm.getColumnCount();
54170
54171         if(this.grid.monitorWindowResize === true){
54172             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54173         }
54174         var header = this.renderHeaders();
54175         var body = this.templates.body.apply({rows:""});
54176         var html = this.templates.master.apply({
54177             lockedBody: body,
54178             body: body,
54179             lockedHeader: header[0],
54180             header: header[1]
54181         });
54182
54183         //this.updateColumns();
54184
54185         this.grid.getGridEl().dom.innerHTML = html;
54186
54187         this.initElements();
54188         
54189         // a kludge to fix the random scolling effect in webkit
54190         this.el.on("scroll", function() {
54191             this.el.dom.scrollTop=0; // hopefully not recursive..
54192         },this);
54193
54194         this.scroller.on("scroll", this.handleScroll, this);
54195         this.lockedBody.on("mousewheel", this.handleWheel, this);
54196         this.mainBody.on("mousewheel", this.handleWheel, this);
54197
54198         this.mainHd.on("mouseover", this.handleHdOver, this);
54199         this.mainHd.on("mouseout", this.handleHdOut, this);
54200         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54201                 {delegate: "."+this.splitClass});
54202
54203         this.lockedHd.on("mouseover", this.handleHdOver, this);
54204         this.lockedHd.on("mouseout", this.handleHdOut, this);
54205         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54206                 {delegate: "."+this.splitClass});
54207
54208         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54209             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54210         }
54211
54212         this.updateSplitters();
54213
54214         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54215             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54216             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54217         }
54218
54219         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54220             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54221             this.hmenu.add(
54222                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54223                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54224             );
54225             if(this.grid.enableColLock !== false){
54226                 this.hmenu.add('-',
54227                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54228                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54229                 );
54230             }
54231             if (Roo.isTouch) {
54232                  this.hmenu.add('-',
54233                     {id:"wider", text: this.columnsWiderText},
54234                     {id:"narrow", text: this.columnsNarrowText }
54235                 );
54236                 
54237                  
54238             }
54239             
54240             if(this.grid.enableColumnHide !== false){
54241
54242                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54243                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54244                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54245
54246                 this.hmenu.add('-',
54247                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54248                 );
54249             }
54250             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54251
54252             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54253         }
54254
54255         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54256             this.dd = new Roo.grid.GridDragZone(this.grid, {
54257                 ddGroup : this.grid.ddGroup || 'GridDD'
54258             });
54259             
54260         }
54261
54262         /*
54263         for(var i = 0; i < colCount; i++){
54264             if(cm.isHidden(i)){
54265                 this.hideColumn(i);
54266             }
54267             if(cm.config[i].align){
54268                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54269                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54270             }
54271         }*/
54272         
54273         this.updateHeaderSortState();
54274
54275         this.beforeInitialResize();
54276         this.layout(true);
54277
54278         // two part rendering gives faster view to the user
54279         this.renderPhase2.defer(1, this);
54280     },
54281
54282     renderPhase2 : function(){
54283         // render the rows now
54284         this.refresh();
54285         if(this.grid.autoSizeColumns){
54286             this.autoSizeColumns();
54287         }
54288     },
54289
54290     beforeInitialResize : function(){
54291
54292     },
54293
54294     onColumnSplitterMoved : function(i, w){
54295         this.userResized = true;
54296         var cm = this.grid.colModel;
54297         cm.setColumnWidth(i, w, true);
54298         var cid = cm.getColumnId(i);
54299         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54300         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54301         this.updateSplitters();
54302         this.layout();
54303         this.grid.fireEvent("columnresize", i, w);
54304     },
54305
54306     syncRowHeights : function(startIndex, endIndex){
54307         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54308             startIndex = startIndex || 0;
54309             var mrows = this.getBodyTable().rows;
54310             var lrows = this.getLockedTable().rows;
54311             var len = mrows.length-1;
54312             endIndex = Math.min(endIndex || len, len);
54313             for(var i = startIndex; i <= endIndex; i++){
54314                 var m = mrows[i], l = lrows[i];
54315                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54316                 m.style.height = l.style.height = h + "px";
54317             }
54318         }
54319     },
54320
54321     layout : function(initialRender, is2ndPass){
54322         var g = this.grid;
54323         var auto = g.autoHeight;
54324         var scrollOffset = 16;
54325         var c = g.getGridEl(), cm = this.cm,
54326                 expandCol = g.autoExpandColumn,
54327                 gv = this;
54328         //c.beginMeasure();
54329
54330         if(!c.dom.offsetWidth){ // display:none?
54331             if(initialRender){
54332                 this.lockedWrap.show();
54333                 this.mainWrap.show();
54334             }
54335             return;
54336         }
54337
54338         var hasLock = this.cm.isLocked(0);
54339
54340         var tbh = this.headerPanel.getHeight();
54341         var bbh = this.footerPanel.getHeight();
54342
54343         if(auto){
54344             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54345             var newHeight = ch + c.getBorderWidth("tb");
54346             if(g.maxHeight){
54347                 newHeight = Math.min(g.maxHeight, newHeight);
54348             }
54349             c.setHeight(newHeight);
54350         }
54351
54352         if(g.autoWidth){
54353             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54354         }
54355
54356         var s = this.scroller;
54357
54358         var csize = c.getSize(true);
54359
54360         this.el.setSize(csize.width, csize.height);
54361
54362         this.headerPanel.setWidth(csize.width);
54363         this.footerPanel.setWidth(csize.width);
54364
54365         var hdHeight = this.mainHd.getHeight();
54366         var vw = csize.width;
54367         var vh = csize.height - (tbh + bbh);
54368
54369         s.setSize(vw, vh);
54370
54371         var bt = this.getBodyTable();
54372         var ltWidth = hasLock ?
54373                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54374
54375         var scrollHeight = bt.offsetHeight;
54376         var scrollWidth = ltWidth + bt.offsetWidth;
54377         var vscroll = false, hscroll = false;
54378
54379         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54380
54381         var lw = this.lockedWrap, mw = this.mainWrap;
54382         var lb = this.lockedBody, mb = this.mainBody;
54383
54384         setTimeout(function(){
54385             var t = s.dom.offsetTop;
54386             var w = s.dom.clientWidth,
54387                 h = s.dom.clientHeight;
54388
54389             lw.setTop(t);
54390             lw.setSize(ltWidth, h);
54391
54392             mw.setLeftTop(ltWidth, t);
54393             mw.setSize(w-ltWidth, h);
54394
54395             lb.setHeight(h-hdHeight);
54396             mb.setHeight(h-hdHeight);
54397
54398             if(is2ndPass !== true && !gv.userResized && expandCol){
54399                 // high speed resize without full column calculation
54400                 
54401                 var ci = cm.getIndexById(expandCol);
54402                 if (ci < 0) {
54403                     ci = cm.findColumnIndex(expandCol);
54404                 }
54405                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54406                 var expandId = cm.getColumnId(ci);
54407                 var  tw = cm.getTotalWidth(false);
54408                 var currentWidth = cm.getColumnWidth(ci);
54409                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54410                 if(currentWidth != cw){
54411                     cm.setColumnWidth(ci, cw, true);
54412                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54413                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54414                     gv.updateSplitters();
54415                     gv.layout(false, true);
54416                 }
54417             }
54418
54419             if(initialRender){
54420                 lw.show();
54421                 mw.show();
54422             }
54423             //c.endMeasure();
54424         }, 10);
54425     },
54426
54427     onWindowResize : function(){
54428         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54429             return;
54430         }
54431         this.layout();
54432     },
54433
54434     appendFooter : function(parentEl){
54435         return null;
54436     },
54437
54438     sortAscText : "Sort Ascending",
54439     sortDescText : "Sort Descending",
54440     lockText : "Lock Column",
54441     unlockText : "Unlock Column",
54442     columnsText : "Columns",
54443  
54444     columnsWiderText : "Wider",
54445     columnsNarrowText : "Thinner"
54446 });
54447
54448
54449 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54450     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54451     this.proxy.el.addClass('x-grid3-col-dd');
54452 };
54453
54454 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54455     handleMouseDown : function(e){
54456
54457     },
54458
54459     callHandleMouseDown : function(e){
54460         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54461     }
54462 });
54463 /*
54464  * Based on:
54465  * Ext JS Library 1.1.1
54466  * Copyright(c) 2006-2007, Ext JS, LLC.
54467  *
54468  * Originally Released Under LGPL - original licence link has changed is not relivant.
54469  *
54470  * Fork - LGPL
54471  * <script type="text/javascript">
54472  */
54473  
54474 // private
54475 // This is a support class used internally by the Grid components
54476 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54477     this.grid = grid;
54478     this.view = grid.getView();
54479     this.proxy = this.view.resizeProxy;
54480     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54481         "gridSplitters" + this.grid.getGridEl().id, {
54482         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54483     });
54484     this.setHandleElId(Roo.id(hd));
54485     this.setOuterHandleElId(Roo.id(hd2));
54486     this.scroll = false;
54487 };
54488 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54489     fly: Roo.Element.fly,
54490
54491     b4StartDrag : function(x, y){
54492         this.view.headersDisabled = true;
54493         this.proxy.setHeight(this.view.mainWrap.getHeight());
54494         var w = this.cm.getColumnWidth(this.cellIndex);
54495         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54496         this.resetConstraints();
54497         this.setXConstraint(minw, 1000);
54498         this.setYConstraint(0, 0);
54499         this.minX = x - minw;
54500         this.maxX = x + 1000;
54501         this.startPos = x;
54502         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54503     },
54504
54505
54506     handleMouseDown : function(e){
54507         ev = Roo.EventObject.setEvent(e);
54508         var t = this.fly(ev.getTarget());
54509         if(t.hasClass("x-grid-split")){
54510             this.cellIndex = this.view.getCellIndex(t.dom);
54511             this.split = t.dom;
54512             this.cm = this.grid.colModel;
54513             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54514                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54515             }
54516         }
54517     },
54518
54519     endDrag : function(e){
54520         this.view.headersDisabled = false;
54521         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54522         var diff = endX - this.startPos;
54523         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54524     },
54525
54526     autoOffset : function(){
54527         this.setDelta(0,0);
54528     }
54529 });/*
54530  * Based on:
54531  * Ext JS Library 1.1.1
54532  * Copyright(c) 2006-2007, Ext JS, LLC.
54533  *
54534  * Originally Released Under LGPL - original licence link has changed is not relivant.
54535  *
54536  * Fork - LGPL
54537  * <script type="text/javascript">
54538  */
54539  
54540 // private
54541 // This is a support class used internally by the Grid components
54542 Roo.grid.GridDragZone = function(grid, config){
54543     this.view = grid.getView();
54544     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54545     if(this.view.lockedBody){
54546         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54547         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54548     }
54549     this.scroll = false;
54550     this.grid = grid;
54551     this.ddel = document.createElement('div');
54552     this.ddel.className = 'x-grid-dd-wrap';
54553 };
54554
54555 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54556     ddGroup : "GridDD",
54557
54558     getDragData : function(e){
54559         var t = Roo.lib.Event.getTarget(e);
54560         var rowIndex = this.view.findRowIndex(t);
54561         var sm = this.grid.selModel;
54562             
54563         //Roo.log(rowIndex);
54564         
54565         if (sm.getSelectedCell) {
54566             // cell selection..
54567             if (!sm.getSelectedCell()) {
54568                 return false;
54569             }
54570             if (rowIndex != sm.getSelectedCell()[0]) {
54571                 return false;
54572             }
54573         
54574         }
54575         
54576         if(rowIndex !== false){
54577             
54578             // if editorgrid.. 
54579             
54580             
54581             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54582                
54583             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54584               //  
54585             //}
54586             if (e.hasModifier()){
54587                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54588             }
54589             
54590             Roo.log("getDragData");
54591             
54592             return {
54593                 grid: this.grid,
54594                 ddel: this.ddel,
54595                 rowIndex: rowIndex,
54596                 selections:sm.getSelections ? sm.getSelections() : (
54597                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54598                 )
54599             };
54600         }
54601         return false;
54602     },
54603
54604     onInitDrag : function(e){
54605         var data = this.dragData;
54606         this.ddel.innerHTML = this.grid.getDragDropText();
54607         this.proxy.update(this.ddel);
54608         // fire start drag?
54609     },
54610
54611     afterRepair : function(){
54612         this.dragging = false;
54613     },
54614
54615     getRepairXY : function(e, data){
54616         return false;
54617     },
54618
54619     onEndDrag : function(data, e){
54620         // fire end drag?
54621     },
54622
54623     onValidDrop : function(dd, e, id){
54624         // fire drag drop?
54625         this.hideProxy();
54626     },
54627
54628     beforeInvalidDrop : function(e, id){
54629
54630     }
54631 });/*
54632  * Based on:
54633  * Ext JS Library 1.1.1
54634  * Copyright(c) 2006-2007, Ext JS, LLC.
54635  *
54636  * Originally Released Under LGPL - original licence link has changed is not relivant.
54637  *
54638  * Fork - LGPL
54639  * <script type="text/javascript">
54640  */
54641  
54642
54643 /**
54644  * @class Roo.grid.ColumnModel
54645  * @extends Roo.util.Observable
54646  * This is the default implementation of a ColumnModel used by the Grid. It defines
54647  * the columns in the grid.
54648  * <br>Usage:<br>
54649  <pre><code>
54650  var colModel = new Roo.grid.ColumnModel([
54651         {header: "Ticker", width: 60, sortable: true, locked: true},
54652         {header: "Company Name", width: 150, sortable: true},
54653         {header: "Market Cap.", width: 100, sortable: true},
54654         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54655         {header: "Employees", width: 100, sortable: true, resizable: false}
54656  ]);
54657  </code></pre>
54658  * <p>
54659  
54660  * The config options listed for this class are options which may appear in each
54661  * individual column definition.
54662  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54663  * @constructor
54664  * @param {Object} config An Array of column config objects. See this class's
54665  * config objects for details.
54666 */
54667 Roo.grid.ColumnModel = function(config){
54668         /**
54669      * The config passed into the constructor
54670      */
54671     this.config = config;
54672     this.lookup = {};
54673
54674     // if no id, create one
54675     // if the column does not have a dataIndex mapping,
54676     // map it to the order it is in the config
54677     for(var i = 0, len = config.length; i < len; i++){
54678         var c = config[i];
54679         if(typeof c.dataIndex == "undefined"){
54680             c.dataIndex = i;
54681         }
54682         if(typeof c.renderer == "string"){
54683             c.renderer = Roo.util.Format[c.renderer];
54684         }
54685         if(typeof c.id == "undefined"){
54686             c.id = Roo.id();
54687         }
54688         if(c.editor && c.editor.xtype){
54689             c.editor  = Roo.factory(c.editor, Roo.grid);
54690         }
54691         if(c.editor && c.editor.isFormField){
54692             c.editor = new Roo.grid.GridEditor(c.editor);
54693         }
54694         this.lookup[c.id] = c;
54695     }
54696
54697     /**
54698      * The width of columns which have no width specified (defaults to 100)
54699      * @type Number
54700      */
54701     this.defaultWidth = 100;
54702
54703     /**
54704      * Default sortable of columns which have no sortable specified (defaults to false)
54705      * @type Boolean
54706      */
54707     this.defaultSortable = false;
54708
54709     this.addEvents({
54710         /**
54711              * @event widthchange
54712              * Fires when the width of a column changes.
54713              * @param {ColumnModel} this
54714              * @param {Number} columnIndex The column index
54715              * @param {Number} newWidth The new width
54716              */
54717             "widthchange": true,
54718         /**
54719              * @event headerchange
54720              * Fires when the text of a header changes.
54721              * @param {ColumnModel} this
54722              * @param {Number} columnIndex The column index
54723              * @param {Number} newText The new header text
54724              */
54725             "headerchange": true,
54726         /**
54727              * @event hiddenchange
54728              * Fires when a column is hidden or "unhidden".
54729              * @param {ColumnModel} this
54730              * @param {Number} columnIndex The column index
54731              * @param {Boolean} hidden true if hidden, false otherwise
54732              */
54733             "hiddenchange": true,
54734             /**
54735          * @event columnmoved
54736          * Fires when a column is moved.
54737          * @param {ColumnModel} this
54738          * @param {Number} oldIndex
54739          * @param {Number} newIndex
54740          */
54741         "columnmoved" : true,
54742         /**
54743          * @event columlockchange
54744          * Fires when a column's locked state is changed
54745          * @param {ColumnModel} this
54746          * @param {Number} colIndex
54747          * @param {Boolean} locked true if locked
54748          */
54749         "columnlockchange" : true
54750     });
54751     Roo.grid.ColumnModel.superclass.constructor.call(this);
54752 };
54753 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54754     /**
54755      * @cfg {String} header The header text to display in the Grid view.
54756      */
54757     /**
54758      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54759      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54760      * specified, the column's index is used as an index into the Record's data Array.
54761      */
54762     /**
54763      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54764      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54765      */
54766     /**
54767      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54768      * Defaults to the value of the {@link #defaultSortable} property.
54769      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54770      */
54771     /**
54772      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54773      */
54774     /**
54775      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54776      */
54777     /**
54778      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54779      */
54780     /**
54781      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54782      */
54783     /**
54784      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54785      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54786      * default renderer uses the raw data value.
54787      */
54788        /**
54789      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54790      */
54791     /**
54792      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54793      */
54794
54795     /**
54796      * Returns the id of the column at the specified index.
54797      * @param {Number} index The column index
54798      * @return {String} the id
54799      */
54800     getColumnId : function(index){
54801         return this.config[index].id;
54802     },
54803
54804     /**
54805      * Returns the column for a specified id.
54806      * @param {String} id The column id
54807      * @return {Object} the column
54808      */
54809     getColumnById : function(id){
54810         return this.lookup[id];
54811     },
54812
54813     
54814     /**
54815      * Returns the column for a specified dataIndex.
54816      * @param {String} dataIndex The column dataIndex
54817      * @return {Object|Boolean} the column or false if not found
54818      */
54819     getColumnByDataIndex: function(dataIndex){
54820         var index = this.findColumnIndex(dataIndex);
54821         return index > -1 ? this.config[index] : false;
54822     },
54823     
54824     /**
54825      * Returns the index for a specified column id.
54826      * @param {String} id The column id
54827      * @return {Number} the index, or -1 if not found
54828      */
54829     getIndexById : function(id){
54830         for(var i = 0, len = this.config.length; i < len; i++){
54831             if(this.config[i].id == id){
54832                 return i;
54833             }
54834         }
54835         return -1;
54836     },
54837     
54838     /**
54839      * Returns the index for a specified column dataIndex.
54840      * @param {String} dataIndex The column dataIndex
54841      * @return {Number} the index, or -1 if not found
54842      */
54843     
54844     findColumnIndex : function(dataIndex){
54845         for(var i = 0, len = this.config.length; i < len; i++){
54846             if(this.config[i].dataIndex == dataIndex){
54847                 return i;
54848             }
54849         }
54850         return -1;
54851     },
54852     
54853     
54854     moveColumn : function(oldIndex, newIndex){
54855         var c = this.config[oldIndex];
54856         this.config.splice(oldIndex, 1);
54857         this.config.splice(newIndex, 0, c);
54858         this.dataMap = null;
54859         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54860     },
54861
54862     isLocked : function(colIndex){
54863         return this.config[colIndex].locked === true;
54864     },
54865
54866     setLocked : function(colIndex, value, suppressEvent){
54867         if(this.isLocked(colIndex) == value){
54868             return;
54869         }
54870         this.config[colIndex].locked = value;
54871         if(!suppressEvent){
54872             this.fireEvent("columnlockchange", this, colIndex, value);
54873         }
54874     },
54875
54876     getTotalLockedWidth : function(){
54877         var totalWidth = 0;
54878         for(var i = 0; i < this.config.length; i++){
54879             if(this.isLocked(i) && !this.isHidden(i)){
54880                 this.totalWidth += this.getColumnWidth(i);
54881             }
54882         }
54883         return totalWidth;
54884     },
54885
54886     getLockedCount : function(){
54887         for(var i = 0, len = this.config.length; i < len; i++){
54888             if(!this.isLocked(i)){
54889                 return i;
54890             }
54891         }
54892     },
54893
54894     /**
54895      * Returns the number of columns.
54896      * @return {Number}
54897      */
54898     getColumnCount : function(visibleOnly){
54899         if(visibleOnly === true){
54900             var c = 0;
54901             for(var i = 0, len = this.config.length; i < len; i++){
54902                 if(!this.isHidden(i)){
54903                     c++;
54904                 }
54905             }
54906             return c;
54907         }
54908         return this.config.length;
54909     },
54910
54911     /**
54912      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54913      * @param {Function} fn
54914      * @param {Object} scope (optional)
54915      * @return {Array} result
54916      */
54917     getColumnsBy : function(fn, scope){
54918         var r = [];
54919         for(var i = 0, len = this.config.length; i < len; i++){
54920             var c = this.config[i];
54921             if(fn.call(scope||this, c, i) === true){
54922                 r[r.length] = c;
54923             }
54924         }
54925         return r;
54926     },
54927
54928     /**
54929      * Returns true if the specified column is sortable.
54930      * @param {Number} col The column index
54931      * @return {Boolean}
54932      */
54933     isSortable : function(col){
54934         if(typeof this.config[col].sortable == "undefined"){
54935             return this.defaultSortable;
54936         }
54937         return this.config[col].sortable;
54938     },
54939
54940     /**
54941      * Returns the rendering (formatting) function defined for the column.
54942      * @param {Number} col The column index.
54943      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54944      */
54945     getRenderer : function(col){
54946         if(!this.config[col].renderer){
54947             return Roo.grid.ColumnModel.defaultRenderer;
54948         }
54949         return this.config[col].renderer;
54950     },
54951
54952     /**
54953      * Sets the rendering (formatting) function for a column.
54954      * @param {Number} col The column index
54955      * @param {Function} fn The function to use to process the cell's raw data
54956      * to return HTML markup for the grid view. The render function is called with
54957      * the following parameters:<ul>
54958      * <li>Data value.</li>
54959      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54960      * <li>css A CSS style string to apply to the table cell.</li>
54961      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54962      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54963      * <li>Row index</li>
54964      * <li>Column index</li>
54965      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54966      */
54967     setRenderer : function(col, fn){
54968         this.config[col].renderer = fn;
54969     },
54970
54971     /**
54972      * Returns the width for the specified column.
54973      * @param {Number} col The column index
54974      * @return {Number}
54975      */
54976     getColumnWidth : function(col){
54977         return this.config[col].width * 1 || this.defaultWidth;
54978     },
54979
54980     /**
54981      * Sets the width for a column.
54982      * @param {Number} col The column index
54983      * @param {Number} width The new width
54984      */
54985     setColumnWidth : function(col, width, suppressEvent){
54986         this.config[col].width = width;
54987         this.totalWidth = null;
54988         if(!suppressEvent){
54989              this.fireEvent("widthchange", this, col, width);
54990         }
54991     },
54992
54993     /**
54994      * Returns the total width of all columns.
54995      * @param {Boolean} includeHidden True to include hidden column widths
54996      * @return {Number}
54997      */
54998     getTotalWidth : function(includeHidden){
54999         if(!this.totalWidth){
55000             this.totalWidth = 0;
55001             for(var i = 0, len = this.config.length; i < len; i++){
55002                 if(includeHidden || !this.isHidden(i)){
55003                     this.totalWidth += this.getColumnWidth(i);
55004                 }
55005             }
55006         }
55007         return this.totalWidth;
55008     },
55009
55010     /**
55011      * Returns the header for the specified column.
55012      * @param {Number} col The column index
55013      * @return {String}
55014      */
55015     getColumnHeader : function(col){
55016         return this.config[col].header;
55017     },
55018
55019     /**
55020      * Sets the header for a column.
55021      * @param {Number} col The column index
55022      * @param {String} header The new header
55023      */
55024     setColumnHeader : function(col, header){
55025         this.config[col].header = header;
55026         this.fireEvent("headerchange", this, col, header);
55027     },
55028
55029     /**
55030      * Returns the tooltip for the specified column.
55031      * @param {Number} col The column index
55032      * @return {String}
55033      */
55034     getColumnTooltip : function(col){
55035             return this.config[col].tooltip;
55036     },
55037     /**
55038      * Sets the tooltip for a column.
55039      * @param {Number} col The column index
55040      * @param {String} tooltip The new tooltip
55041      */
55042     setColumnTooltip : function(col, tooltip){
55043             this.config[col].tooltip = tooltip;
55044     },
55045
55046     /**
55047      * Returns the dataIndex for the specified column.
55048      * @param {Number} col The column index
55049      * @return {Number}
55050      */
55051     getDataIndex : function(col){
55052         return this.config[col].dataIndex;
55053     },
55054
55055     /**
55056      * Sets the dataIndex for a column.
55057      * @param {Number} col The column index
55058      * @param {Number} dataIndex The new dataIndex
55059      */
55060     setDataIndex : function(col, dataIndex){
55061         this.config[col].dataIndex = dataIndex;
55062     },
55063
55064     
55065     
55066     /**
55067      * Returns true if the cell is editable.
55068      * @param {Number} colIndex The column index
55069      * @param {Number} rowIndex The row index
55070      * @return {Boolean}
55071      */
55072     isCellEditable : function(colIndex, rowIndex){
55073         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55074     },
55075
55076     /**
55077      * Returns the editor defined for the cell/column.
55078      * return false or null to disable editing.
55079      * @param {Number} colIndex The column index
55080      * @param {Number} rowIndex The row index
55081      * @return {Object}
55082      */
55083     getCellEditor : function(colIndex, rowIndex){
55084         return this.config[colIndex].editor;
55085     },
55086
55087     /**
55088      * Sets if a column is editable.
55089      * @param {Number} col The column index
55090      * @param {Boolean} editable True if the column is editable
55091      */
55092     setEditable : function(col, editable){
55093         this.config[col].editable = editable;
55094     },
55095
55096
55097     /**
55098      * Returns true if the column is hidden.
55099      * @param {Number} colIndex The column index
55100      * @return {Boolean}
55101      */
55102     isHidden : function(colIndex){
55103         return this.config[colIndex].hidden;
55104     },
55105
55106
55107     /**
55108      * Returns true if the column width cannot be changed
55109      */
55110     isFixed : function(colIndex){
55111         return this.config[colIndex].fixed;
55112     },
55113
55114     /**
55115      * Returns true if the column can be resized
55116      * @return {Boolean}
55117      */
55118     isResizable : function(colIndex){
55119         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55120     },
55121     /**
55122      * Sets if a column is hidden.
55123      * @param {Number} colIndex The column index
55124      * @param {Boolean} hidden True if the column is hidden
55125      */
55126     setHidden : function(colIndex, hidden){
55127         this.config[colIndex].hidden = hidden;
55128         this.totalWidth = null;
55129         this.fireEvent("hiddenchange", this, colIndex, hidden);
55130     },
55131
55132     /**
55133      * Sets the editor for a column.
55134      * @param {Number} col The column index
55135      * @param {Object} editor The editor object
55136      */
55137     setEditor : function(col, editor){
55138         this.config[col].editor = editor;
55139     }
55140 });
55141
55142 Roo.grid.ColumnModel.defaultRenderer = function(value){
55143         if(typeof value == "string" && value.length < 1){
55144             return "&#160;";
55145         }
55146         return value;
55147 };
55148
55149 // Alias for backwards compatibility
55150 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55151 /*
55152  * Based on:
55153  * Ext JS Library 1.1.1
55154  * Copyright(c) 2006-2007, Ext JS, LLC.
55155  *
55156  * Originally Released Under LGPL - original licence link has changed is not relivant.
55157  *
55158  * Fork - LGPL
55159  * <script type="text/javascript">
55160  */
55161
55162 /**
55163  * @class Roo.grid.AbstractSelectionModel
55164  * @extends Roo.util.Observable
55165  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55166  * implemented by descendant classes.  This class should not be directly instantiated.
55167  * @constructor
55168  */
55169 Roo.grid.AbstractSelectionModel = function(){
55170     this.locked = false;
55171     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55172 };
55173
55174 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55175     /** @ignore Called by the grid automatically. Do not call directly. */
55176     init : function(grid){
55177         this.grid = grid;
55178         this.initEvents();
55179     },
55180
55181     /**
55182      * Locks the selections.
55183      */
55184     lock : function(){
55185         this.locked = true;
55186     },
55187
55188     /**
55189      * Unlocks the selections.
55190      */
55191     unlock : function(){
55192         this.locked = false;
55193     },
55194
55195     /**
55196      * Returns true if the selections are locked.
55197      * @return {Boolean}
55198      */
55199     isLocked : function(){
55200         return this.locked;
55201     }
55202 });/*
55203  * Based on:
55204  * Ext JS Library 1.1.1
55205  * Copyright(c) 2006-2007, Ext JS, LLC.
55206  *
55207  * Originally Released Under LGPL - original licence link has changed is not relivant.
55208  *
55209  * Fork - LGPL
55210  * <script type="text/javascript">
55211  */
55212 /**
55213  * @extends Roo.grid.AbstractSelectionModel
55214  * @class Roo.grid.RowSelectionModel
55215  * The default SelectionModel used by {@link Roo.grid.Grid}.
55216  * It supports multiple selections and keyboard selection/navigation. 
55217  * @constructor
55218  * @param {Object} config
55219  */
55220 Roo.grid.RowSelectionModel = function(config){
55221     Roo.apply(this, config);
55222     this.selections = new Roo.util.MixedCollection(false, function(o){
55223         return o.id;
55224     });
55225
55226     this.last = false;
55227     this.lastActive = false;
55228
55229     this.addEvents({
55230         /**
55231              * @event selectionchange
55232              * Fires when the selection changes
55233              * @param {SelectionModel} this
55234              */
55235             "selectionchange" : true,
55236         /**
55237              * @event afterselectionchange
55238              * Fires after the selection changes (eg. by key press or clicking)
55239              * @param {SelectionModel} this
55240              */
55241             "afterselectionchange" : true,
55242         /**
55243              * @event beforerowselect
55244              * Fires when a row is selected being selected, return false to cancel.
55245              * @param {SelectionModel} this
55246              * @param {Number} rowIndex The selected index
55247              * @param {Boolean} keepExisting False if other selections will be cleared
55248              */
55249             "beforerowselect" : true,
55250         /**
55251              * @event rowselect
55252              * Fires when a row is selected.
55253              * @param {SelectionModel} this
55254              * @param {Number} rowIndex The selected index
55255              * @param {Roo.data.Record} r The record
55256              */
55257             "rowselect" : true,
55258         /**
55259              * @event rowdeselect
55260              * Fires when a row is deselected.
55261              * @param {SelectionModel} this
55262              * @param {Number} rowIndex The selected index
55263              */
55264         "rowdeselect" : true
55265     });
55266     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55267     this.locked = false;
55268 };
55269
55270 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55271     /**
55272      * @cfg {Boolean} singleSelect
55273      * True to allow selection of only one row at a time (defaults to false)
55274      */
55275     singleSelect : false,
55276
55277     // private
55278     initEvents : function(){
55279
55280         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55281             this.grid.on("mousedown", this.handleMouseDown, this);
55282         }else{ // allow click to work like normal
55283             this.grid.on("rowclick", this.handleDragableRowClick, this);
55284         }
55285
55286         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55287             "up" : function(e){
55288                 if(!e.shiftKey){
55289                     this.selectPrevious(e.shiftKey);
55290                 }else if(this.last !== false && this.lastActive !== false){
55291                     var last = this.last;
55292                     this.selectRange(this.last,  this.lastActive-1);
55293                     this.grid.getView().focusRow(this.lastActive);
55294                     if(last !== false){
55295                         this.last = last;
55296                     }
55297                 }else{
55298                     this.selectFirstRow();
55299                 }
55300                 this.fireEvent("afterselectionchange", this);
55301             },
55302             "down" : function(e){
55303                 if(!e.shiftKey){
55304                     this.selectNext(e.shiftKey);
55305                 }else if(this.last !== false && this.lastActive !== false){
55306                     var last = this.last;
55307                     this.selectRange(this.last,  this.lastActive+1);
55308                     this.grid.getView().focusRow(this.lastActive);
55309                     if(last !== false){
55310                         this.last = last;
55311                     }
55312                 }else{
55313                     this.selectFirstRow();
55314                 }
55315                 this.fireEvent("afterselectionchange", this);
55316             },
55317             scope: this
55318         });
55319
55320         var view = this.grid.view;
55321         view.on("refresh", this.onRefresh, this);
55322         view.on("rowupdated", this.onRowUpdated, this);
55323         view.on("rowremoved", this.onRemove, this);
55324     },
55325
55326     // private
55327     onRefresh : function(){
55328         var ds = this.grid.dataSource, i, v = this.grid.view;
55329         var s = this.selections;
55330         s.each(function(r){
55331             if((i = ds.indexOfId(r.id)) != -1){
55332                 v.onRowSelect(i);
55333             }else{
55334                 s.remove(r);
55335             }
55336         });
55337     },
55338
55339     // private
55340     onRemove : function(v, index, r){
55341         this.selections.remove(r);
55342     },
55343
55344     // private
55345     onRowUpdated : function(v, index, r){
55346         if(this.isSelected(r)){
55347             v.onRowSelect(index);
55348         }
55349     },
55350
55351     /**
55352      * Select records.
55353      * @param {Array} records The records to select
55354      * @param {Boolean} keepExisting (optional) True to keep existing selections
55355      */
55356     selectRecords : function(records, keepExisting){
55357         if(!keepExisting){
55358             this.clearSelections();
55359         }
55360         var ds = this.grid.dataSource;
55361         for(var i = 0, len = records.length; i < len; i++){
55362             this.selectRow(ds.indexOf(records[i]), true);
55363         }
55364     },
55365
55366     /**
55367      * Gets the number of selected rows.
55368      * @return {Number}
55369      */
55370     getCount : function(){
55371         return this.selections.length;
55372     },
55373
55374     /**
55375      * Selects the first row in the grid.
55376      */
55377     selectFirstRow : function(){
55378         this.selectRow(0);
55379     },
55380
55381     /**
55382      * Select the last row.
55383      * @param {Boolean} keepExisting (optional) True to keep existing selections
55384      */
55385     selectLastRow : function(keepExisting){
55386         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55387     },
55388
55389     /**
55390      * Selects the row immediately following the last selected row.
55391      * @param {Boolean} keepExisting (optional) True to keep existing selections
55392      */
55393     selectNext : function(keepExisting){
55394         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55395             this.selectRow(this.last+1, keepExisting);
55396             this.grid.getView().focusRow(this.last);
55397         }
55398     },
55399
55400     /**
55401      * Selects the row that precedes the last selected row.
55402      * @param {Boolean} keepExisting (optional) True to keep existing selections
55403      */
55404     selectPrevious : function(keepExisting){
55405         if(this.last){
55406             this.selectRow(this.last-1, keepExisting);
55407             this.grid.getView().focusRow(this.last);
55408         }
55409     },
55410
55411     /**
55412      * Returns the selected records
55413      * @return {Array} Array of selected records
55414      */
55415     getSelections : function(){
55416         return [].concat(this.selections.items);
55417     },
55418
55419     /**
55420      * Returns the first selected record.
55421      * @return {Record}
55422      */
55423     getSelected : function(){
55424         return this.selections.itemAt(0);
55425     },
55426
55427
55428     /**
55429      * Clears all selections.
55430      */
55431     clearSelections : function(fast){
55432         if(this.locked) return;
55433         if(fast !== true){
55434             var ds = this.grid.dataSource;
55435             var s = this.selections;
55436             s.each(function(r){
55437                 this.deselectRow(ds.indexOfId(r.id));
55438             }, this);
55439             s.clear();
55440         }else{
55441             this.selections.clear();
55442         }
55443         this.last = false;
55444     },
55445
55446
55447     /**
55448      * Selects all rows.
55449      */
55450     selectAll : function(){
55451         if(this.locked) return;
55452         this.selections.clear();
55453         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55454             this.selectRow(i, true);
55455         }
55456     },
55457
55458     /**
55459      * Returns True if there is a selection.
55460      * @return {Boolean}
55461      */
55462     hasSelection : function(){
55463         return this.selections.length > 0;
55464     },
55465
55466     /**
55467      * Returns True if the specified row is selected.
55468      * @param {Number/Record} record The record or index of the record to check
55469      * @return {Boolean}
55470      */
55471     isSelected : function(index){
55472         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55473         return (r && this.selections.key(r.id) ? true : false);
55474     },
55475
55476     /**
55477      * Returns True if the specified record id is selected.
55478      * @param {String} id The id of record to check
55479      * @return {Boolean}
55480      */
55481     isIdSelected : function(id){
55482         return (this.selections.key(id) ? true : false);
55483     },
55484
55485     // private
55486     handleMouseDown : function(e, t){
55487         var view = this.grid.getView(), rowIndex;
55488         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55489             return;
55490         };
55491         if(e.shiftKey && this.last !== false){
55492             var last = this.last;
55493             this.selectRange(last, rowIndex, e.ctrlKey);
55494             this.last = last; // reset the last
55495             view.focusRow(rowIndex);
55496         }else{
55497             var isSelected = this.isSelected(rowIndex);
55498             if(e.button !== 0 && isSelected){
55499                 view.focusRow(rowIndex);
55500             }else if(e.ctrlKey && isSelected){
55501                 this.deselectRow(rowIndex);
55502             }else if(!isSelected){
55503                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55504                 view.focusRow(rowIndex);
55505             }
55506         }
55507         this.fireEvent("afterselectionchange", this);
55508     },
55509     // private
55510     handleDragableRowClick :  function(grid, rowIndex, e) 
55511     {
55512         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55513             this.selectRow(rowIndex, false);
55514             grid.view.focusRow(rowIndex);
55515              this.fireEvent("afterselectionchange", this);
55516         }
55517     },
55518     
55519     /**
55520      * Selects multiple rows.
55521      * @param {Array} rows Array of the indexes of the row to select
55522      * @param {Boolean} keepExisting (optional) True to keep existing selections
55523      */
55524     selectRows : function(rows, keepExisting){
55525         if(!keepExisting){
55526             this.clearSelections();
55527         }
55528         for(var i = 0, len = rows.length; i < len; i++){
55529             this.selectRow(rows[i], true);
55530         }
55531     },
55532
55533     /**
55534      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55535      * @param {Number} startRow The index of the first row in the range
55536      * @param {Number} endRow The index of the last row in the range
55537      * @param {Boolean} keepExisting (optional) True to retain existing selections
55538      */
55539     selectRange : function(startRow, endRow, keepExisting){
55540         if(this.locked) return;
55541         if(!keepExisting){
55542             this.clearSelections();
55543         }
55544         if(startRow <= endRow){
55545             for(var i = startRow; i <= endRow; i++){
55546                 this.selectRow(i, true);
55547             }
55548         }else{
55549             for(var i = startRow; i >= endRow; i--){
55550                 this.selectRow(i, true);
55551             }
55552         }
55553     },
55554
55555     /**
55556      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55557      * @param {Number} startRow The index of the first row in the range
55558      * @param {Number} endRow The index of the last row in the range
55559      */
55560     deselectRange : function(startRow, endRow, preventViewNotify){
55561         if(this.locked) return;
55562         for(var i = startRow; i <= endRow; i++){
55563             this.deselectRow(i, preventViewNotify);
55564         }
55565     },
55566
55567     /**
55568      * Selects a row.
55569      * @param {Number} row The index of the row to select
55570      * @param {Boolean} keepExisting (optional) True to keep existing selections
55571      */
55572     selectRow : function(index, keepExisting, preventViewNotify){
55573         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55574         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55575             if(!keepExisting || this.singleSelect){
55576                 this.clearSelections();
55577             }
55578             var r = this.grid.dataSource.getAt(index);
55579             this.selections.add(r);
55580             this.last = this.lastActive = index;
55581             if(!preventViewNotify){
55582                 this.grid.getView().onRowSelect(index);
55583             }
55584             this.fireEvent("rowselect", this, index, r);
55585             this.fireEvent("selectionchange", this);
55586         }
55587     },
55588
55589     /**
55590      * Deselects a row.
55591      * @param {Number} row The index of the row to deselect
55592      */
55593     deselectRow : function(index, preventViewNotify){
55594         if(this.locked) return;
55595         if(this.last == index){
55596             this.last = false;
55597         }
55598         if(this.lastActive == index){
55599             this.lastActive = false;
55600         }
55601         var r = this.grid.dataSource.getAt(index);
55602         this.selections.remove(r);
55603         if(!preventViewNotify){
55604             this.grid.getView().onRowDeselect(index);
55605         }
55606         this.fireEvent("rowdeselect", this, index);
55607         this.fireEvent("selectionchange", this);
55608     },
55609
55610     // private
55611     restoreLast : function(){
55612         if(this._last){
55613             this.last = this._last;
55614         }
55615     },
55616
55617     // private
55618     acceptsNav : function(row, col, cm){
55619         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55620     },
55621
55622     // private
55623     onEditorKey : function(field, e){
55624         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55625         if(k == e.TAB){
55626             e.stopEvent();
55627             ed.completeEdit();
55628             if(e.shiftKey){
55629                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55630             }else{
55631                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55632             }
55633         }else if(k == e.ENTER && !e.ctrlKey){
55634             e.stopEvent();
55635             ed.completeEdit();
55636             if(e.shiftKey){
55637                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55638             }else{
55639                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55640             }
55641         }else if(k == e.ESC){
55642             ed.cancelEdit();
55643         }
55644         if(newCell){
55645             g.startEditing(newCell[0], newCell[1]);
55646         }
55647     }
55648 });/*
55649  * Based on:
55650  * Ext JS Library 1.1.1
55651  * Copyright(c) 2006-2007, Ext JS, LLC.
55652  *
55653  * Originally Released Under LGPL - original licence link has changed is not relivant.
55654  *
55655  * Fork - LGPL
55656  * <script type="text/javascript">
55657  */
55658 /**
55659  * @class Roo.grid.CellSelectionModel
55660  * @extends Roo.grid.AbstractSelectionModel
55661  * This class provides the basic implementation for cell selection in a grid.
55662  * @constructor
55663  * @param {Object} config The object containing the configuration of this model.
55664  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55665  */
55666 Roo.grid.CellSelectionModel = function(config){
55667     Roo.apply(this, config);
55668
55669     this.selection = null;
55670
55671     this.addEvents({
55672         /**
55673              * @event beforerowselect
55674              * Fires before a cell is selected.
55675              * @param {SelectionModel} this
55676              * @param {Number} rowIndex The selected row index
55677              * @param {Number} colIndex The selected cell index
55678              */
55679             "beforecellselect" : true,
55680         /**
55681              * @event cellselect
55682              * Fires when a cell is selected.
55683              * @param {SelectionModel} this
55684              * @param {Number} rowIndex The selected row index
55685              * @param {Number} colIndex The selected cell index
55686              */
55687             "cellselect" : true,
55688         /**
55689              * @event selectionchange
55690              * Fires when the active selection changes.
55691              * @param {SelectionModel} this
55692              * @param {Object} selection null for no selection or an object (o) with two properties
55693                 <ul>
55694                 <li>o.record: the record object for the row the selection is in</li>
55695                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55696                 </ul>
55697              */
55698             "selectionchange" : true,
55699         /**
55700              * @event tabend
55701              * Fires when the tab (or enter) was pressed on the last editable cell
55702              * You can use this to trigger add new row.
55703              * @param {SelectionModel} this
55704              */
55705             "tabend" : true,
55706          /**
55707              * @event beforeeditnext
55708              * Fires before the next editable sell is made active
55709              * You can use this to skip to another cell or fire the tabend
55710              *    if you set cell to false
55711              * @param {Object} eventdata object : { cell : [ row, col ] } 
55712              */
55713             "beforeeditnext" : true
55714     });
55715     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55716 };
55717
55718 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55719     
55720     enter_is_tab: false,
55721
55722     /** @ignore */
55723     initEvents : function(){
55724         this.grid.on("mousedown", this.handleMouseDown, this);
55725         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55726         var view = this.grid.view;
55727         view.on("refresh", this.onViewChange, this);
55728         view.on("rowupdated", this.onRowUpdated, this);
55729         view.on("beforerowremoved", this.clearSelections, this);
55730         view.on("beforerowsinserted", this.clearSelections, this);
55731         if(this.grid.isEditor){
55732             this.grid.on("beforeedit", this.beforeEdit,  this);
55733         }
55734     },
55735
55736         //private
55737     beforeEdit : function(e){
55738         this.select(e.row, e.column, false, true, e.record);
55739     },
55740
55741         //private
55742     onRowUpdated : function(v, index, r){
55743         if(this.selection && this.selection.record == r){
55744             v.onCellSelect(index, this.selection.cell[1]);
55745         }
55746     },
55747
55748         //private
55749     onViewChange : function(){
55750         this.clearSelections(true);
55751     },
55752
55753         /**
55754          * Returns the currently selected cell,.
55755          * @return {Array} The selected cell (row, column) or null if none selected.
55756          */
55757     getSelectedCell : function(){
55758         return this.selection ? this.selection.cell : null;
55759     },
55760
55761     /**
55762      * Clears all selections.
55763      * @param {Boolean} true to prevent the gridview from being notified about the change.
55764      */
55765     clearSelections : function(preventNotify){
55766         var s = this.selection;
55767         if(s){
55768             if(preventNotify !== true){
55769                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55770             }
55771             this.selection = null;
55772             this.fireEvent("selectionchange", this, null);
55773         }
55774     },
55775
55776     /**
55777      * Returns true if there is a selection.
55778      * @return {Boolean}
55779      */
55780     hasSelection : function(){
55781         return this.selection ? true : false;
55782     },
55783
55784     /** @ignore */
55785     handleMouseDown : function(e, t){
55786         var v = this.grid.getView();
55787         if(this.isLocked()){
55788             return;
55789         };
55790         var row = v.findRowIndex(t);
55791         var cell = v.findCellIndex(t);
55792         if(row !== false && cell !== false){
55793             this.select(row, cell);
55794         }
55795     },
55796
55797     /**
55798      * Selects a cell.
55799      * @param {Number} rowIndex
55800      * @param {Number} collIndex
55801      */
55802     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55803         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55804             this.clearSelections();
55805             r = r || this.grid.dataSource.getAt(rowIndex);
55806             this.selection = {
55807                 record : r,
55808                 cell : [rowIndex, colIndex]
55809             };
55810             if(!preventViewNotify){
55811                 var v = this.grid.getView();
55812                 v.onCellSelect(rowIndex, colIndex);
55813                 if(preventFocus !== true){
55814                     v.focusCell(rowIndex, colIndex);
55815                 }
55816             }
55817             this.fireEvent("cellselect", this, rowIndex, colIndex);
55818             this.fireEvent("selectionchange", this, this.selection);
55819         }
55820     },
55821
55822         //private
55823     isSelectable : function(rowIndex, colIndex, cm){
55824         return !cm.isHidden(colIndex);
55825     },
55826
55827     /** @ignore */
55828     handleKeyDown : function(e){
55829         //Roo.log('Cell Sel Model handleKeyDown');
55830         if(!e.isNavKeyPress()){
55831             return;
55832         }
55833         var g = this.grid, s = this.selection;
55834         if(!s){
55835             e.stopEvent();
55836             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55837             if(cell){
55838                 this.select(cell[0], cell[1]);
55839             }
55840             return;
55841         }
55842         var sm = this;
55843         var walk = function(row, col, step){
55844             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55845         };
55846         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55847         var newCell;
55848
55849       
55850
55851         switch(k){
55852             case e.TAB:
55853                 // handled by onEditorKey
55854                 if (g.isEditor && g.editing) {
55855                     return;
55856                 }
55857                 if(e.shiftKey) {
55858                     newCell = walk(r, c-1, -1);
55859                 } else {
55860                     newCell = walk(r, c+1, 1);
55861                 }
55862                 break;
55863             
55864             case e.DOWN:
55865                newCell = walk(r+1, c, 1);
55866                 break;
55867             
55868             case e.UP:
55869                 newCell = walk(r-1, c, -1);
55870                 break;
55871             
55872             case e.RIGHT:
55873                 newCell = walk(r, c+1, 1);
55874                 break;
55875             
55876             case e.LEFT:
55877                 newCell = walk(r, c-1, -1);
55878                 break;
55879             
55880             case e.ENTER:
55881                 
55882                 if(g.isEditor && !g.editing){
55883                    g.startEditing(r, c);
55884                    e.stopEvent();
55885                    return;
55886                 }
55887                 
55888                 
55889              break;
55890         };
55891         if(newCell){
55892             this.select(newCell[0], newCell[1]);
55893             e.stopEvent();
55894             
55895         }
55896     },
55897
55898     acceptsNav : function(row, col, cm){
55899         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55900     },
55901     /**
55902      * Selects a cell.
55903      * @param {Number} field (not used) - as it's normally used as a listener
55904      * @param {Number} e - event - fake it by using
55905      *
55906      * var e = Roo.EventObjectImpl.prototype;
55907      * e.keyCode = e.TAB
55908      *
55909      * 
55910      */
55911     onEditorKey : function(field, e){
55912         
55913         var k = e.getKey(),
55914             newCell,
55915             g = this.grid,
55916             ed = g.activeEditor,
55917             forward = false;
55918         ///Roo.log('onEditorKey' + k);
55919         
55920         
55921         if (this.enter_is_tab && k == e.ENTER) {
55922             k = e.TAB;
55923         }
55924         
55925         if(k == e.TAB){
55926             if(e.shiftKey){
55927                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55928             }else{
55929                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55930                 forward = true;
55931             }
55932             
55933             e.stopEvent();
55934             
55935         } else if(k == e.ENTER &&  !e.ctrlKey){
55936             ed.completeEdit();
55937             e.stopEvent();
55938             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55939         
55940                 } else if(k == e.ESC){
55941             ed.cancelEdit();
55942         }
55943                 
55944         if (newCell) {
55945             var ecall = { cell : newCell, forward : forward };
55946             this.fireEvent('beforeeditnext', ecall );
55947             newCell = ecall.cell;
55948                         forward = ecall.forward;
55949         }
55950                 
55951         if(newCell){
55952             //Roo.log('next cell after edit');
55953             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55954         } else if (forward) {
55955             // tabbed past last
55956             this.fireEvent.defer(100, this, ['tabend',this]);
55957         }
55958     }
55959 });/*
55960  * Based on:
55961  * Ext JS Library 1.1.1
55962  * Copyright(c) 2006-2007, Ext JS, LLC.
55963  *
55964  * Originally Released Under LGPL - original licence link has changed is not relivant.
55965  *
55966  * Fork - LGPL
55967  * <script type="text/javascript">
55968  */
55969  
55970 /**
55971  * @class Roo.grid.EditorGrid
55972  * @extends Roo.grid.Grid
55973  * Class for creating and editable grid.
55974  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55975  * The container MUST have some type of size defined for the grid to fill. The container will be 
55976  * automatically set to position relative if it isn't already.
55977  * @param {Object} dataSource The data model to bind to
55978  * @param {Object} colModel The column model with info about this grid's columns
55979  */
55980 Roo.grid.EditorGrid = function(container, config){
55981     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55982     this.getGridEl().addClass("xedit-grid");
55983
55984     if(!this.selModel){
55985         this.selModel = new Roo.grid.CellSelectionModel();
55986     }
55987
55988     this.activeEditor = null;
55989
55990         this.addEvents({
55991             /**
55992              * @event beforeedit
55993              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55994              * <ul style="padding:5px;padding-left:16px;">
55995              * <li>grid - This grid</li>
55996              * <li>record - The record being edited</li>
55997              * <li>field - The field name being edited</li>
55998              * <li>value - The value for the field being edited.</li>
55999              * <li>row - The grid row index</li>
56000              * <li>column - The grid column index</li>
56001              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56002              * </ul>
56003              * @param {Object} e An edit event (see above for description)
56004              */
56005             "beforeedit" : true,
56006             /**
56007              * @event afteredit
56008              * Fires after a cell is edited. <br />
56009              * <ul style="padding:5px;padding-left:16px;">
56010              * <li>grid - This grid</li>
56011              * <li>record - The record being edited</li>
56012              * <li>field - The field name being edited</li>
56013              * <li>value - The value being set</li>
56014              * <li>originalValue - The original value for the field, before the edit.</li>
56015              * <li>row - The grid row index</li>
56016              * <li>column - The grid column index</li>
56017              * </ul>
56018              * @param {Object} e An edit event (see above for description)
56019              */
56020             "afteredit" : true,
56021             /**
56022              * @event validateedit
56023              * Fires after a cell is edited, but before the value is set in the record. 
56024          * You can use this to modify the value being set in the field, Return false
56025              * to cancel the change. The edit event object has the following properties <br />
56026              * <ul style="padding:5px;padding-left:16px;">
56027          * <li>editor - This editor</li>
56028              * <li>grid - This grid</li>
56029              * <li>record - The record being edited</li>
56030              * <li>field - The field name being edited</li>
56031              * <li>value - The value being set</li>
56032              * <li>originalValue - The original value for the field, before the edit.</li>
56033              * <li>row - The grid row index</li>
56034              * <li>column - The grid column index</li>
56035              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56036              * </ul>
56037              * @param {Object} e An edit event (see above for description)
56038              */
56039             "validateedit" : true
56040         });
56041     this.on("bodyscroll", this.stopEditing,  this);
56042     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56043 };
56044
56045 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56046     /**
56047      * @cfg {Number} clicksToEdit
56048      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56049      */
56050     clicksToEdit: 2,
56051
56052     // private
56053     isEditor : true,
56054     // private
56055     trackMouseOver: false, // causes very odd FF errors
56056
56057     onCellDblClick : function(g, row, col){
56058         this.startEditing(row, col);
56059     },
56060
56061     onEditComplete : function(ed, value, startValue){
56062         this.editing = false;
56063         this.activeEditor = null;
56064         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56065         var r = ed.record;
56066         var field = this.colModel.getDataIndex(ed.col);
56067         var e = {
56068             grid: this,
56069             record: r,
56070             field: field,
56071             originalValue: startValue,
56072             value: value,
56073             row: ed.row,
56074             column: ed.col,
56075             cancel:false,
56076             editor: ed
56077         };
56078         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56079         cell.show();
56080           
56081         if(String(value) !== String(startValue)){
56082             
56083             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56084                 r.set(field, e.value);
56085                 // if we are dealing with a combo box..
56086                 // then we also set the 'name' colum to be the displayField
56087                 if (ed.field.displayField && ed.field.name) {
56088                     r.set(ed.field.name, ed.field.el.dom.value);
56089                 }
56090                 
56091                 delete e.cancel; //?? why!!!
56092                 this.fireEvent("afteredit", e);
56093             }
56094         } else {
56095             this.fireEvent("afteredit", e); // always fire it!
56096         }
56097         this.view.focusCell(ed.row, ed.col);
56098     },
56099
56100     /**
56101      * Starts editing the specified for the specified row/column
56102      * @param {Number} rowIndex
56103      * @param {Number} colIndex
56104      */
56105     startEditing : function(row, col){
56106         this.stopEditing();
56107         if(this.colModel.isCellEditable(col, row)){
56108             this.view.ensureVisible(row, col, true);
56109           
56110             var r = this.dataSource.getAt(row);
56111             var field = this.colModel.getDataIndex(col);
56112             var cell = Roo.get(this.view.getCell(row,col));
56113             var e = {
56114                 grid: this,
56115                 record: r,
56116                 field: field,
56117                 value: r.data[field],
56118                 row: row,
56119                 column: col,
56120                 cancel:false 
56121             };
56122             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56123                 this.editing = true;
56124                 var ed = this.colModel.getCellEditor(col, row);
56125                 
56126                 if (!ed) {
56127                     return;
56128                 }
56129                 if(!ed.rendered){
56130                     ed.render(ed.parentEl || document.body);
56131                 }
56132                 ed.field.reset();
56133                
56134                 cell.hide();
56135                 
56136                 (function(){ // complex but required for focus issues in safari, ie and opera
56137                     ed.row = row;
56138                     ed.col = col;
56139                     ed.record = r;
56140                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56141                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56142                     this.activeEditor = ed;
56143                     var v = r.data[field];
56144                     ed.startEdit(this.view.getCell(row, col), v);
56145                     // combo's with 'displayField and name set
56146                     if (ed.field.displayField && ed.field.name) {
56147                         ed.field.el.dom.value = r.data[ed.field.name];
56148                     }
56149                     
56150                     
56151                 }).defer(50, this);
56152             }
56153         }
56154     },
56155         
56156     /**
56157      * Stops any active editing
56158      */
56159     stopEditing : function(){
56160         if(this.activeEditor){
56161             this.activeEditor.completeEdit();
56162         }
56163         this.activeEditor = null;
56164     },
56165         
56166          /**
56167      * Called to get grid's drag proxy text, by default returns this.ddText.
56168      * @return {String}
56169      */
56170     getDragDropText : function(){
56171         var count = this.selModel.getSelectedCell() ? 1 : 0;
56172         return String.format(this.ddText, count, count == 1 ? '' : 's');
56173     }
56174         
56175 });/*
56176  * Based on:
56177  * Ext JS Library 1.1.1
56178  * Copyright(c) 2006-2007, Ext JS, LLC.
56179  *
56180  * Originally Released Under LGPL - original licence link has changed is not relivant.
56181  *
56182  * Fork - LGPL
56183  * <script type="text/javascript">
56184  */
56185
56186 // private - not really -- you end up using it !
56187 // This is a support class used internally by the Grid components
56188
56189 /**
56190  * @class Roo.grid.GridEditor
56191  * @extends Roo.Editor
56192  * Class for creating and editable grid elements.
56193  * @param {Object} config any settings (must include field)
56194  */
56195 Roo.grid.GridEditor = function(field, config){
56196     if (!config && field.field) {
56197         config = field;
56198         field = Roo.factory(config.field, Roo.form);
56199     }
56200     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56201     field.monitorTab = false;
56202 };
56203
56204 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56205     
56206     /**
56207      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56208      */
56209     
56210     alignment: "tl-tl",
56211     autoSize: "width",
56212     hideEl : false,
56213     cls: "x-small-editor x-grid-editor",
56214     shim:false,
56215     shadow:"frame"
56216 });/*
56217  * Based on:
56218  * Ext JS Library 1.1.1
56219  * Copyright(c) 2006-2007, Ext JS, LLC.
56220  *
56221  * Originally Released Under LGPL - original licence link has changed is not relivant.
56222  *
56223  * Fork - LGPL
56224  * <script type="text/javascript">
56225  */
56226   
56227
56228   
56229 Roo.grid.PropertyRecord = Roo.data.Record.create([
56230     {name:'name',type:'string'},  'value'
56231 ]);
56232
56233
56234 Roo.grid.PropertyStore = function(grid, source){
56235     this.grid = grid;
56236     this.store = new Roo.data.Store({
56237         recordType : Roo.grid.PropertyRecord
56238     });
56239     this.store.on('update', this.onUpdate,  this);
56240     if(source){
56241         this.setSource(source);
56242     }
56243     Roo.grid.PropertyStore.superclass.constructor.call(this);
56244 };
56245
56246
56247
56248 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56249     setSource : function(o){
56250         this.source = o;
56251         this.store.removeAll();
56252         var data = [];
56253         for(var k in o){
56254             if(this.isEditableValue(o[k])){
56255                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56256             }
56257         }
56258         this.store.loadRecords({records: data}, {}, true);
56259     },
56260
56261     onUpdate : function(ds, record, type){
56262         if(type == Roo.data.Record.EDIT){
56263             var v = record.data['value'];
56264             var oldValue = record.modified['value'];
56265             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56266                 this.source[record.id] = v;
56267                 record.commit();
56268                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56269             }else{
56270                 record.reject();
56271             }
56272         }
56273     },
56274
56275     getProperty : function(row){
56276        return this.store.getAt(row);
56277     },
56278
56279     isEditableValue: function(val){
56280         if(val && val instanceof Date){
56281             return true;
56282         }else if(typeof val == 'object' || typeof val == 'function'){
56283             return false;
56284         }
56285         return true;
56286     },
56287
56288     setValue : function(prop, value){
56289         this.source[prop] = value;
56290         this.store.getById(prop).set('value', value);
56291     },
56292
56293     getSource : function(){
56294         return this.source;
56295     }
56296 });
56297
56298 Roo.grid.PropertyColumnModel = function(grid, store){
56299     this.grid = grid;
56300     var g = Roo.grid;
56301     g.PropertyColumnModel.superclass.constructor.call(this, [
56302         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56303         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56304     ]);
56305     this.store = store;
56306     this.bselect = Roo.DomHelper.append(document.body, {
56307         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56308             {tag: 'option', value: 'true', html: 'true'},
56309             {tag: 'option', value: 'false', html: 'false'}
56310         ]
56311     });
56312     Roo.id(this.bselect);
56313     var f = Roo.form;
56314     this.editors = {
56315         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56316         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56317         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56318         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56319         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56320     };
56321     this.renderCellDelegate = this.renderCell.createDelegate(this);
56322     this.renderPropDelegate = this.renderProp.createDelegate(this);
56323 };
56324
56325 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56326     
56327     
56328     nameText : 'Name',
56329     valueText : 'Value',
56330     
56331     dateFormat : 'm/j/Y',
56332     
56333     
56334     renderDate : function(dateVal){
56335         return dateVal.dateFormat(this.dateFormat);
56336     },
56337
56338     renderBool : function(bVal){
56339         return bVal ? 'true' : 'false';
56340     },
56341
56342     isCellEditable : function(colIndex, rowIndex){
56343         return colIndex == 1;
56344     },
56345
56346     getRenderer : function(col){
56347         return col == 1 ?
56348             this.renderCellDelegate : this.renderPropDelegate;
56349     },
56350
56351     renderProp : function(v){
56352         return this.getPropertyName(v);
56353     },
56354
56355     renderCell : function(val){
56356         var rv = val;
56357         if(val instanceof Date){
56358             rv = this.renderDate(val);
56359         }else if(typeof val == 'boolean'){
56360             rv = this.renderBool(val);
56361         }
56362         return Roo.util.Format.htmlEncode(rv);
56363     },
56364
56365     getPropertyName : function(name){
56366         var pn = this.grid.propertyNames;
56367         return pn && pn[name] ? pn[name] : name;
56368     },
56369
56370     getCellEditor : function(colIndex, rowIndex){
56371         var p = this.store.getProperty(rowIndex);
56372         var n = p.data['name'], val = p.data['value'];
56373         
56374         if(typeof(this.grid.customEditors[n]) == 'string'){
56375             return this.editors[this.grid.customEditors[n]];
56376         }
56377         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56378             return this.grid.customEditors[n];
56379         }
56380         if(val instanceof Date){
56381             return this.editors['date'];
56382         }else if(typeof val == 'number'){
56383             return this.editors['number'];
56384         }else if(typeof val == 'boolean'){
56385             return this.editors['boolean'];
56386         }else{
56387             return this.editors['string'];
56388         }
56389     }
56390 });
56391
56392 /**
56393  * @class Roo.grid.PropertyGrid
56394  * @extends Roo.grid.EditorGrid
56395  * This class represents the  interface of a component based property grid control.
56396  * <br><br>Usage:<pre><code>
56397  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56398       
56399  });
56400  // set any options
56401  grid.render();
56402  * </code></pre>
56403   
56404  * @constructor
56405  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56406  * The container MUST have some type of size defined for the grid to fill. The container will be
56407  * automatically set to position relative if it isn't already.
56408  * @param {Object} config A config object that sets properties on this grid.
56409  */
56410 Roo.grid.PropertyGrid = function(container, config){
56411     config = config || {};
56412     var store = new Roo.grid.PropertyStore(this);
56413     this.store = store;
56414     var cm = new Roo.grid.PropertyColumnModel(this, store);
56415     store.store.sort('name', 'ASC');
56416     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56417         ds: store.store,
56418         cm: cm,
56419         enableColLock:false,
56420         enableColumnMove:false,
56421         stripeRows:false,
56422         trackMouseOver: false,
56423         clicksToEdit:1
56424     }, config));
56425     this.getGridEl().addClass('x-props-grid');
56426     this.lastEditRow = null;
56427     this.on('columnresize', this.onColumnResize, this);
56428     this.addEvents({
56429          /**
56430              * @event beforepropertychange
56431              * Fires before a property changes (return false to stop?)
56432              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56433              * @param {String} id Record Id
56434              * @param {String} newval New Value
56435          * @param {String} oldval Old Value
56436              */
56437         "beforepropertychange": true,
56438         /**
56439              * @event propertychange
56440              * Fires after a property changes
56441              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56442              * @param {String} id Record Id
56443              * @param {String} newval New Value
56444          * @param {String} oldval Old Value
56445              */
56446         "propertychange": true
56447     });
56448     this.customEditors = this.customEditors || {};
56449 };
56450 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56451     
56452      /**
56453      * @cfg {Object} customEditors map of colnames=> custom editors.
56454      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56455      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56456      * false disables editing of the field.
56457          */
56458     
56459       /**
56460      * @cfg {Object} propertyNames map of property Names to their displayed value
56461          */
56462     
56463     render : function(){
56464         Roo.grid.PropertyGrid.superclass.render.call(this);
56465         this.autoSize.defer(100, this);
56466     },
56467
56468     autoSize : function(){
56469         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56470         if(this.view){
56471             this.view.fitColumns();
56472         }
56473     },
56474
56475     onColumnResize : function(){
56476         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56477         this.autoSize();
56478     },
56479     /**
56480      * Sets the data for the Grid
56481      * accepts a Key => Value object of all the elements avaiable.
56482      * @param {Object} data  to appear in grid.
56483      */
56484     setSource : function(source){
56485         this.store.setSource(source);
56486         //this.autoSize();
56487     },
56488     /**
56489      * Gets all the data from the grid.
56490      * @return {Object} data  data stored in grid
56491      */
56492     getSource : function(){
56493         return this.store.getSource();
56494     }
56495 });/*
56496   
56497  * Licence LGPL
56498  
56499  */
56500  
56501 /**
56502  * @class Roo.grid.Calendar
56503  * @extends Roo.util.Grid
56504  * This class extends the Grid to provide a calendar widget
56505  * <br><br>Usage:<pre><code>
56506  var grid = new Roo.grid.Calendar("my-container-id", {
56507      ds: myDataStore,
56508      cm: myColModel,
56509      selModel: mySelectionModel,
56510      autoSizeColumns: true,
56511      monitorWindowResize: false,
56512      trackMouseOver: true
56513      eventstore : real data store..
56514  });
56515  // set any options
56516  grid.render();
56517   
56518   * @constructor
56519  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56520  * The container MUST have some type of size defined for the grid to fill. The container will be
56521  * automatically set to position relative if it isn't already.
56522  * @param {Object} config A config object that sets properties on this grid.
56523  */
56524 Roo.grid.Calendar = function(container, config){
56525         // initialize the container
56526         this.container = Roo.get(container);
56527         this.container.update("");
56528         this.container.setStyle("overflow", "hidden");
56529     this.container.addClass('x-grid-container');
56530
56531     this.id = this.container.id;
56532
56533     Roo.apply(this, config);
56534     // check and correct shorthanded configs
56535     
56536     var rows = [];
56537     var d =1;
56538     for (var r = 0;r < 6;r++) {
56539         
56540         rows[r]=[];
56541         for (var c =0;c < 7;c++) {
56542             rows[r][c]= '';
56543         }
56544     }
56545     if (this.eventStore) {
56546         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56547         this.eventStore.on('load',this.onLoad, this);
56548         this.eventStore.on('beforeload',this.clearEvents, this);
56549          
56550     }
56551     
56552     this.dataSource = new Roo.data.Store({
56553             proxy: new Roo.data.MemoryProxy(rows),
56554             reader: new Roo.data.ArrayReader({}, [
56555                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56556     });
56557
56558     this.dataSource.load();
56559     this.ds = this.dataSource;
56560     this.ds.xmodule = this.xmodule || false;
56561     
56562     
56563     var cellRender = function(v,x,r)
56564     {
56565         return String.format(
56566             '<div class="fc-day  fc-widget-content"><div>' +
56567                 '<div class="fc-event-container"></div>' +
56568                 '<div class="fc-day-number">{0}</div>'+
56569                 
56570                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56571             '</div></div>', v);
56572     
56573     }
56574     
56575     
56576     this.colModel = new Roo.grid.ColumnModel( [
56577         {
56578             xtype: 'ColumnModel',
56579             xns: Roo.grid,
56580             dataIndex : 'weekday0',
56581             header : 'Sunday',
56582             renderer : cellRender
56583         },
56584         {
56585             xtype: 'ColumnModel',
56586             xns: Roo.grid,
56587             dataIndex : 'weekday1',
56588             header : 'Monday',
56589             renderer : cellRender
56590         },
56591         {
56592             xtype: 'ColumnModel',
56593             xns: Roo.grid,
56594             dataIndex : 'weekday2',
56595             header : 'Tuesday',
56596             renderer : cellRender
56597         },
56598         {
56599             xtype: 'ColumnModel',
56600             xns: Roo.grid,
56601             dataIndex : 'weekday3',
56602             header : 'Wednesday',
56603             renderer : cellRender
56604         },
56605         {
56606             xtype: 'ColumnModel',
56607             xns: Roo.grid,
56608             dataIndex : 'weekday4',
56609             header : 'Thursday',
56610             renderer : cellRender
56611         },
56612         {
56613             xtype: 'ColumnModel',
56614             xns: Roo.grid,
56615             dataIndex : 'weekday5',
56616             header : 'Friday',
56617             renderer : cellRender
56618         },
56619         {
56620             xtype: 'ColumnModel',
56621             xns: Roo.grid,
56622             dataIndex : 'weekday6',
56623             header : 'Saturday',
56624             renderer : cellRender
56625         }
56626     ]);
56627     this.cm = this.colModel;
56628     this.cm.xmodule = this.xmodule || false;
56629  
56630         
56631           
56632     //this.selModel = new Roo.grid.CellSelectionModel();
56633     //this.sm = this.selModel;
56634     //this.selModel.init(this);
56635     
56636     
56637     if(this.width){
56638         this.container.setWidth(this.width);
56639     }
56640
56641     if(this.height){
56642         this.container.setHeight(this.height);
56643     }
56644     /** @private */
56645         this.addEvents({
56646         // raw events
56647         /**
56648          * @event click
56649          * The raw click event for the entire grid.
56650          * @param {Roo.EventObject} e
56651          */
56652         "click" : true,
56653         /**
56654          * @event dblclick
56655          * The raw dblclick event for the entire grid.
56656          * @param {Roo.EventObject} e
56657          */
56658         "dblclick" : true,
56659         /**
56660          * @event contextmenu
56661          * The raw contextmenu event for the entire grid.
56662          * @param {Roo.EventObject} e
56663          */
56664         "contextmenu" : true,
56665         /**
56666          * @event mousedown
56667          * The raw mousedown event for the entire grid.
56668          * @param {Roo.EventObject} e
56669          */
56670         "mousedown" : true,
56671         /**
56672          * @event mouseup
56673          * The raw mouseup event for the entire grid.
56674          * @param {Roo.EventObject} e
56675          */
56676         "mouseup" : true,
56677         /**
56678          * @event mouseover
56679          * The raw mouseover event for the entire grid.
56680          * @param {Roo.EventObject} e
56681          */
56682         "mouseover" : true,
56683         /**
56684          * @event mouseout
56685          * The raw mouseout event for the entire grid.
56686          * @param {Roo.EventObject} e
56687          */
56688         "mouseout" : true,
56689         /**
56690          * @event keypress
56691          * The raw keypress event for the entire grid.
56692          * @param {Roo.EventObject} e
56693          */
56694         "keypress" : true,
56695         /**
56696          * @event keydown
56697          * The raw keydown event for the entire grid.
56698          * @param {Roo.EventObject} e
56699          */
56700         "keydown" : true,
56701
56702         // custom events
56703
56704         /**
56705          * @event cellclick
56706          * Fires when a cell is clicked
56707          * @param {Grid} this
56708          * @param {Number} rowIndex
56709          * @param {Number} columnIndex
56710          * @param {Roo.EventObject} e
56711          */
56712         "cellclick" : true,
56713         /**
56714          * @event celldblclick
56715          * Fires when a cell is double clicked
56716          * @param {Grid} this
56717          * @param {Number} rowIndex
56718          * @param {Number} columnIndex
56719          * @param {Roo.EventObject} e
56720          */
56721         "celldblclick" : true,
56722         /**
56723          * @event rowclick
56724          * Fires when a row is clicked
56725          * @param {Grid} this
56726          * @param {Number} rowIndex
56727          * @param {Roo.EventObject} e
56728          */
56729         "rowclick" : true,
56730         /**
56731          * @event rowdblclick
56732          * Fires when a row is double clicked
56733          * @param {Grid} this
56734          * @param {Number} rowIndex
56735          * @param {Roo.EventObject} e
56736          */
56737         "rowdblclick" : true,
56738         /**
56739          * @event headerclick
56740          * Fires when a header is clicked
56741          * @param {Grid} this
56742          * @param {Number} columnIndex
56743          * @param {Roo.EventObject} e
56744          */
56745         "headerclick" : true,
56746         /**
56747          * @event headerdblclick
56748          * Fires when a header cell is double clicked
56749          * @param {Grid} this
56750          * @param {Number} columnIndex
56751          * @param {Roo.EventObject} e
56752          */
56753         "headerdblclick" : true,
56754         /**
56755          * @event rowcontextmenu
56756          * Fires when a row is right clicked
56757          * @param {Grid} this
56758          * @param {Number} rowIndex
56759          * @param {Roo.EventObject} e
56760          */
56761         "rowcontextmenu" : true,
56762         /**
56763          * @event cellcontextmenu
56764          * Fires when a cell is right clicked
56765          * @param {Grid} this
56766          * @param {Number} rowIndex
56767          * @param {Number} cellIndex
56768          * @param {Roo.EventObject} e
56769          */
56770          "cellcontextmenu" : true,
56771         /**
56772          * @event headercontextmenu
56773          * Fires when a header is right clicked
56774          * @param {Grid} this
56775          * @param {Number} columnIndex
56776          * @param {Roo.EventObject} e
56777          */
56778         "headercontextmenu" : true,
56779         /**
56780          * @event bodyscroll
56781          * Fires when the body element is scrolled
56782          * @param {Number} scrollLeft
56783          * @param {Number} scrollTop
56784          */
56785         "bodyscroll" : true,
56786         /**
56787          * @event columnresize
56788          * Fires when the user resizes a column
56789          * @param {Number} columnIndex
56790          * @param {Number} newSize
56791          */
56792         "columnresize" : true,
56793         /**
56794          * @event columnmove
56795          * Fires when the user moves a column
56796          * @param {Number} oldIndex
56797          * @param {Number} newIndex
56798          */
56799         "columnmove" : true,
56800         /**
56801          * @event startdrag
56802          * Fires when row(s) start being dragged
56803          * @param {Grid} this
56804          * @param {Roo.GridDD} dd The drag drop object
56805          * @param {event} e The raw browser event
56806          */
56807         "startdrag" : true,
56808         /**
56809          * @event enddrag
56810          * Fires when a drag operation is complete
56811          * @param {Grid} this
56812          * @param {Roo.GridDD} dd The drag drop object
56813          * @param {event} e The raw browser event
56814          */
56815         "enddrag" : true,
56816         /**
56817          * @event dragdrop
56818          * Fires when dragged row(s) are dropped on a valid DD target
56819          * @param {Grid} this
56820          * @param {Roo.GridDD} dd The drag drop object
56821          * @param {String} targetId The target drag drop object
56822          * @param {event} e The raw browser event
56823          */
56824         "dragdrop" : true,
56825         /**
56826          * @event dragover
56827          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56828          * @param {Grid} this
56829          * @param {Roo.GridDD} dd The drag drop object
56830          * @param {String} targetId The target drag drop object
56831          * @param {event} e The raw browser event
56832          */
56833         "dragover" : true,
56834         /**
56835          * @event dragenter
56836          *  Fires when the dragged row(s) first cross another DD target while being dragged
56837          * @param {Grid} this
56838          * @param {Roo.GridDD} dd The drag drop object
56839          * @param {String} targetId The target drag drop object
56840          * @param {event} e The raw browser event
56841          */
56842         "dragenter" : true,
56843         /**
56844          * @event dragout
56845          * Fires when the dragged row(s) leave another DD target while being dragged
56846          * @param {Grid} this
56847          * @param {Roo.GridDD} dd The drag drop object
56848          * @param {String} targetId The target drag drop object
56849          * @param {event} e The raw browser event
56850          */
56851         "dragout" : true,
56852         /**
56853          * @event rowclass
56854          * Fires when a row is rendered, so you can change add a style to it.
56855          * @param {GridView} gridview   The grid view
56856          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56857          */
56858         'rowclass' : true,
56859
56860         /**
56861          * @event render
56862          * Fires when the grid is rendered
56863          * @param {Grid} grid
56864          */
56865         'render' : true,
56866             /**
56867              * @event select
56868              * Fires when a date is selected
56869              * @param {DatePicker} this
56870              * @param {Date} date The selected date
56871              */
56872         'select': true,
56873         /**
56874              * @event monthchange
56875              * Fires when the displayed month changes 
56876              * @param {DatePicker} this
56877              * @param {Date} date The selected month
56878              */
56879         'monthchange': true,
56880         /**
56881              * @event evententer
56882              * Fires when mouse over an event
56883              * @param {Calendar} this
56884              * @param {event} Event
56885              */
56886         'evententer': true,
56887         /**
56888              * @event eventleave
56889              * Fires when the mouse leaves an
56890              * @param {Calendar} this
56891              * @param {event}
56892              */
56893         'eventleave': true,
56894         /**
56895              * @event eventclick
56896              * Fires when the mouse click an
56897              * @param {Calendar} this
56898              * @param {event}
56899              */
56900         'eventclick': true,
56901         /**
56902              * @event eventrender
56903              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56904              * @param {Calendar} this
56905              * @param {data} data to be modified
56906              */
56907         'eventrender': true
56908         
56909     });
56910
56911     Roo.grid.Grid.superclass.constructor.call(this);
56912     this.on('render', function() {
56913         this.view.el.addClass('x-grid-cal'); 
56914         
56915         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56916
56917     },this);
56918     
56919     if (!Roo.grid.Calendar.style) {
56920         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56921             
56922             
56923             '.x-grid-cal .x-grid-col' :  {
56924                 height: 'auto !important',
56925                 'vertical-align': 'top'
56926             },
56927             '.x-grid-cal  .fc-event-hori' : {
56928                 height: '14px'
56929             }
56930              
56931             
56932         }, Roo.id());
56933     }
56934
56935     
56936     
56937 };
56938 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56939     /**
56940      * @cfg {Store} eventStore The store that loads events.
56941      */
56942     eventStore : 25,
56943
56944      
56945     activeDate : false,
56946     startDay : 0,
56947     autoWidth : true,
56948     monitorWindowResize : false,
56949
56950     
56951     resizeColumns : function() {
56952         var col = (this.view.el.getWidth() / 7) - 3;
56953         // loop through cols, and setWidth
56954         for(var i =0 ; i < 7 ; i++){
56955             this.cm.setColumnWidth(i, col);
56956         }
56957     },
56958      setDate :function(date) {
56959         
56960         Roo.log('setDate?');
56961         
56962         this.resizeColumns();
56963         var vd = this.activeDate;
56964         this.activeDate = date;
56965 //        if(vd && this.el){
56966 //            var t = date.getTime();
56967 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56968 //                Roo.log('using add remove');
56969 //                
56970 //                this.fireEvent('monthchange', this, date);
56971 //                
56972 //                this.cells.removeClass("fc-state-highlight");
56973 //                this.cells.each(function(c){
56974 //                   if(c.dateValue == t){
56975 //                       c.addClass("fc-state-highlight");
56976 //                       setTimeout(function(){
56977 //                            try{c.dom.firstChild.focus();}catch(e){}
56978 //                       }, 50);
56979 //                       return false;
56980 //                   }
56981 //                   return true;
56982 //                });
56983 //                return;
56984 //            }
56985 //        }
56986         
56987         var days = date.getDaysInMonth();
56988         
56989         var firstOfMonth = date.getFirstDateOfMonth();
56990         var startingPos = firstOfMonth.getDay()-this.startDay;
56991         
56992         if(startingPos < this.startDay){
56993             startingPos += 7;
56994         }
56995         
56996         var pm = date.add(Date.MONTH, -1);
56997         var prevStart = pm.getDaysInMonth()-startingPos;
56998 //        
56999         
57000         
57001         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57002         
57003         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57004         //this.cells.addClassOnOver('fc-state-hover');
57005         
57006         var cells = this.cells.elements;
57007         var textEls = this.textNodes;
57008         
57009         //Roo.each(cells, function(cell){
57010         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57011         //});
57012         
57013         days += startingPos;
57014
57015         // convert everything to numbers so it's fast
57016         var day = 86400000;
57017         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57018         //Roo.log(d);
57019         //Roo.log(pm);
57020         //Roo.log(prevStart);
57021         
57022         var today = new Date().clearTime().getTime();
57023         var sel = date.clearTime().getTime();
57024         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57025         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57026         var ddMatch = this.disabledDatesRE;
57027         var ddText = this.disabledDatesText;
57028         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57029         var ddaysText = this.disabledDaysText;
57030         var format = this.format;
57031         
57032         var setCellClass = function(cal, cell){
57033             
57034             //Roo.log('set Cell Class');
57035             cell.title = "";
57036             var t = d.getTime();
57037             
57038             //Roo.log(d);
57039             
57040             
57041             cell.dateValue = t;
57042             if(t == today){
57043                 cell.className += " fc-today";
57044                 cell.className += " fc-state-highlight";
57045                 cell.title = cal.todayText;
57046             }
57047             if(t == sel){
57048                 // disable highlight in other month..
57049                 cell.className += " fc-state-highlight";
57050                 
57051             }
57052             // disabling
57053             if(t < min) {
57054                 //cell.className = " fc-state-disabled";
57055                 cell.title = cal.minText;
57056                 return;
57057             }
57058             if(t > max) {
57059                 //cell.className = " fc-state-disabled";
57060                 cell.title = cal.maxText;
57061                 return;
57062             }
57063             if(ddays){
57064                 if(ddays.indexOf(d.getDay()) != -1){
57065                     // cell.title = ddaysText;
57066                    // cell.className = " fc-state-disabled";
57067                 }
57068             }
57069             if(ddMatch && format){
57070                 var fvalue = d.dateFormat(format);
57071                 if(ddMatch.test(fvalue)){
57072                     cell.title = ddText.replace("%0", fvalue);
57073                    cell.className = " fc-state-disabled";
57074                 }
57075             }
57076             
57077             if (!cell.initialClassName) {
57078                 cell.initialClassName = cell.dom.className;
57079             }
57080             
57081             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57082         };
57083
57084         var i = 0;
57085         
57086         for(; i < startingPos; i++) {
57087             cells[i].dayName =  (++prevStart);
57088             Roo.log(textEls[i]);
57089             d.setDate(d.getDate()+1);
57090             
57091             //cells[i].className = "fc-past fc-other-month";
57092             setCellClass(this, cells[i]);
57093         }
57094         
57095         var intDay = 0;
57096         
57097         for(; i < days; i++){
57098             intDay = i - startingPos + 1;
57099             cells[i].dayName =  (intDay);
57100             d.setDate(d.getDate()+1);
57101             
57102             cells[i].className = ''; // "x-date-active";
57103             setCellClass(this, cells[i]);
57104         }
57105         var extraDays = 0;
57106         
57107         for(; i < 42; i++) {
57108             //textEls[i].innerHTML = (++extraDays);
57109             
57110             d.setDate(d.getDate()+1);
57111             cells[i].dayName = (++extraDays);
57112             cells[i].className = "fc-future fc-other-month";
57113             setCellClass(this, cells[i]);
57114         }
57115         
57116         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57117         
57118         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57119         
57120         // this will cause all the cells to mis
57121         var rows= [];
57122         var i =0;
57123         for (var r = 0;r < 6;r++) {
57124             for (var c =0;c < 7;c++) {
57125                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57126             }    
57127         }
57128         
57129         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57130         for(i=0;i<cells.length;i++) {
57131             
57132             this.cells.elements[i].dayName = cells[i].dayName ;
57133             this.cells.elements[i].className = cells[i].className;
57134             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57135             this.cells.elements[i].title = cells[i].title ;
57136             this.cells.elements[i].dateValue = cells[i].dateValue ;
57137         }
57138         
57139         
57140         
57141         
57142         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57143         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57144         
57145         ////if(totalRows != 6){
57146             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57147            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57148        // }
57149         
57150         this.fireEvent('monthchange', this, date);
57151         
57152         
57153     },
57154  /**
57155      * Returns the grid's SelectionModel.
57156      * @return {SelectionModel}
57157      */
57158     getSelectionModel : function(){
57159         if(!this.selModel){
57160             this.selModel = new Roo.grid.CellSelectionModel();
57161         }
57162         return this.selModel;
57163     },
57164
57165     load: function() {
57166         this.eventStore.load()
57167         
57168         
57169         
57170     },
57171     
57172     findCell : function(dt) {
57173         dt = dt.clearTime().getTime();
57174         var ret = false;
57175         this.cells.each(function(c){
57176             //Roo.log("check " +c.dateValue + '?=' + dt);
57177             if(c.dateValue == dt){
57178                 ret = c;
57179                 return false;
57180             }
57181             return true;
57182         });
57183         
57184         return ret;
57185     },
57186     
57187     findCells : function(rec) {
57188         var s = rec.data.start_dt.clone().clearTime().getTime();
57189        // Roo.log(s);
57190         var e= rec.data.end_dt.clone().clearTime().getTime();
57191        // Roo.log(e);
57192         var ret = [];
57193         this.cells.each(function(c){
57194              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57195             
57196             if(c.dateValue > e){
57197                 return ;
57198             }
57199             if(c.dateValue < s){
57200                 return ;
57201             }
57202             ret.push(c);
57203         });
57204         
57205         return ret;    
57206     },
57207     
57208     findBestRow: function(cells)
57209     {
57210         var ret = 0;
57211         
57212         for (var i =0 ; i < cells.length;i++) {
57213             ret  = Math.max(cells[i].rows || 0,ret);
57214         }
57215         return ret;
57216         
57217     },
57218     
57219     
57220     addItem : function(rec)
57221     {
57222         // look for vertical location slot in
57223         var cells = this.findCells(rec);
57224         
57225         rec.row = this.findBestRow(cells);
57226         
57227         // work out the location.
57228         
57229         var crow = false;
57230         var rows = [];
57231         for(var i =0; i < cells.length; i++) {
57232             if (!crow) {
57233                 crow = {
57234                     start : cells[i],
57235                     end :  cells[i]
57236                 };
57237                 continue;
57238             }
57239             if (crow.start.getY() == cells[i].getY()) {
57240                 // on same row.
57241                 crow.end = cells[i];
57242                 continue;
57243             }
57244             // different row.
57245             rows.push(crow);
57246             crow = {
57247                 start: cells[i],
57248                 end : cells[i]
57249             };
57250             
57251         }
57252         
57253         rows.push(crow);
57254         rec.els = [];
57255         rec.rows = rows;
57256         rec.cells = cells;
57257         for (var i = 0; i < cells.length;i++) {
57258             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57259             
57260         }
57261         
57262         
57263     },
57264     
57265     clearEvents: function() {
57266         
57267         if (!this.eventStore.getCount()) {
57268             return;
57269         }
57270         // reset number of rows in cells.
57271         Roo.each(this.cells.elements, function(c){
57272             c.rows = 0;
57273         });
57274         
57275         this.eventStore.each(function(e) {
57276             this.clearEvent(e);
57277         },this);
57278         
57279     },
57280     
57281     clearEvent : function(ev)
57282     {
57283         if (ev.els) {
57284             Roo.each(ev.els, function(el) {
57285                 el.un('mouseenter' ,this.onEventEnter, this);
57286                 el.un('mouseleave' ,this.onEventLeave, this);
57287                 el.remove();
57288             },this);
57289             ev.els = [];
57290         }
57291     },
57292     
57293     
57294     renderEvent : function(ev,ctr) {
57295         if (!ctr) {
57296              ctr = this.view.el.select('.fc-event-container',true).first();
57297         }
57298         
57299          
57300         this.clearEvent(ev);
57301             //code
57302        
57303         
57304         
57305         ev.els = [];
57306         var cells = ev.cells;
57307         var rows = ev.rows;
57308         this.fireEvent('eventrender', this, ev);
57309         
57310         for(var i =0; i < rows.length; i++) {
57311             
57312             cls = '';
57313             if (i == 0) {
57314                 cls += ' fc-event-start';
57315             }
57316             if ((i+1) == rows.length) {
57317                 cls += ' fc-event-end';
57318             }
57319             
57320             //Roo.log(ev.data);
57321             // how many rows should it span..
57322             var cg = this.eventTmpl.append(ctr,Roo.apply({
57323                 fccls : cls
57324                 
57325             }, ev.data) , true);
57326             
57327             
57328             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57329             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57330             cg.on('click', this.onEventClick, this, ev);
57331             
57332             ev.els.push(cg);
57333             
57334             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57335             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57336             //Roo.log(cg);
57337              
57338             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57339             cg.setWidth(ebox.right - sbox.x -2);
57340         }
57341     },
57342     
57343     renderEvents: function()
57344     {   
57345         // first make sure there is enough space..
57346         
57347         if (!this.eventTmpl) {
57348             this.eventTmpl = new Roo.Template(
57349                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57350                     '<div class="fc-event-inner">' +
57351                         '<span class="fc-event-time">{time}</span>' +
57352                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57353                     '</div>' +
57354                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57355                 '</div>'
57356             );
57357                 
57358         }
57359                
57360         
57361         
57362         this.cells.each(function(c) {
57363             //Roo.log(c.select('.fc-day-content div',true).first());
57364             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57365         });
57366         
57367         var ctr = this.view.el.select('.fc-event-container',true).first();
57368         
57369         var cls;
57370         this.eventStore.each(function(ev){
57371             
57372             this.renderEvent(ev);
57373              
57374              
57375         }, this);
57376         this.view.layout();
57377         
57378     },
57379     
57380     onEventEnter: function (e, el,event,d) {
57381         this.fireEvent('evententer', this, el, event);
57382     },
57383     
57384     onEventLeave: function (e, el,event,d) {
57385         this.fireEvent('eventleave', this, el, event);
57386     },
57387     
57388     onEventClick: function (e, el,event,d) {
57389         this.fireEvent('eventclick', this, el, event);
57390     },
57391     
57392     onMonthChange: function () {
57393         this.store.load();
57394     },
57395     
57396     onLoad: function () {
57397         
57398         //Roo.log('calendar onload');
57399 //         
57400         if(this.eventStore.getCount() > 0){
57401             
57402            
57403             
57404             this.eventStore.each(function(d){
57405                 
57406                 
57407                 // FIXME..
57408                 var add =   d.data;
57409                 if (typeof(add.end_dt) == 'undefined')  {
57410                     Roo.log("Missing End time in calendar data: ");
57411                     Roo.log(d);
57412                     return;
57413                 }
57414                 if (typeof(add.start_dt) == 'undefined')  {
57415                     Roo.log("Missing Start time in calendar data: ");
57416                     Roo.log(d);
57417                     return;
57418                 }
57419                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57420                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57421                 add.id = add.id || d.id;
57422                 add.title = add.title || '??';
57423                 
57424                 this.addItem(d);
57425                 
57426              
57427             },this);
57428         }
57429         
57430         this.renderEvents();
57431     }
57432     
57433
57434 });
57435 /*
57436  grid : {
57437                 xtype: 'Grid',
57438                 xns: Roo.grid,
57439                 listeners : {
57440                     render : function ()
57441                     {
57442                         _this.grid = this;
57443                         
57444                         if (!this.view.el.hasClass('course-timesheet')) {
57445                             this.view.el.addClass('course-timesheet');
57446                         }
57447                         if (this.tsStyle) {
57448                             this.ds.load({});
57449                             return; 
57450                         }
57451                         Roo.log('width');
57452                         Roo.log(_this.grid.view.el.getWidth());
57453                         
57454                         
57455                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57456                             '.course-timesheet .x-grid-row' : {
57457                                 height: '80px'
57458                             },
57459                             '.x-grid-row td' : {
57460                                 'vertical-align' : 0
57461                             },
57462                             '.course-edit-link' : {
57463                                 'color' : 'blue',
57464                                 'text-overflow' : 'ellipsis',
57465                                 'overflow' : 'hidden',
57466                                 'white-space' : 'nowrap',
57467                                 'cursor' : 'pointer'
57468                             },
57469                             '.sub-link' : {
57470                                 'color' : 'green'
57471                             },
57472                             '.de-act-sup-link' : {
57473                                 'color' : 'purple',
57474                                 'text-decoration' : 'line-through'
57475                             },
57476                             '.de-act-link' : {
57477                                 'color' : 'red',
57478                                 'text-decoration' : 'line-through'
57479                             },
57480                             '.course-timesheet .course-highlight' : {
57481                                 'border-top-style': 'dashed !important',
57482                                 'border-bottom-bottom': 'dashed !important'
57483                             },
57484                             '.course-timesheet .course-item' : {
57485                                 'font-family'   : 'tahoma, arial, helvetica',
57486                                 'font-size'     : '11px',
57487                                 'overflow'      : 'hidden',
57488                                 'padding-left'  : '10px',
57489                                 'padding-right' : '10px',
57490                                 'padding-top' : '10px' 
57491                             }
57492                             
57493                         }, Roo.id());
57494                                 this.ds.load({});
57495                     }
57496                 },
57497                 autoWidth : true,
57498                 monitorWindowResize : false,
57499                 cellrenderer : function(v,x,r)
57500                 {
57501                     return v;
57502                 },
57503                 sm : {
57504                     xtype: 'CellSelectionModel',
57505                     xns: Roo.grid
57506                 },
57507                 dataSource : {
57508                     xtype: 'Store',
57509                     xns: Roo.data,
57510                     listeners : {
57511                         beforeload : function (_self, options)
57512                         {
57513                             options.params = options.params || {};
57514                             options.params._month = _this.monthField.getValue();
57515                             options.params.limit = 9999;
57516                             options.params['sort'] = 'when_dt';    
57517                             options.params['dir'] = 'ASC';    
57518                             this.proxy.loadResponse = this.loadResponse;
57519                             Roo.log("load?");
57520                             //this.addColumns();
57521                         },
57522                         load : function (_self, records, options)
57523                         {
57524                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57525                                 // if you click on the translation.. you can edit it...
57526                                 var el = Roo.get(this);
57527                                 var id = el.dom.getAttribute('data-id');
57528                                 var d = el.dom.getAttribute('data-date');
57529                                 var t = el.dom.getAttribute('data-time');
57530                                 //var id = this.child('span').dom.textContent;
57531                                 
57532                                 //Roo.log(this);
57533                                 Pman.Dialog.CourseCalendar.show({
57534                                     id : id,
57535                                     when_d : d,
57536                                     when_t : t,
57537                                     productitem_active : id ? 1 : 0
57538                                 }, function() {
57539                                     _this.grid.ds.load({});
57540                                 });
57541                            
57542                            });
57543                            
57544                            _this.panel.fireEvent('resize', [ '', '' ]);
57545                         }
57546                     },
57547                     loadResponse : function(o, success, response){
57548                             // this is overridden on before load..
57549                             
57550                             Roo.log("our code?");       
57551                             //Roo.log(success);
57552                             //Roo.log(response)
57553                             delete this.activeRequest;
57554                             if(!success){
57555                                 this.fireEvent("loadexception", this, o, response);
57556                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57557                                 return;
57558                             }
57559                             var result;
57560                             try {
57561                                 result = o.reader.read(response);
57562                             }catch(e){
57563                                 Roo.log("load exception?");
57564                                 this.fireEvent("loadexception", this, o, response, e);
57565                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57566                                 return;
57567                             }
57568                             Roo.log("ready...");        
57569                             // loop through result.records;
57570                             // and set this.tdate[date] = [] << array of records..
57571                             _this.tdata  = {};
57572                             Roo.each(result.records, function(r){
57573                                 //Roo.log(r.data);
57574                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57575                                     _this.tdata[r.data.when_dt.format('j')] = [];
57576                                 }
57577                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57578                             });
57579                             
57580                             //Roo.log(_this.tdata);
57581                             
57582                             result.records = [];
57583                             result.totalRecords = 6;
57584                     
57585                             // let's generate some duumy records for the rows.
57586                             //var st = _this.dateField.getValue();
57587                             
57588                             // work out monday..
57589                             //st = st.add(Date.DAY, -1 * st.format('w'));
57590                             
57591                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57592                             
57593                             var firstOfMonth = date.getFirstDayOfMonth();
57594                             var days = date.getDaysInMonth();
57595                             var d = 1;
57596                             var firstAdded = false;
57597                             for (var i = 0; i < result.totalRecords ; i++) {
57598                                 //var d= st.add(Date.DAY, i);
57599                                 var row = {};
57600                                 var added = 0;
57601                                 for(var w = 0 ; w < 7 ; w++){
57602                                     if(!firstAdded && firstOfMonth != w){
57603                                         continue;
57604                                     }
57605                                     if(d > days){
57606                                         continue;
57607                                     }
57608                                     firstAdded = true;
57609                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57610                                     row['weekday'+w] = String.format(
57611                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57612                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57613                                                     d,
57614                                                     date.format('Y-m-')+dd
57615                                                 );
57616                                     added++;
57617                                     if(typeof(_this.tdata[d]) != 'undefined'){
57618                                         Roo.each(_this.tdata[d], function(r){
57619                                             var is_sub = '';
57620                                             var deactive = '';
57621                                             var id = r.id;
57622                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57623                                             if(r.parent_id*1>0){
57624                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57625                                                 id = r.parent_id;
57626                                             }
57627                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57628                                                 deactive = 'de-act-link';
57629                                             }
57630                                             
57631                                             row['weekday'+w] += String.format(
57632                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57633                                                     id, //0
57634                                                     r.product_id_name, //1
57635                                                     r.when_dt.format('h:ia'), //2
57636                                                     is_sub, //3
57637                                                     deactive, //4
57638                                                     desc // 5
57639                                             );
57640                                         });
57641                                     }
57642                                     d++;
57643                                 }
57644                                 
57645                                 // only do this if something added..
57646                                 if(added > 0){ 
57647                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57648                                 }
57649                                 
57650                                 
57651                                 // push it twice. (second one with an hour..
57652                                 
57653                             }
57654                             //Roo.log(result);
57655                             this.fireEvent("load", this, o, o.request.arg);
57656                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57657                         },
57658                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57659                     proxy : {
57660                         xtype: 'HttpProxy',
57661                         xns: Roo.data,
57662                         method : 'GET',
57663                         url : baseURL + '/Roo/Shop_course.php'
57664                     },
57665                     reader : {
57666                         xtype: 'JsonReader',
57667                         xns: Roo.data,
57668                         id : 'id',
57669                         fields : [
57670                             {
57671                                 'name': 'id',
57672                                 'type': 'int'
57673                             },
57674                             {
57675                                 'name': 'when_dt',
57676                                 'type': 'string'
57677                             },
57678                             {
57679                                 'name': 'end_dt',
57680                                 'type': 'string'
57681                             },
57682                             {
57683                                 'name': 'parent_id',
57684                                 'type': 'int'
57685                             },
57686                             {
57687                                 'name': 'product_id',
57688                                 'type': 'int'
57689                             },
57690                             {
57691                                 'name': 'productitem_id',
57692                                 'type': 'int'
57693                             },
57694                             {
57695                                 'name': 'guid',
57696                                 'type': 'int'
57697                             }
57698                         ]
57699                     }
57700                 },
57701                 toolbar : {
57702                     xtype: 'Toolbar',
57703                     xns: Roo,
57704                     items : [
57705                         {
57706                             xtype: 'Button',
57707                             xns: Roo.Toolbar,
57708                             listeners : {
57709                                 click : function (_self, e)
57710                                 {
57711                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57712                                     sd.setMonth(sd.getMonth()-1);
57713                                     _this.monthField.setValue(sd.format('Y-m-d'));
57714                                     _this.grid.ds.load({});
57715                                 }
57716                             },
57717                             text : "Back"
57718                         },
57719                         {
57720                             xtype: 'Separator',
57721                             xns: Roo.Toolbar
57722                         },
57723                         {
57724                             xtype: 'MonthField',
57725                             xns: Roo.form,
57726                             listeners : {
57727                                 render : function (_self)
57728                                 {
57729                                     _this.monthField = _self;
57730                                    // _this.monthField.set  today
57731                                 },
57732                                 select : function (combo, date)
57733                                 {
57734                                     _this.grid.ds.load({});
57735                                 }
57736                             },
57737                             value : (function() { return new Date(); })()
57738                         },
57739                         {
57740                             xtype: 'Separator',
57741                             xns: Roo.Toolbar
57742                         },
57743                         {
57744                             xtype: 'TextItem',
57745                             xns: Roo.Toolbar,
57746                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57747                         },
57748                         {
57749                             xtype: 'Fill',
57750                             xns: Roo.Toolbar
57751                         },
57752                         {
57753                             xtype: 'Button',
57754                             xns: Roo.Toolbar,
57755                             listeners : {
57756                                 click : function (_self, e)
57757                                 {
57758                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57759                                     sd.setMonth(sd.getMonth()+1);
57760                                     _this.monthField.setValue(sd.format('Y-m-d'));
57761                                     _this.grid.ds.load({});
57762                                 }
57763                             },
57764                             text : "Next"
57765                         }
57766                     ]
57767                 },
57768                  
57769             }
57770         };
57771         
57772         *//*
57773  * Based on:
57774  * Ext JS Library 1.1.1
57775  * Copyright(c) 2006-2007, Ext JS, LLC.
57776  *
57777  * Originally Released Under LGPL - original licence link has changed is not relivant.
57778  *
57779  * Fork - LGPL
57780  * <script type="text/javascript">
57781  */
57782  
57783 /**
57784  * @class Roo.LoadMask
57785  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57786  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57787  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57788  * element's UpdateManager load indicator and will be destroyed after the initial load.
57789  * @constructor
57790  * Create a new LoadMask
57791  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57792  * @param {Object} config The config object
57793  */
57794 Roo.LoadMask = function(el, config){
57795     this.el = Roo.get(el);
57796     Roo.apply(this, config);
57797     if(this.store){
57798         this.store.on('beforeload', this.onBeforeLoad, this);
57799         this.store.on('load', this.onLoad, this);
57800         this.store.on('loadexception', this.onLoadException, this);
57801         this.removeMask = false;
57802     }else{
57803         var um = this.el.getUpdateManager();
57804         um.showLoadIndicator = false; // disable the default indicator
57805         um.on('beforeupdate', this.onBeforeLoad, this);
57806         um.on('update', this.onLoad, this);
57807         um.on('failure', this.onLoad, this);
57808         this.removeMask = true;
57809     }
57810 };
57811
57812 Roo.LoadMask.prototype = {
57813     /**
57814      * @cfg {Boolean} removeMask
57815      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57816      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57817      */
57818     /**
57819      * @cfg {String} msg
57820      * The text to display in a centered loading message box (defaults to 'Loading...')
57821      */
57822     msg : 'Loading...',
57823     /**
57824      * @cfg {String} msgCls
57825      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57826      */
57827     msgCls : 'x-mask-loading',
57828
57829     /**
57830      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57831      * @type Boolean
57832      */
57833     disabled: false,
57834
57835     /**
57836      * Disables the mask to prevent it from being displayed
57837      */
57838     disable : function(){
57839        this.disabled = true;
57840     },
57841
57842     /**
57843      * Enables the mask so that it can be displayed
57844      */
57845     enable : function(){
57846         this.disabled = false;
57847     },
57848     
57849     onLoadException : function()
57850     {
57851         Roo.log(arguments);
57852         
57853         if (typeof(arguments[3]) != 'undefined') {
57854             Roo.MessageBox.alert("Error loading",arguments[3]);
57855         } 
57856         /*
57857         try {
57858             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57859                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57860             }   
57861         } catch(e) {
57862             
57863         }
57864         */
57865     
57866         
57867         
57868         this.el.unmask(this.removeMask);
57869     },
57870     // private
57871     onLoad : function()
57872     {
57873         this.el.unmask(this.removeMask);
57874     },
57875
57876     // private
57877     onBeforeLoad : function(){
57878         if(!this.disabled){
57879             this.el.mask(this.msg, this.msgCls);
57880         }
57881     },
57882
57883     // private
57884     destroy : function(){
57885         if(this.store){
57886             this.store.un('beforeload', this.onBeforeLoad, this);
57887             this.store.un('load', this.onLoad, this);
57888             this.store.un('loadexception', this.onLoadException, this);
57889         }else{
57890             var um = this.el.getUpdateManager();
57891             um.un('beforeupdate', this.onBeforeLoad, this);
57892             um.un('update', this.onLoad, this);
57893             um.un('failure', this.onLoad, this);
57894         }
57895     }
57896 };/*
57897  * Based on:
57898  * Ext JS Library 1.1.1
57899  * Copyright(c) 2006-2007, Ext JS, LLC.
57900  *
57901  * Originally Released Under LGPL - original licence link has changed is not relivant.
57902  *
57903  * Fork - LGPL
57904  * <script type="text/javascript">
57905  */
57906
57907
57908 /**
57909  * @class Roo.XTemplate
57910  * @extends Roo.Template
57911  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57912 <pre><code>
57913 var t = new Roo.XTemplate(
57914         '&lt;select name="{name}"&gt;',
57915                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57916         '&lt;/select&gt;'
57917 );
57918  
57919 // then append, applying the master template values
57920  </code></pre>
57921  *
57922  * Supported features:
57923  *
57924  *  Tags:
57925
57926 <pre><code>
57927       {a_variable} - output encoded.
57928       {a_variable.format:("Y-m-d")} - call a method on the variable
57929       {a_variable:raw} - unencoded output
57930       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57931       {a_variable:this.method_on_template(...)} - call a method on the template object.
57932  
57933 </code></pre>
57934  *  The tpl tag:
57935 <pre><code>
57936         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57937         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57938         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57939         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57940   
57941         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57942         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57943 </code></pre>
57944  *      
57945  */
57946 Roo.XTemplate = function()
57947 {
57948     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57949     if (this.html) {
57950         this.compile();
57951     }
57952 };
57953
57954
57955 Roo.extend(Roo.XTemplate, Roo.Template, {
57956
57957     /**
57958      * The various sub templates
57959      */
57960     tpls : false,
57961     /**
57962      *
57963      * basic tag replacing syntax
57964      * WORD:WORD()
57965      *
57966      * // you can fake an object call by doing this
57967      *  x.t:(test,tesT) 
57968      * 
57969      */
57970     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57971
57972     /**
57973      * compile the template
57974      *
57975      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57976      *
57977      */
57978     compile: function()
57979     {
57980         var s = this.html;
57981      
57982         s = ['<tpl>', s, '</tpl>'].join('');
57983     
57984         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57985             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57986             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57987             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57988             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57989             m,
57990             id     = 0,
57991             tpls   = [];
57992     
57993         while(true == !!(m = s.match(re))){
57994             var forMatch   = m[0].match(nameRe),
57995                 ifMatch   = m[0].match(ifRe),
57996                 execMatch   = m[0].match(execRe),
57997                 namedMatch   = m[0].match(namedRe),
57998                 
57999                 exp  = null, 
58000                 fn   = null,
58001                 exec = null,
58002                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58003                 
58004             if (ifMatch) {
58005                 // if - puts fn into test..
58006                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58007                 if(exp){
58008                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58009                 }
58010             }
58011             
58012             if (execMatch) {
58013                 // exec - calls a function... returns empty if true is  returned.
58014                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58015                 if(exp){
58016                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58017                 }
58018             }
58019             
58020             
58021             if (name) {
58022                 // for = 
58023                 switch(name){
58024                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58025                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58026                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58027                 }
58028             }
58029             var uid = namedMatch ? namedMatch[1] : id;
58030             
58031             
58032             tpls.push({
58033                 id:     namedMatch ? namedMatch[1] : id,
58034                 target: name,
58035                 exec:   exec,
58036                 test:   fn,
58037                 body:   m[1] || ''
58038             });
58039             if (namedMatch) {
58040                 s = s.replace(m[0], '');
58041             } else { 
58042                 s = s.replace(m[0], '{xtpl'+ id + '}');
58043             }
58044             ++id;
58045         }
58046         this.tpls = [];
58047         for(var i = tpls.length-1; i >= 0; --i){
58048             this.compileTpl(tpls[i]);
58049             this.tpls[tpls[i].id] = tpls[i];
58050         }
58051         this.master = tpls[tpls.length-1];
58052         return this;
58053     },
58054     /**
58055      * same as applyTemplate, except it's done to one of the subTemplates
58056      * when using named templates, you can do:
58057      *
58058      * var str = pl.applySubTemplate('your-name', values);
58059      *
58060      * 
58061      * @param {Number} id of the template
58062      * @param {Object} values to apply to template
58063      * @param {Object} parent (normaly the instance of this object)
58064      */
58065     applySubTemplate : function(id, values, parent)
58066     {
58067         
58068         
58069         var t = this.tpls[id];
58070         
58071         
58072         try { 
58073             if(t.test && !t.test.call(this, values, parent)){
58074                 return '';
58075             }
58076         } catch(e) {
58077             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58078             Roo.log(e.toString());
58079             Roo.log(t.test);
58080             return ''
58081         }
58082         try { 
58083             
58084             if(t.exec && t.exec.call(this, values, parent)){
58085                 return '';
58086             }
58087         } catch(e) {
58088             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58089             Roo.log(e.toString());
58090             Roo.log(t.exec);
58091             return ''
58092         }
58093         try {
58094             var vs = t.target ? t.target.call(this, values, parent) : values;
58095             parent = t.target ? values : parent;
58096             if(t.target && vs instanceof Array){
58097                 var buf = [];
58098                 for(var i = 0, len = vs.length; i < len; i++){
58099                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58100                 }
58101                 return buf.join('');
58102             }
58103             return t.compiled.call(this, vs, parent);
58104         } catch (e) {
58105             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58106             Roo.log(e.toString());
58107             Roo.log(t.compiled);
58108             return '';
58109         }
58110     },
58111
58112     compileTpl : function(tpl)
58113     {
58114         var fm = Roo.util.Format;
58115         var useF = this.disableFormats !== true;
58116         var sep = Roo.isGecko ? "+" : ",";
58117         var undef = function(str) {
58118             Roo.log("Property not found :"  + str);
58119             return '';
58120         };
58121         
58122         var fn = function(m, name, format, args)
58123         {
58124             //Roo.log(arguments);
58125             args = args ? args.replace(/\\'/g,"'") : args;
58126             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58127             if (typeof(format) == 'undefined') {
58128                 format= 'htmlEncode';
58129             }
58130             if (format == 'raw' ) {
58131                 format = false;
58132             }
58133             
58134             if(name.substr(0, 4) == 'xtpl'){
58135                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58136             }
58137             
58138             // build an array of options to determine if value is undefined..
58139             
58140             // basically get 'xxxx.yyyy' then do
58141             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58142             //    (function () { Roo.log("Property not found"); return ''; })() :
58143             //    ......
58144             
58145             var udef_ar = [];
58146             var lookfor = '';
58147             Roo.each(name.split('.'), function(st) {
58148                 lookfor += (lookfor.length ? '.': '') + st;
58149                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58150             });
58151             
58152             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58153             
58154             
58155             if(format && useF){
58156                 
58157                 args = args ? ',' + args : "";
58158                  
58159                 if(format.substr(0, 5) != "this."){
58160                     format = "fm." + format + '(';
58161                 }else{
58162                     format = 'this.call("'+ format.substr(5) + '", ';
58163                     args = ", values";
58164                 }
58165                 
58166                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58167             }
58168              
58169             if (args.length) {
58170                 // called with xxyx.yuu:(test,test)
58171                 // change to ()
58172                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58173             }
58174             // raw.. - :raw modifier..
58175             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58176             
58177         };
58178         var body;
58179         // branched to use + in gecko and [].join() in others
58180         if(Roo.isGecko){
58181             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58182                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58183                     "';};};";
58184         }else{
58185             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58186             body.push(tpl.body.replace(/(\r\n|\n)/g,
58187                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58188             body.push("'].join('');};};");
58189             body = body.join('');
58190         }
58191         
58192         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58193        
58194         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58195         eval(body);
58196         
58197         return this;
58198     },
58199
58200     applyTemplate : function(values){
58201         return this.master.compiled.call(this, values, {});
58202         //var s = this.subs;
58203     },
58204
58205     apply : function(){
58206         return this.applyTemplate.apply(this, arguments);
58207     }
58208
58209  });
58210
58211 Roo.XTemplate.from = function(el){
58212     el = Roo.getDom(el);
58213     return new Roo.XTemplate(el.value || el.innerHTML);
58214 };